I need assistance with gathering Active Directory data based on a table in my DB. I have an entity class that holds user requests. Each request has the user's windows name from System.Web.HttpContext.Current.User.Identity.Name. My problem is I cannot figure out how to setup a linq query to associate the AD username to the rest of the AD so I can display their full names instead of their username in my table. Here is what I have so far, any help will be appreciated.
public partial class RequestInfo
{
public int RequestInfoId { get; set; }
public string RequestByUserADId { get; set; }
public System.DateTime RequestDateTime { get; set; }
public string Explanation { get; set; }
public virtual UserInfo UserInfo { get; set; } // where I define my custom roles
}
I can query AD by using the code below. I have tried Get Active Directory User Information With Windows Authentication in MVC 4, but it did not help.
using (PrincipalContext context = new PrincipalContext(ContextType.Domain))
using (UserPrincipal user = UserPrincipal.FindByIdentity(context, requestByAdId))
{
return user.DisplayName
}
I may be off here because I am not sure if you are able to successful establish a user principal or not but if you have the user principal you can get property information like the following:
user.GetProperty("propertyName")
Here is a static method that should get you the department for a user, for example.
public static String GetDepartment(UserPrincipal principal)
{
return principal.GetProperty("department");
}
Let me know where this gets you and I can elaborate further if this isn't working.
Edit
It appears you need to go one level deeper to get the fields that aren't by default a part of the user principal. For this you will need to get the directory entry from the user principal first:
DirectoryEntry directoryEntry = (userPrincipal.GetUnderlyingObject() as DirectoryEntry);
Then you need to check if the attribute you are looking for exists, and if it does, get the value. A great way to do this is to create a helper method that you pass your directory entry to along with the string value for the property name that you want to get.
public string GetProperty(DirectoryEntry directoryEntry, string propertyName)
{
if (directoryEntry.Properties.Contains(propertyName))
{
return directoryEntry.Properties[propertyName][0].ToString();
}
else
{
return string.Empty;
}
}
Please note that going to the underlying object is expensive. I believe this operation, by default, is cached for you so subsequent use of this information can be retrieved from cache. Playing around with
directoryEntry.RefreshCache
will get you started with that.
Let me know if this does the trick for you!
Related
How can I save the form values as a string after the page is redirected?
I use a static variable, if the number of users on the site increases for example 20000, will the site have problems?
Which method is better?
Using a static variable?
session?
Cookies?
ViewBag?
View State?
sql server database?
Actually, There is no the best method, Only the most suitable method. The methods you list above all have their own most suitable use cases.
From your comment, I think you may wanna redirect to another page with a string parameter, So you can try to use :
return RedirectToAction("actionName","controllerName",new { Name="xxx"});
It will send a get request: acontrollerName/actionName?Name=xxx.
Or, if you wanna save that string value for a long time, You can also try to use Session or Database. But one thing you need to consider is that you need to set the session time out according to the actual situation of your project.
==========================edited===========================
I think you can use Session to achieve it.
public class TestModel
{
public int ID { get; set; }
public string Name { get; set; }
}
public async Task<IActionResult> OnPostSelectMenu(int selectedid)
{
TestModel selected = await _db.Table.FindAsync(selectedid);
//set session
HttpContext.Session.SetString("Key", JsonConvert.SerializeObject(selected));
return RedirectToPage("MyPage");
}
public async Task<IActionResult> OnGetAsync()
{
//......
//get session
Input = JsonConvert.DeserializeObject<TestModel>(HttpContext.Session.GetString("Key"));
return Page();
}
Using ASP.NET Core identity, I create a new user with UserManager.CreateAsync() and assign them to an existing role with UserManager.AddToRoleAsync. This works, as the realtion between user and role is stored in the AspNetUserRoles table of the database.
But when I fetch the user using the UserManager (e.g. UserManager.FindByMail() method) then the Role list is empty. I also tried the Include function from EF like this:
var user = userManager.Users.Include(u => u.Roles).FirstOrDefault(u => u.Email == "test#test.de");
This gave me the Ids of the n:m association table, which is not very usefull as I need the role names. Loading them using a second query is also not possible, since the Roles attribute of the identity user is readonly. I would expect to get a List<string> of the role-names. Couldn't find any information about this.
For me the only workaround seems to add a custom attribute to my user and fill them with the data, which I fetch using a second query. But thats not a nice solution. Cant belive that ASP.NET Core Identity has no better way of getting those data...
UserManager does not load the relations by default.
The manual inclusion is a good way but as stated here - direct M:N relationships are not yet supported by EntityFramework Core.
So there are two ways I see:
(The preffered one) Use
userManager.GetRolesAsync(user);
This will return a List<string> with user's role names. Or use some EF query to get an IdentityRole objects by joined IdentityUserRole. Unfortunatelly, this requires an acceptance with the fact the roles will not be directly in the User entity.
OR you can implement custom IdentityUserRole, create a relation to IdentityRole there and then query it with `
Include(user => user.Roles).ThenInclude(role => role.Role)
How to implement own Identity entities is described e.g. here. But it's complicated approach and the Role objects will be nested in the binding entities.
However, you can declare a property in your ApplicationUser:
[NotMapped]
public List<string> RoleNames {get; set;}
and use it at you free will...
Here is the solution I came up with. It may not be the most efficient. Hope it helps someone.
public class UserVM
{
public string Id { get; set; }
public string UserName { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public string Roles { get; set; }
}
// Project each element of query into List<UserVM>
var list = (from user in _userManager.Users
select new UserVM
{
Id = user.Id,
UserName = user.UserName,
Name = user.Name,
Email = user.Email
}).ToList();
list.Select(async user =>
{
var userEntity = await _userManager.FindByIdAsync(user.Id);
user.Roles = string.Join("; ", await _userManager.GetRolesAsync(userEntity));
}).ToList();
I have following tables:
Users, Groups, GroupUsers
I have my class like:
public class User
{
List<int> GroupIds;
}
I have a method in my Data Access Layer which returns a user after successful login with the list of all the group ids. How can I override or do something similar in identity to get information from that method?
I have searched the Internet a lot, watched YouTube tutorials, but no one is actually explaining it the way I need it. Can any one help?
First, use claims based security instead of role based: http://visualstudiomagazine.com/articles/2013/08/01/leveraging-claims-based-security-in-aspnet-45.aspx
I've also been retro-fitting an existing SQL based login system to work with identity management. Most of the work you're going to have to do lies within IdentityModel.cs. Specifically how you define ApplicationUser
public class ApplicationUser : IdentityUser<string, ApplicationUserLogin, ApplicationUserRole, ApplicationUserClaim>
{
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(ApplicationUserManager manager)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
// Add custom user claims here
return userIdentity;
}
}
I've overridden all 4 type parameters on the IdentityUser base class for my implementation, you may not need to do so, depends on how different your retrofit is from how Identity expects things to be.
The other main place you'll most likely have to customize things is within ApplicationDbContext where you'll have to set up where your user, group, and claims/role definitions reside with SQL.
I found these articles on typecastexception to be very useful:
http://typecastexception.com/post/2014/04/20/ASPNET-MVC-and-Identity-20-Understanding-the-Basics.aspx
http://typecastexception.com/post/2014/06/22/ASPNET-Identity-20-Customizing-Users-and-Roles.aspx
http://typecastexception.com/post/2014/04/20/ASPNET-Identity-20-Setting-Up-Account-Validation-and-Two-Factor-Authorization.aspx
http://typecastexception.com/post/2014/07/13/ASPNET-Identity-20-Extending-Identity-Models-and-Using-Integer-Keys-Instead-of-Strings.aspx
Overall there going to be a lot of trial and error in the process as you figure out what pieces of Identity you can utilize as is and where you need to plug in your own code.
Something else you may run into if your passwords aren't stored is having to provide your own implementation of PasswordHasher and plugging that in:
Asp.net Identity password hashing
I did' t get your question, if you want to override you have to mark the method virtual and inherit the class like this:
public class User
{
public virtual void YourMethod()
{
}
}
public class YourClass : User
{
public override void YourMethod()
{
}
}
If you want to separate the class to add some more mothods you can go like this:
partial class User
{
public static void YourMethod()
{
}
}
Create a UserInfo object
public class ApplicationUser : IdentityUser
{
public virtual UserInfo UserInfo { get; set; }
}
public class UserInfo : ComparableEntity
{
public string Email { get; set; }
public string FullName { get; set; }
public string KidName { get; set; }
public string MobilePhone { get; set; }
}
Then create a database context
public class DatabaseContext : IdentityDbContext<ApplicationUser>, IDatabaseContext
{
public IDbSet<UserInfo> UserInfos { get; set; }
}
This is a followup to the question here, where the answer seems to refer to an overly-complicated and overly-specific (EF, which I'm not using - not even using an ORM).
There has to be a more straightforward way around this common scenario than the smoke, mirrors, and sorcery hinted at in that answer.
Note: I encased "context" in parenthesis because I'm not using EF, so it is not a literal "dbcontext" that I'm talking about here.
So I got to wondering: Could I set a global variable for each session when the user is authenticated and authorized?
e.g., when the user is authenticated/authorized, I would know which database context/contents should be served up to him.
So it seems I could set a value in Global.asax.cs' Application_Start() method and then either alter the RepositoriesInstaller (implementing IWindsorInstaller) class to conditionally register different concrete Repositories based on the user and what data he should have OR place conditional code in the concrete Repository itself to use this or that database instance.
Is this feasible? Is one way (altering the RepositoriesInstaller / altering the concrete Repositories class) preferred?
Have a look at this answer which will show you how to resolve the correct repository based on a key or value.
If you want to store it with the authorized user, you need to simple serialize the data and store it on the authenticated cookie:
Create a Model to represent the logged in information:
public class AuthenticatedMember
{
public Guid Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public int SiteNumber { get; set; }
}
Do something like a login in your controller:
var authenticatedMember = MembershipManager.ValidateLogin(model.Email, model.Password);
var cookie = FormsAuthentication.GetAuthCookie(authenticatedMember.Id.ToString(), false);
var ticket = FormsAuthentication.Decrypt(cookie.Value);
var newTicket = new FormsAuthenticationTicket(ticket.Version, ticket.Name, ticket.IssueDate, ticket.Expiration, ticket.IsPersistent, authenticatedMember.ToJson(), ticket.CookiePath);
cookie.Value = FormsAuthentication.Encrypt(newTicket);
Response.Cookies.Add(cookie);
Then use a model binder to deserialize the AuthenticatedMember when you require:
public class AuthenticatedMemberModelBinder : IModelBinder
{
#region IModelBinder Members
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
if (bindingContext.Model != null)
{
throw new InvalidOperationException("Cannot update instances");
}
if (controllerContext.RequestContext.HttpContext.Request.IsAuthenticated)
{
var cookie = controllerContext
.RequestContext
.HttpContext
.Request
.Cookies[FormsAuthentication.FormsCookieName];
if (null == cookie)
return null;
var decrypted = FormsAuthentication.Decrypt(cookie.Value);
if (!string.IsNullOrEmpty(decrypted.UserData))
{
return JsonConvert.DeserializeObject<AuthenticatedMember>(decrypted.UserData);
}
}
return null;
}
#endregion
}
How can I integrate EF 5.0 with membership provider using code first?
I have my own database schema which I want to use for registration of users etc.
You should take a look at the SimpleMembershipProvider
It is very easy to use it together with EF.
Update
For MVC4 I would start with the blank template.
you need WebSecurity.InitializeDatabaseConnection to set up the database.
WebSecurity.InitializeDatabaseConnection("DefaultConnection", "Users", "Id", "UserName", true);
It takes as parameters a name of a connectionstring, the table, a unique identifier column and the username-column.
The model I use for this piece of code above is:
[Table("Users")]
public class User
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Required]
public string UserName { get; set; }
public string Email { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
To log someone in:
WebSecurity.Login(aUsername, aPassword);
The method above returns a bool, if it is true, login was successfull.
In the Web.config you do not need to define a membershipProvider as it was with default ASP.NET Membership.
If you need to gain access to the provider (to delete an account):
var provider = (SimpleMembershipProvider) Membership.Provider;
provider.DeleteAccount(aUsername); // Delete the account
provider.DeleteUser(aUsername, true); // delete the user and its data
For creating a new user (with my given model in this case)
WebSecurity.CreateUserAndAccount(aUsername, aPassword, new { Email = aEmail, FirstName = aFirstName, LastName = aLastName });
Benefit is, that you can now use your model for other EF classes as a foreign key without having the hassle when you want to do this with the normal asp.net membership. :-)
http://msdn.microsoft.com/en-us/library/f1kyba5e
This is actually nothing to do with EF.
EF is just a way to read data.
See also FORMS Authentication
http://support.microsoft.com/kb/301240?wa=wsignin1.0