Search code examples
entity-frameworkasp.net-mvc-4membership-providerdbcontextroleprovider

Custom RoleProvider and Custom Membership provider


I am working on an MVC4 application that has authentication completed by ADFS before the application is launched and will only be launched by successfully authenticated users. Within the application itself I have a list of users held within the following class:

public class MyAppUser : IPrincipal
{
    [Key]
    public int UserID { get; set; }

    [StringLength(128)]
    public string Username { get; set; }              

    public int ParticipationPoints { get; set; }

    public DateTime JoinDate { get; set; }

    public DateTime LastLogin { get; set; }

    //1-to-1 Mappings
    public int? CompanyID { get; set; }
    public virtual Company Company { get; set; }

    public int? ProfileID { get; set; }
    public virtual Profile Profile { get; set; }

    public int? SocialID { get; set; }
    public virtual SocialMedia Social { get; set; }

    public int? RoleID { get; set; }
    public virtual Role Role { get; set; }

    //1-to-many Mappings
    public virtual ICollection<Block> Blocks { get; set; }

    public virtual ICollection<Enrollment> Enrollments { get; set; }

    public virtual ICollection<CategoryMap> CategoryMaps { get; set; }

    public virtual ICollection<Feedback> Feedbacks { get; set; }

    public virtual ICollection<Survey> Surveys { get; set; }

    public virtual ICollection<Friend> Friends { get; set; }

    public virtual ICollection<Participation> Participations { get; set; }

    public virtual ICollection<Post> Posts { get; set; }

    public virtual ICollection<Target> Targets { get; set; }

    public virtual ICollection<CourseStatus> CourseStatuses { get; set; }

    public virtual ICollection<Objective> Objectives { get; set; }

    public virtual ICollection<UserCourseInfo> UserCourseInfos { get; set; }

    public IIdentity Identity { get; set; }

    public bool IsInRole(string role)
    {
        if (this.Role.Name == role)
        {
            return true;
        }
        return false;
    }
}

I have created a custom role provider that is tied to my dbcontext as follows:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Configuration.Provider;
using MyApp.Data;
using MyApp.ViewModels;

namespace MyApp.Providers
{
  public class MyAppProvider : System.Web.Security.SqlRoleProvider
  {
    private MyAppContext dbcontext = new MyAppContext(System.Configuration.ConfigurationManager.ConnectionStrings["MyAppContext"].ConnectionString);
    private Repository<MyAppUser> userRepository;
    private Repository<Role> roleRepository;

    public MyAppProvider()
    {
        this.userRepository = new Repository<MyAppUser>(dbcontext);
        this.roleRepository = new Repository<Role>(dbcontext);
    }

    public override string[] GetAllRoles()
    {
        IEnumerable<Role> dbRoles = roleRepository.GetAll();
        int dbRolesCount = roleRepository.GetAll().Count();
        string[] roles = new string[dbRolesCount];
        int i = 0;
        foreach(var role in dbRoles)
        {
            roles[i] = role.Name;
            i++;
        }
        return roles;
    }

    public string[] GetAllUsers()
    {
        IEnumerable<MyAppUser> dbUsers = userRepository.GetAll();
        int dbUsersCount = userRepository.GetAll().Count();
        string[] users = new string[dbUsersCount];
        int i = 0;
        foreach (var user in dbUsers)
        {
            users[i] = user.Username;
            i++;
        }
        return users;
    }

    public override bool RoleExists(string roleName)
    {
        string[] roles = { "Admin", "User", "BobsBusiness" };
        if(roles.Contains(roleName))
            return true;
        else
            return false;
    }

    public override void CreateRole(string roleName)
    {
        Role newRole = new Role();
        newRole.Name = roleName;
        roleRepository.Add(newRole);
        roleRepository.SaveChanges();
    }

    public override void AddUsersToRoles(string[] usernames, string[] roleNames)
    {
        foreach (var username in usernames)
        {
            MyAppUser user = userRepository.Get(u => u.Username == username).FirstOrDefault();

            foreach (var rolename in roleNames)
            {
                Role role = roleRepository.Get(r => r.Name == rolename).FirstOrDefault();
                user.RoleID = role.RoleID;
                userRepository.Add(user);
                userRepository.SaveChanges();
            }

        }
    }

    public override string[] GetRolesForUser(string username)
    {
        MyAppUser user = userRepository.Get(u => u.Username == username).FirstOrDefault();
        if (user == null)
        {
            string[] roles = new string[1];
            roles[0] = "Fail";
            return roles;
        }
        else
        {
            Role role = roleRepository.Get(r => r.RoleID == user.RoleID).FirstOrDefault();
            if (role == null)
            {
                string[] roles = new string[1];
                roles[0] = "Fail";
                return roles;
            }
            else
            {
                string[] roles = new string[1];
                roles[0] = role.Name;
                return roles;
            }
        }

    }

