Customized authorization attribute in MVC 4 with Roles
Asked Answered
D

3

0

I have created a customized role base authorization attribute.My idea is that when a user with role name "employee" Log In should not be allowed to access the "admin" page through URL. But when I implement the [MyRoleAuthorization] in Employee controller and Log In the error says "This webpage has a redirect loop". This is code for [MyRoleAuthorization]

public class MyRoleAuthorization : AuthorizeAttribute
{
    string isAuthorized;
    private string AuthorizeUser(AuthorizationContext filterContext)
    {
        if (filterContext.RequestContext.HttpContext != null)
        {
            var context = filterContext.RequestContext.HttpContext;


            if (Convert.ToString(context.Session["RoleName"]) == "Admin")
            {
                isAuthorized = "Admin";

            }
            else if (Convert.ToString(context.Session["RoleName"]) == "Employee")
            {
                isAuthorized = "Employee";

            }
            else if (Convert.ToString((context.Session["RoleName"])) == "Customer")
            {
                isAuthorized = "Customer";
            }
            else
            {
                throw new ArgumentException("filterContext");
            }
        }
        return isAuthorized;
    }

    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        if (filterContext == null)
            throw new ArgumentException("filterContext");

        if (AuthorizeUser(filterContext) == "Admin")
        {
            filterContext.Result = new RedirectToRouteResult
                 (new RouteValueDictionary(new { controller = "Admin" }));
        }

        else if (AuthorizeUser(filterContext) == "Employee")
        {
            filterContext.Result = new RedirectToRouteResult
                 (new RouteValueDictionary(new { controller = "Employee" }));
        }
        else if (AuthorizeUser(filterContext) == "Customer")
        {
            filterContext.Result = new RedirectToRouteResult
                 (new RouteValueDictionary(new { controller = "Customer" }));

        }
    }

}
} 

My Employee controller looks like this

   [MyRoleAuthorization]        
    public ActionResult Index()
    {
        var employee = db.Employee.Include(e => e.User);
        return View(employee.ToList());
    }

Can you please help me.

Doorstep answered 6/1, 2014 at 2:35 Comment(0)
J
1

Your redirection code is always going to redirect the user to the Employee Index Action, even when the action your are redirecting to is authenticated for the employee. You will need to provide another set of rules in your authorization and change your OnAuthorize method.

Such as

public class MyRoleAuthorization : AuthorizeAttribute
{
/// <summary>
/// the allowed types
/// </summary>
readonly string[] allowedTypes;

/// <summary>
/// Default constructor with the allowed user types
/// </summary>
/// <param name="allowedTypes"></param>
public MyRoleAuthorization(params string[] allowedTypes)
{
    this.allowedTypes = allowedTypes;
}

/// <summary>
/// Gets the allowed types
/// </summary>
public string[] AllowedTypes
{
    get { return this.allowedTypes; }
}

/// <summary>
/// Gets the authorize user
/// </summary>
/// <param name="filterContext">the context</param>
/// <returns></returns>
private string AuthorizeUser(AuthorizationContext filterContext)
{
    if (filterContext.RequestContext.HttpContext != null)
    {
        var context = filterContext.RequestContext.HttpContext;
        string roleName = Convert.ToString(context.Session["RoleName"]);
        switch (roleName)
        {
            case "Admin":
            case "Employee":
            case "Customer":
                return roleName;
            default:
                throw new ArgumentException("filterContext");
        }
    }
    throw new ArgumentException("filterContext");
}

/// <summary>
/// The authorization override
/// </summary>
/// <param name="filterContext"></param>
public override void OnAuthorization(AuthorizationContext filterContext)
{
    if (filterContext == null)
        throw new ArgumentException("filterContext");
    string authUser = AuthorizeUser(filterContext);
    if (!this.AllowedTypes.Any(x => x.Equals(authUser, StringComparison.CurrentCultureIgnoreCase)))
    {
        filterContext.Result = new HttpUnauthorizedResult();
        return;
    }
}

}

