0

I have an ASP.NET MVC5 CRUD application that is using Windows Authentication and the site is secured by an Active Directory group, all members in this group have the same permissions. I am using Entity Framework 6 to access the database.

I have a very basic User model at present where Login would be DOMAIN\username. All my models reference this model via CreatedByUserId property, I've omitted the relationships to other models below.

public class User
{
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }

    [Index(IsUnique = true)]
    [MaxLength(100)]
    public string Login { get; set; }

    [DatabaseGenerated(DatabaseGeneratedOption.Computed)]
    public DateTime CreatedDatetime { get; set; }
}

I want to be able to achieve two things though I am not sure how without repeating code in all my controllers:

  1. Users that are authenticated via the Active Directory group have a User model created and stored in the database, if it doesn't already exist.
  2. When new items are created the CreatedByUserId is populated with the Id of the User performing the action.

I believe the first part could be achieved via a global AuthorizeAttribute filter that would create a new User if they didn't already exist and retreive the Id when they do, but where should this be stored for future reference.

The second part I am not sure on, I believe it is possible to do this without having to keep querying the database based on the current user. Could this value be set via a constructor using the Id retrieved when the User accesses the site?

mheptinstall
  • 2,109
  • 3
  • 24
  • 44

1 Answers1

0

The custom Authorize attribute sounds like a good idea. You can verify that the user is authenticated and check that they are a member of the authorized group. I would suggest that you don't need to store their id separately. Their identity will be available throughout your application via

HttpContext.Current.Request.RequestContext.HttpContext.User.Identity.Name

In your DbContext you can override SaveChanges() and access the user's identity.

Here is a good link to get you started Overriding SaveChanges and setting ModifiedDate, but how do I set ModifiedBy?

In your authorize attribute you could do something like this:

protected override void OnAuthorization(AuthorizationContext filterContext)
{
    // Get user id (create if not exists) using 
    // filterContext.HttpContext.User.Identity.Name

    // Set  generic principal using Id
    IPrincipal principal = new GenericPrincipal(
    new GenericIdentity("myuserid"), new string[] { "myrole" });

    HttpContext.Current.User = principal;
}

Then your user id will be accessible anywhere the old DOMAIN\Username value was.

An extra point to bear in mind when authenticating via active directory is that users can and do change their usernames for example when getting married or divorced. AD also has a unique ID (a Guid) which can be used as a reference, however this can also change - usually when a user's profile gets deleted and recreated. If you are maintaining your own user records you should also maintain the AD Id and your logic should probably go something like:

Query My User table by username

Query AD by by username (returns AD Id as Guid)

My User exists -> update Ad Id if changed. Return user.

My User does not exist -> Query My User table by AD Id

My User exists -> update My User username. Return user.

My User does not exist -> Create new User (Username, Ad Id). Return user.

ste-fu
  • 6,879
  • 3
  • 27
  • 46
  • I plan to expand the `Users` table by having their real name and other details from active directory. The generated Id is used in my other models instead of storing the `DOMAIN\User` in every table. The data that this application will generate isn't going to be big so it wouldn't be a problem storing `DOMAIN\User` as the key. Though I'd be interested to hear how best to solve this without having to keep making a database call. – mheptinstall Oct 26 '16 at 10:37