Why does the code below work fine when I run my web application localhost but not when I install it to an IIS server?
using (HostingEnvironment.Impersonate())
{
UserPrincipal activeUser = UserPrincipal.Current;
String activeUserSid = activeUser.Sid.ToString();
String activeUserUPN = activeUser.UserPrincipalName;
}
Please don't suggest I stick with HttpContext.Current.User as it doesn't provide access to SID or UPN without additional calls to Active Directory.
The web application will be used by Windows authenticated users from three separate domains, the web server is hosted in a fourth domain. The Application Pool is configured to run under the NetworkService identity and the web app configuration has identity impersonation set to true.
The error message when it runs on IIS is:
Error in Page_Load(): UserPrincipal.Current.
System.InvalidCastException: Unable to cast object of type
'System.DirectoryServices.AccountManagement.GroupPrincipal' to type
'System.DirectoryServices.AccountManagement.UserPrincipal'.
at System.DirectoryServices.AccountManagement.UserPrincipal.FindByIdentity(PrincipalContext
context, IdentityType identityType, String identityValue)
at System.DirectoryServices.AccountManagement.UserPrincipal.get_Current()
at webapp.Details.Default.Page_Load(Object sender, EventArgs e)
EDIT:
Tried both the following and unfortunately get the same error.
UserPrincipal userPrincipal = UserPrincipal.Current;
Response.Write(userPrincipal.Name);
Principal userOrGroup = UserPrincipal.Current;
Response.Write(userOrGroup.Name);
I had a lot of issues deploying UserPrincipal.Current and still don't fully understand why.
I finally ended up using PrincipalSearcher, and created the following function to do what I thought UserPrincipal.Current was doing.
private UserPrincipal GetActiveDirectoryUser(string userName)
{
using(var ctx = new PrincipalContext(ContextType.Domain))
using(var user = new UserPrincipal(ctx) { SamAccountName = userName})
using(var searcher = new PrincipalSearcher(user))
{
return searcher.FindOne() as UserPrincipal;
}
}
And I passed System.Web.HttpContext.Current.User.Identity.Name into that method as the userName.
It seems like need some other method to determine user.
Here description from msdn for property:
"Gets a user principal object that represents the current user under which the thread is running."
So, UserPrincipal.Current returns user under what IIS running.
http://msdn.microsoft.com/en-us/library/system.directoryservices.accountmanagement.userprincipal.aspx
Yes, its because you are disposing of the returned UserPrincipal object due to the multiple using statements. Remove 'ctx' from the using statement, then it becomes the callers responsibility to dispose of the returned object.
Related
I've been running into an error on one of my applications that happens a few times a month but has occurred twice this week. When this happens, it's always first thing in the morning when the first user loads the app and begins working (web application, 3-4 internal users) The error originates from this very simple method and once if fails, it will not work until I restart the application pool. Now, I'm querying AD in other ways as well but this is the first AD related method that's called when the users begin work in the morning.
public DomainUser GetDomainUser(string userLoginName)
{
using (PrincipalContext context = new PrincipalContext(ContextType.Domain, this.DomainName))
{
using (UserPrincipal user = UserPrincipal.FindByIdentity(context, userLoginName))
{
// If user is null, the result is not a UserPrinciple
if (user != null)
{
string firstName = user.GivenName;
string middleName = user.MiddleName;
string lastName = user.Surname;
int empId = Convert.ToInt32(user.EmployeeId);
string emailAddr = user.EmailAddress;
string userName = user.SamAccountName;
DateTime? accountExp = user.AccountExpirationDate;
return new DomainUser
{
FirstName = firstName,
MiddleName = middleName,
LastName = lastName,
EmployeeId = empId,
Email = emailAddr,
UserName = userName,
AccountExpiration = accountExp
};
}
return null;
}
}
}
So this question is closely related but my permissions are setup correctly and the code works 99% of the time and will continue to run after an application pool restart.
Stack trace looks something like this:
System.Runtime.InteropServices.COMException (0x80005000): Unknown error (0x80005000)
at System.DirectoryServices.DirectoryEntry.Bind(Boolean throwIfFail)
at System.DirectoryServices.DirectoryEntry.Bind()
at System.DirectoryServices.DirectoryEntry.get_AdsObject()
at System.DirectoryServices.PropertyValueCollection.PopulateList()
at System.DirectoryServices.PropertyValueCollection..ctor(DirectoryEntry entry, String propertyName)
at System.DirectoryServices.PropertyCollection.get_Item(String propertyName)
at System.DirectoryServices.AccountManagement.PrincipalContext.DoLDAPDirectoryInitNoContainer()
at System.DirectoryServices.AccountManagement.PrincipalContext.DoDomainInit()
at System.DirectoryServices.AccountManagement.PrincipalContext.Initialize()
at System.DirectoryServices.AccountManagement.PrincipalContext.get_QueryCtx()
at System.DirectoryServices.AccountManagement.Principal.FindByIdentityWithTypeHelper(PrincipalContext context, Type principalType, Nullable`1 identityType, String identityValue, DateTime refDate)
at System.DirectoryServices.AccountManagement.Principal.FindByIdentityWithType(PrincipalContext context, Type principalType, String identityValue)
at System.DirectoryServices.AccountManagement.UserPrincipal.FindByIdentity(PrincipalContext context, String identityValue)
at ADWrapper.AdSearch.GetDomainUser(String userLoginName)
What could the problem be? Memory leaks? The common pattern is that this happens first thing in the morning when the first user begins using the app.
We had a similar issue. Here was the solution provided by Microsoft. I hope this helps someone.
The DirectoryEntry.Bind function eventually calls into ADsOpenObject (https://learn.microsoft.com/en-us/windows/win32/api/adshlp/nf-adshlp-adsopenobject)
This function has a “router”. The initialization of the router enumerates providers from the registry, such as the “LdapNamespace”. This is located at HKEY_CLASSES_ROOT\CLSID{228D9A82-C302-11cf-9AA4-00AA004A5691}\ProgID.
The other providers, such as WinNT namespace are also enumerated.
In the trace, an error is returned when looking up these registry keys. The error is,
ERROR_KEY_DELETED
1018 (0x3FA)
Illegal operation attempted on a registry key that has been marked for deletion.
This error can be caused by an unload of the user profile that the process is using for its identity.
The Windows User Profile Service forcefully unloads user profiles. This causes problems with the process.
I have seen this with w3wp.exe and dllhost.exe, where the registry profile is unloaded before the process is done.
Here’s a blog we did on the issue for dllhost.exe: https://blogs.msdn.microsoft.com/distributedservices/2009/11/06/a-com-application-may-stop-working-on-windows-server-2008-when-the-identity-user-logs-off/
You may see warnings in the application log with descriptions like this:
Windows detected your registry file is still in use by other applications or services. The file will be unloaded now. The applications or services that hold your registry file may not function properly afterwards.
I think we should try the resolution/workaround in the blog:
Resolution
As a workaround it may be necessary to disable this feature which is the default behavior. The policy setting 'Do not forcefully unload the user registry at user logoff' counters the default behavior of Windows 2008. When enabled, Windows 2008 does not forcefully unload the registry and waits until no other processes are using the user registry before it unloads it.
The policy can be found in the group policy editor (gpedit.msc)
Computer Configuration->Administrative Templates->System-> UserProfiles
Do not forcefully unload the user registry at user logoff
Change the setting from “Not Configured” to “Enabled”, which disables the new User Profile Service feature.
There should not be any side effects from this change.
I was struggling with absolutely same issue for a long time. My solution was actually install feature IIS 6 Metabase Compatiblity. After that no problems. Some articles which helped me are below
http://blogs.msdn.com/b/jpsanders/archive/2009/05/13/iis-7-adsi-error-system-runtime-interopservices-comexception-0x80005000-unknown-error-0x80005000.aspx
http://michaelwasham.com/2011/04/25/annoying-error-using-system-directoryservices-and-iis-app-pools/
I have a Winform client that we are slowing changing inline SQL data calls into ASP.NET Web API calls. We currently use the WindowsPrincipal.IsInRole check in the Winform client to determine if the user can run the SQL data calls. We would like to move into a Claims type setup where both the Winform client and the Web API can check the roles "claims" of a user.
I can't seem to find any "good" articles on how to get a Winform client to (1. Pass the claim to the service) and (2. Use a claim check inside the Winform client like the IsInRole). Any help or push in the right direction would be great.
--EDIT
So I used this article http://zamd.net/2012/05/04/claim-based-security-for-asp-net-web-apis-using-dotnetopenauth/ as a sample on getting a token back from the server but the article does not show how to get the claims identity out of the http client. Any idea how to get the claims identity out of the http client?
While I haven't tested this code, hopefully it will get you moving in the right direction.
I believe to answer your question you do this in your ClaimsAuthenticationManager where upon validating the token received from the server you set the Thread.CurrentPrincipal -- the same way you do on the web side without setting the HttpContext.Current.User principal.
Again this isn't tested but I think it would look something like this...
In my Token Validator I have the following code:
public static ClaimsPrincipal ValidateToken(string token)
{
var tokenHandler = new JwtSecurityTokenHandler();
ClaimsPrincipal claimsPrincipal = tokenHandler.ValidateToken(new JwtSecurityToken(token),
Constants.TokenValidationParameters);
return FederatedAuthentication.FederationConfiguration
.IdentityConfiguration
.ClaimsAuthenticationManager.Authenticate(token, claimsPrincipal);
}
public static string GetToken(string username, string password)
{
OAuth2Client client = Constants.OAuth2Client;
AccessTokenResponse response = client.RequestAccessTokenUserName(username.ToLower(), password,
Constants.AllowedAudience);
return response.AccessToken;
}
Within my ClaimsAuthenticationManager I have modified the following code as you don't want to set the HttpContext in a non web environment:
public override ClaimsPrincipal Authenticate(string resourceName, ClaimsPrincipal incomingPrincipal)
{
if (!incomingPrincipal.Identity.IsAuthenticated)
{
return base.Authenticate(resourceName, incomingPrincipal);
}
/* HttpContext.Current.User = */ Thread.CurrentPrincipal = incomingPrincipal;
return incomingPrincipal;
}
I believe you then just have to set the appropriate keys in the app.config, specifically the system.identityModel => identityConfiguration => claimsAuthenticationManager
Once the thread you are running on has the "Authenticated Principal" you should be able to call the ClaimsPrincipal.Current.HasClaim() or your higher level Authorization.CheckAccess() function to validate sections of your WinForm logic.
Hope this helps :)
I was working with ASP.Net Membership and was wondering when exactly ApplicationID is generated. I want to know what events are called and when does ApplicationID is generated when we use "Membership.CreateUser" method.
I googled it for some time but couldn't find any satisfactory answer.
Can anyone explain what is the working of this method?
Thanks
I want to know what events are called and when does ApplicationID is
generated when we use "Membership.CreateUser" method.
Right before processing any request to Membership table (such as select/insert/update/delete), Application is retrieved by applicationName.
For example, inside the Membership.CreateUser method, QueryHelper.GetApplication is called right before creating a new user.
Application application = QueryHelper.GetApplication(membershipEntity,
applicationName);
// QueryHelper.GetApplication
internal static Application GetApplication(MembershipEntities ctx,
string applicationName)
{
ObjectParameter[] objectParameter = new ObjectParameter[1];
objectParameter[0] = new ObjectParameter("name",
applicationName.ToLowerInvariant());
Application application = ctx.CreateQuery<Application>(
"select value a FROM Applications as a WHERE ToLower(a.ApplicationName) = #name",
objectParameter).FirstOrDefault<Application>();
return application;
}
If application is null, it is created an application like this -
internal static Application CreateApplication(MembershipEntities ctx,
string appName)
{
Application application = new Application();
application.ApplicationId = Guid.NewGuid();
application.ApplicationName = appName;
ctx.Applications.AddObject(application);
Application application1 = application;
return application1;
}
About code is from ASP.NET Universal Providers. Legacy Membership Provider uses Store Procedure, but the logic is almost same.
I am creating asp.net MVC Application using MVC 3.0. I have 2 users but the DataBase is the same. So, Is it possible to setup two connection strings or even more in web.config? when user login, I redirect it to his database, so then he can use his DataBase.
So major issue here is to find out which user is logged in and use connection string for that user.
I am using default mvc account controller and for example when i want to display welcome message for user in my view i type: if (#User.Identity.Name == "UserName") then some message
So where is the best place to find out which user is logged in and set his connection string in controller or in a view?
Yes, you can have as many connection strings in your web.config file as you want.
But, if you're designing a multi-tenant application than there are better ways of doing it than adding a connection string to web.config file every time a new user signs up.
Probably the best way for you is to have a single database where user-related tables have foreign keys to Users table.
You can learn more about multi-tenant architectures from this Microsoft article.
I agree with Jakub's answer: there are better ways of handling multi-tenancy than having a different database per user.
However, to answer your specific question, there are two options that come to mind:
You can set the connection string to a session variable immediately after login.
Your data access layer can choose the connection string based on the logged in user when it's created. (I'd recommend this over the first option)
To store the connection after login, if you're using the standard ASP.NET MVC Account Controller, look at the LogOn post action:
[HttpPost]
public ActionResult LogOn(LogOnModel model, string returnUrl)
{
if (ModelState.IsValid)
{
if (Membership.ValidateUser(model.UserName, model.Password))
{
//EXAMPLE OF WHERE YOU COULD STORE THE CONNECTION STRING
Session["userConnectionString"] = SomeClass.GetConnectionStringForUser(model.UserName);
FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
if (Url.IsLocalUrl(returnUrl) && returnUrl.Length > 1 && returnUrl.StartsWith("/")
&& !returnUrl.StartsWith("//") && !returnUrl.StartsWith("/\\"))
{
return Redirect(returnUrl);
}
else
{
return RedirectToAction("Index", "Home");
}
}
else
{
ModelState.AddModelError("", "The user name or password provided is incorrect.");
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
If you wanted to select the connection string when doing data access, your repository or data access layer will probably have a system for handling that. For instance with Entity Framework Code First, the DbContext constructor allows you to pass in the name of a connection string when you're creating it:
connectionString = SomeClass.GetConnectionStringForUser(model.UserName);
DbContext context = new DbContext(connectionString);
But again, I'd look at other ways of handling multitenancy unless your business dictates that your users have physically separate databases.
you can have multiple connection strings in web.config. Now if you want to use different connection string for different users there must be some criteria for division of users
<appSettings><add key="connectionString" value="Data Source=develope\sqlexpress;Initial Catalog=validation_tdsl;Integrated Security=True;Max Pool Size=1000;Connect Timeout=60000;"></add>
<add key="connectionString1" value="server=MARK\SQLEXPRESS;database=name;integrated security=true;Max Pool Size=1000;Connect Timeout=60000;"></add>
<add key="connectionString2" value="server=name\SQLEXPRESS;database=FM;integrated security=true;Max Pool Size=1000;Connect Timeout=60000;"></add>
and later you can use them like following
Dim con As New SqlConnection(System.Configuration.ConfigurationSettings.AppSettings("connectionString"))
Dim con1 As New SqlConnection(System.Configuration.ConfigurationSettings.AppSettings("connectionString1"))
EDIT : In c# it would be:
SqlConnection con = new SqlConnection(System.Configuration.ConfigurationManager.AppSettings["connectionString"]);
SqlConnection con1 = new SqlConnection(System.Configuration.ConfigurationManager.AppSettings["connectionString1"])
Note: ConfigurationSettings is now obsolete.
I'm using asp.net and trying to assign roles for a user with forms authentication like this:
public ActionResult AdminLogin(string password, string username)
{
User _user = _us.GetUsers(username, password).FirstOrDefault();
if (_user != null)
{
string _username = _user.Username;
FormsAuthentication.SetAuthCookie(_username, false);
string[] _roles = _us.GetUserRoles(_username);
HttpContext.User = new GenericPrincipal(HttpContext.User.Identity, _roles);
return RedirectToAction("Index", "Admin");
When I debug HttpContext.User.Identity always is null, but _username and _roles contains the proper data. Howto fix this?
/M
Your action is setting the User IPrincipal for the current context. As soon as you redirect to your other action (and all subsequent requests) a new HttpContext is created with a null User IPrincipal.
What you could do is persist the information in the authentication cookie and then extract that data in the Application_AuthenticateRequest method in your Global.asax file and set the User property of the HttpContext there.
This answer contains more details and example code
I believe the issue is that you are just setting the user as authenticated, and therefore, the HttpContext is not updated yet since the auth cookie has not yet been set on the users side of the request.
I was struggling too.
I was trying to carryout my authentication and authorization inside a WCF service using standard ASP.Net Membership and Role providers.
I wanted to pass in credentials and a 'requested app' to determine if the user 'authenticated' for that app. (not the ASP.Net APP, but an app in my own database).
To do this, I wanted access to the roles, but didn't want to 'redirect' or have a second call to my WCF service.
Here is some code that works for me:
First I determine if the user is valid as follows:
if (Membership.ValidateUser(CompanyCn, CompanyPwd))
{
sbLogText.AppendFormat("\r\n\r\n\tValid User UID/PWD: '{0}'/'{1}'", CompanyCn, CompanyPwd);
FormsAuthentication.SetAuthCookie(CompanyCn, false);
}
Then the following code workes nicely for getting the list of roles:
List<string> roleList = new List<string>(Roles.GetRolesForUser(CompanyCn));
sbLogText.AppendFormat("\r\n\r\n\tUser ('{0}'): Roles ({1}):", CompanyCn, roleList.Count);
foreach (string s in roleList)
sbLogText.AppendFormat("\r\n\t\tRole: {0}", s);