This can then be decorated as

public class EmployeeController : Controller
{
    [MyRoleAuthorization("Employee")]
    public ActionResult Index()
    {
        return View();
    }
}

Now your login code should be modified to send the user to the correct controller.

Junitajunius answered 6/1, 2014 at 2:54 Comment(0)
G
1

Your biggest problem is when you go to the employee controller as an employee, you're redirected to the employee controller, where your attribute redirects you to the employee controller and so on. Try to avoid redirecting within the attribute as it makes your code brittle & when you come back in a years time, you won't remember why your routes don't work as you intend

Try this:

public class MyRoleAuthorization : AuthorizeAttribute
{

    public string Role{get;set;}

    private string AuthorizeUser(AuthorizationContext filterContext)
    {
        if (filterContext.RequestContext.HttpContext != null)
        {
            var context = filterContext.RequestContext.HttpContext;

            return (string)context.Session["RoleName"];
        }
        throw new ArgumentException("filterContext");
    }

    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        if (filterContext == null)
            throw new ArgumentException("filterContext");

        var role = AuthorizeUser(filterContext);
        if (role.Equals(Role))
        {
        // insert positive outcome from role check, ie let the action continue
        }
        else
        {
        // denied! redirect to login page or show denied page (403)
        }
    }
} 


[MyRoleAuthorization("Employee")]        
public ActionResult Index()
{
    var employee = db.Employee.Include(e => e.User);
    return View(employee.ToList());
}
Gwyngwyneth answered 6/1, 2014 at 2:52 Comment(0)
J
1

Your redirection code is always going to redirect the user to the Employee Index Action, even when the action your are redirecting to is authenticated for the employee. You will need to provide another set of rules in your authorization and change your OnAuthorize method.

Such as

public class MyRoleAuthorization : AuthorizeAttribute
{
/// <summary>
/// the allowed types
/// </summary>
readonly string[] allowedTypes;

/// <summary>
/// Default constructor with the allowed user types
/// </summary>
/// <param name="allowedTypes"></param>
public MyRoleAuthorization(params string[] allowedTypes)
{
    this.allowedTypes = allowedTypes;
}

/// <summary>
/// Gets the allowed types
/// </summary>
public string[] AllowedTypes
{
    get { return this.allowedTypes; }
}

/// <summary>
/// Gets the authorize user
/// </summary>
/// <param name="filterContext">the context</param>
/// <returns></returns>
private string AuthorizeUser(AuthorizationContext filterContext)
{
    if (filterContext.RequestContext.HttpContext != null)
    {
        var context = filterContext.RequestContext.HttpContext;
        string roleName = Convert.ToString(context.Session["RoleName"]);
        switch (roleName)
        {
            case "Admin":
            case "Employee":
            case "Customer":
                return roleName;
            default:
                throw new ArgumentException("filterContext");
        }
    }
    throw new ArgumentException("filterContext");
}

/// <summary>
/// The authorization override
/// </summary>
/// <param name="filterContext"></param>
public override void OnAuthorization(AuthorizationContext filterContext)
{
    if (filterContext == null)
        throw new ArgumentException("filterContext");
    string authUser = AuthorizeUser(filterContext);
    if (!this.AllowedTypes.Any(x => x.Equals(authUser, StringComparison.CurrentCultureIgnoreCase)))
    {
        filterContext.Result = new HttpUnauthorizedResult();
        return;
    }
}

}

This can then be decorated as

public class EmployeeController : Controller
{
    [MyRoleAuthorization("Employee")]
    public ActionResult Index()
    {
        return View();
    }
}

Now your login code should be modified to send the user to the correct controller.

Junitajunius answered 6/1, 2014 at 2:54 Comment(0)
S
0

It seems that when authorized, you redirect to the Customer controller, for example. This controller likely has your attribute on it, and so it authorizes the user, who is seen as a customer, and redirects to the Customer controller... Which has your attribute on it, and so it authorizes the user...

Infinite loop.

Stalag answered 6/1, 2014 at 2:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.