    public override bool IsUserInRole(string userName, string roleName)
    {
        MyAppUser user = userRepository.Get(u => u.Username == userName).FirstOrDefault();
        if (user == null)
          throw new ProviderException("Username cannot be empty or null.");
        Role role = roleRepository.Get(r => r.Name == roleName).FirstOrDefault();
        if (role == null)
          throw new ProviderException("Role name cannot be empty or null.");
        if (user.RoleID == role.RoleID)
            return true;
        else
            return false;
    }
  }
}

Now I have tried to create a CustomMembershipProvider but I am unsure how to tie this to the dbcontext and the MyAppUser that I have created. Due to the authentication type we do not have an AccountController and so simpleMembership is not an option. I have created the abstract class for the custom membership provider as follows:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using MyApp.Data;

namespace MyApp.Providers
{
  public class MyAppMembershipProvider : System.Web.Security.MembershipProvider
  {
    private MyAppContext dbcontext = new MyAppContext(System.Configuration.ConfigurationManager.ConnectionStrings["MyAppContext"].ConnectionString);
    private Repository<MyAppUser> userRepository;
    public MyAppUser User { get; private set; }

    public MyAppMembershipProvider()
    {
        this.userRepository = new Repository<MyAppUser>(dbcontext);
        User = userRepository.Get(u => u.Username == "jpmcfeely").FirstOrDefault();
    }

    public override string ApplicationName
    {
        get
        {
            throw new NotImplementedException();
        }
        set
        {
            throw new NotImplementedException();
        }
    }

    public override bool ChangePassword(string username, string oldPassword, string newPassword)
    {
        throw new NotImplementedException();
    }

    public override bool ChangePasswordQuestionAndAnswer(string username, string password, string newPasswordQuestion, string newPasswordAnswer)
    {
        throw new NotImplementedException();
    }

    public override System.Web.Security.MembershipUser CreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out System.Web.Security.MembershipCreateStatus status)
    {
        throw new NotImplementedException();
    }

    public override bool DeleteUser(string username, bool deleteAllRelatedData)
    {
        throw new NotImplementedException();
    }

    public override bool EnablePasswordReset
    {
        get { throw new NotImplementedException(); }
    }

    public override bool EnablePasswordRetrieval
    {
        get { throw new NotImplementedException(); }
    }

    public override System.Web.Security.MembershipUserCollection FindUsersByEmail(string emailToMatch, int pageIndex, int pageSize, out int totalRecords)
    {
        throw new NotImplementedException();
    }

    public override System.Web.Security.MembershipUserCollection FindUsersByName(string usernameToMatch, int pageIndex, int pageSize, out int totalRecords)
    {
        throw new NotImplementedException();
    }

    public override System.Web.Security.MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords)
    {
        throw new NotImplementedException();
    }

    public override int GetNumberOfUsersOnline()
    {
        throw new NotImplementedException();
    }

    public override string GetPassword(string username, string answer)
    {
        throw new NotImplementedException();
    }

    public override System.Web.Security.MembershipUser GetUser(string username, bool userIsOnline)
    {
        //throw new NotImplementedException();
        MyAppUser user = userRepository.Get(u => u.Username == username).FirstOrDefault();
        return GetUser(username, true);
    }

    public override System.Web.Security.MembershipUser GetUser(object providerUserKey, bool userIsOnline)
    {
        throw new NotImplementedException();
    }

    public override string GetUserNameByEmail(string email)
    {
        throw new NotImplementedException();
    }

    public override int MaxInvalidPasswordAttempts
    {
        get { throw new NotImplementedException(); }
    }

    public override int MinRequiredNonAlphanumericCharacters
    {
        get { throw new NotImplementedException(); }
    }

    public override int MinRequiredPasswordLength
    {
        get { throw new NotImplementedException(); }
    }

    public override int PasswordAttemptWindow
    {
        get { throw new NotImplementedException(); }
    }

    public override System.Web.Security.MembershipPasswordFormat PasswordFormat
    {
        get { throw new NotImplementedException(); }
    }

    public override string PasswordStrengthRegularExpression
    {
        get { throw new NotImplementedException(); }
    }

    public override bool RequiresQuestionAndAnswer
    {
        get { throw new NotImplementedException(); }
    }

    public override bool RequiresUniqueEmail
    {
        get { throw new NotImplementedException(); }
    }

    public override string ResetPassword(string username, string answer)
    {
        throw new NotImplementedException();
    }

    public override bool UnlockUser(string userName)
    {
        throw new NotImplementedException();
    }

    public override void UpdateUser(System.Web.Security.MembershipUser user)
    {
        throw new NotImplementedException();
    }

    public override bool ValidateUser(string username, string password)
    {
        throw new NotImplementedException();
    }
  }
}

I would be very grateful for any tips anyone has in connecting the MyAppUser to the custom membership provider.


Solution

  • As I was already authenticated by ADFS I had gotten the username in the claims and held it in session variable. I then used this session variable to call my database user with the GetRolesForUser method that I had overriden. Works great not too much effort.