6

I have created a custom authentication and authorisation for my users.The problem I am facing is how to get mvc to check that role from inside my users table matches the [Authorize(Role)] on my controller so as to set httpauthorised to true.Below is my customauthorise class.

 [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = true)]
public class CustomAuthorizeAttribute : AuthorizeAttribute 
{
    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        base.OnAuthorization(filterContext);
        if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
        {
            filterContext.Controller.TempData["ErrorDetails"] = "You must be logged in to access this page";
            filterContext.Result = new RedirectResult("~/User/Login");
            return;
        }

        if (filterContext.HttpContext.Request.IsAuthenticated)
        {
            using (var db = new GManagerDBEntities())
            {
                var authorizedRoles = (from u in db.Users
                                       where u.Username == filterContext.HttpContext.User.Identity.Name
                                       select u.Role).FirstOrDefault();
                Roles = String.IsNullOrEmpty(Roles) ? authorizedRoles.ToString() : Roles;
            }
        }

        if (filterContext.Result is HttpUnauthorizedResult)
        {
            filterContext.Controller.TempData["ErrorDetails"] = "You do nat have necessary rights to access this page";
            filterContext.Result = new RedirectResult("~/User/Login");
            return;
        }

    }
    public CustomAuthorizeAttribute(params object[] roles)
    {
        if (roles.Any(r => r.GetType().BaseType != typeof(Enum)))
            throw new ArgumentException("roles");

        this.Roles = string.Join(",", roles.Select(r => Enum.GetName(r.GetType(), r)));
    }
}

below is my controller with decoration

 [CustomAuthorize(Role.Administrator)]
    [HttpGet]
    public ActionResult CreateEmployees()
    {
        return View();
    }

and my enum for role

public enum Role
{
    Administrator = 1,
    UserWithPrivileges = 2,
    User = 3,
}

and model

public class UserModel
{
    public int UserID { get; set; }
    [Required]
    [Display(Name="Username:")]
    public string Username { get; set; }
    [Required]
    public string Password { get; set; }
    public int Role { get; set; }
}

see pastie for clear view pastie

links I have viewed in trying to solve this among others but I cant seem to piece it togetherMVC 3 Authorize custom roles http://forums.asp.net/p/1573254/3948388.aspx

Customized authorization attribute in MVC 4 with Roles

4
  • 1
    take a look at codeproject.com/Articles/578374/… Commented Aug 28, 2014 at 12:18
  • Am hoping to use one single table and role as an integer but if all fails will just create a roles table and implement your suggestion @VikasRana Commented Aug 28, 2014 at 12:55
  • I tried changing it according to your link to this pastie.org/9510205 still no luck Commented Aug 28, 2014 at 14:26
  • Finally managed I hadnt put the ' protected void Application_PostAuthenticateRequest(Object sender, EventArgs e)' method in global.asax Commented Aug 29, 2014 at 7:06

2 Answers 2

4

using the link shared by @VikasRana http://www.codeproject.com/Articles/578374/AplusBeginner-splusTutorialplusonplusCustomplusF

I got rid of my enum Role and my method

public CustomAuthorizeAttribute(params object[] roles)
    { ...}

I then changed Role in my model to be a string e.g. User.Role="Admin" instead of int. In my onAuthorization method I changed it to:

` public override void OnAuthorization(AuthorizationContext filterContext)
    {
        base.OnAuthorization(filterContext);
        if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
        {
            filterContext.Controller.TempData["ErrorDetails"] = "You must be logged in to access this page";
            filterContext.Result = new RedirectResult("~/User/Login");
            return;
        }
        if (filterContext.Result is HttpUnauthorizedResult)
        {
            filterContext.Controller.TempData["ErrorDetails"] = "You don't have access rights to this page";
            filterContext.Result = new RedirectResult("~/User/Login");
            return;
        }
        }

and in my global.asax added this.

protected void Application_PostAuthenticateRequest(Object sender, EventArgs e)
    {
        if (FormsAuthentication.CookiesSupported == true && Request.IsAuthenticated== true)
        {
            if (Request.Cookies[FormsAuthentication.FormsCookieName] != null)
            {
                try
                {
                    //let us take out the username now                
                    string username = FormsAuthentication.Decrypt(Request.Cookies[FormsAuthentication.FormsCookieName].Value).Name;
                    string roles = string.Empty;

                    using (GManagerDBEntities db = new GManagerDBEntities())
                    {
                        User user = db.Users.SingleOrDefault(u => u.Username == username);

                        roles = user.Role;
                    }
                    //let us extract the roles from our own custom cookie
                    //Let us set the Pricipal with our user specific details
                    HttpContext.Current.User = new System.Security.Principal.GenericPrincipal(
                      new System.Security.Principal.GenericIdentity(username, "Forms"), roles.Split(';'));
                }
                catch (Exception)
                {
                    //something went wrong
                }
            }
        }
    }   

Above method is not ideal though.It gets run for every simple page request about 3 times or more.

So here is solution 2:better solution Implement a custom role provider since we are already using custom role implementation.Simply follow this linkhttp://techbrij.com/custom-roleprovider-authorization-asp-net-mvc

Sign up to request clarification or add additional context in comments.

Comments

1

Thanks Gotalove for this method in Global.asax. Here is some more help for anyone trying to do a custom Forms Authentication (FormsAuthentication, FormsAuthenticationTicket) using entity framework.

Login Controller SetAuthTicket

protected void GetRoles(int UserID)
    {
        var db = new ResearchSurveysEntities();
        string[] getRoles = { };
        try
        {
            var query =

                from p in db.UserProfiles
                join i in db.webpages_UsersInRoles on p.UserId equals i.UserId
                join r in db.webpages_Roles on i.RoleId equals r.RoleId
                where p.UserId == UserID
                select new { p.UserId, r.RoleName };

            if (query.Count() > 0)
            {

                List<string> gRoles = new List<string>();
                foreach (var item in query)
                {
                    gRoles.Add(item.RoleName);
                }
                getRoles = gRoles.ToArray();
            }
            roles = String.Join("|", getRoles);
        }
        catch (Exception ex)
        {
            WebUtilities wu = new WebUtilities();

            wu.NotifyWebmaster(ex.ToString(), "Get roles for AdminUserID: " + UserID.ToString(), string.Empty, "Login Error");


        }
        finally
        {
            db.Dispose();
        }
    }

WebConfig

<authentication mode="Forms">
  <forms loginUrl="~/Account/Login" timeout="2880" />
</authentication>

Global.asax (From above example)

protected void Application_AuthenticateRequest(Object sender, EventArgs e)
    {
        HttpCookie authCookie = Context.Request.Cookies[FormsAuthentication.FormsCookieName];
        if (authCookie == null || authCookie.Value == "")
            return;

        FormsAuthenticationTicket authTicket;
        try
        {
            authTicket = FormsAuthentication.Decrypt(authCookie.Value);
        }
        catch
        {
            return;
        }

        // retrieve roles from UserData
        string[] roles = authTicket.UserData.Split('|');

        if (Context.User != null)
            Context.User = new GenericPrincipal(Context.User.Identity, roles);
    }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.