locked
Need help on Custom AuthorizeAttribute implementation RRS feed

  • Question

  • User1094269964 posted

    HI Friends,

    Need help on the below scenario for  Custom AuthorizeAttribute implementation.

    When the user login using  Authentication/Login(FormCollection form) users roles will be fetched from database. See my code below but its not working 

    Lets say User1 logins to the application and we fetches his UserId,UserName and his roles.

    Lets say his roles are "Admin,Developer,Tester" then he is allowed to call all the Action methods of AdminController,DeveloperController and TestController

    But in case User1 has "Admin" role alone then he is allowed to call all the Action method of AdminController Only. 

    But in case User1 has "Developer" role alone then he is allowed to call all the Action method of DeveloperController Only. 

    How to do the above. Please help.

    Below is my code and it is not working as per the above

     

    namespace Sample.Controllers
    {
    public class AuthenticationController : Controller
    {

    [HttpGet]
    public ActionResult Login()
    {
    return View();
    }


    [HttpPost]

    public void Login(FormCollection form)
    {
    UserSession userSession = UserSession.LoadUserSession(System.Web.HttpContext.Current.Session);
    userSession.UserID = form["UserId"].ToUpper();
    string url = "~/Authentication/NotAuthorized";
    try
    {
    userSession.ValidateUser();
    if(userSession.HasUserAuthenticated)
    {
    UserSession.SaveUserSession(System.Web.HttpContext.Current.Session, userSession);
    url = "~/Home/Index";
    }
    }
    catch(Exception ex)
    {
    throw ex;
    }
    this.Response.Redirect(url, false);
    }

    [HttpGet]
    public ActionResult NotAuthorized()
    {
    return View();
    }
    }

    public class AdminController :Controller
    {
    [HttpGet]
    [CustomAuthorize(Roles.ADMIN)]
    public ActionResult Index()
    {
    return View();
    }

    [HttpPost]
    [CustomAuthorize(Roles.ADMIN)]
    public ActionResult Index(FormCollection collection)
    {
    return View();
    }
    }

    public class DeveloperController :Controller
    {
    [HttpGet]
    [CustomAuthorize(Roles.ADMIN,Roles.DEVELOPER)]
    public ActionResult Index()
    {
    return View();
    }

    [HttpPost]
    [CustomAuthorize(Roles.ADMIN, Roles.DEVELOPER)]
    public ActionResult Index(FormCollection collection)
    {
    return View();
    }
    }

    public class TesterController :Controller
    {
    [HttpGet]
    [CustomAuthorize(Roles.ADMIN, Roles.TESTER)]
    public ActionResult Index()
    {
    return View();
    }

    [HttpPost]
    [CustomAuthorize(Roles.ADMIN, Roles.TESTER)]
    public ActionResult Index(FormCollection collection)
    {
    return View();
    }
    }
    }

    public class CustomAuthorize : AuthorizeAttribute
    {
    public readonly string[] AllowedRoles;
    UserSession loggedInUser = null;

    public CustomAuthorize(params string[] roles)
    {
    this.AllowedRoles = roles;
    }
    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
    loggedInUser = UserSession.LoadUserSession(System.Web.HttpContext.Current.Session);
    bool authorize = false;
    if(loggedInUser.HasUserAuthenticated)
    {
    if(this.AllowedRoles.Count() > 0)
    {
    foreach (string role in this.AllowedRoles)
    {
    if (loggedInUser.UserRoles.Contains(role))
    {
    authorize = true;
    }
    }
    }
    authorize = true;
    }
    return authorize;
    }

    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
    {
    if(loggedInUser.HasUserAuthenticated)
    {
    filterContext.Result = new RedirectResult("/Authentication/NotAuthorized");
    }
    else
    {
    filterContext.Result = new HttpUnauthorizedResult();
    }
    }

    }


    public class UserSession
    {
    public string UserID { get; set; }
    public string UserName { get; set; }
    public List<string> UserRoles { get; set; }
    public UserSession()
    {
    this.UserRoles = new List<string>();
    }
    public bool HasUserAuthenticated { get; set; }

    public void ValidateUser()
    {
    OracleParameter[] param = null;
    param = new OracleParameter[] { new OracleParameter("UserId", this.UserID) };
    OracleDataReader reader = DBHelper.ExecuteReader("select UserID,UserName,UserRoles from UserTable where UserId=:UserId", param);
    while (reader.Read())
    {
    this.UserID = reader.GetString(0);
    this.UserName = reader.GetString(1);
    string roles = reader.GetString(2); //here we will get command separated string
    foreach (string role in roles.Split(','))
    {
    this.UserRoles.Add(role);
    }
    }

    }

    public static UserSession LoadUserSession(HttpSessionState state)
    {
    if (state["_userSession"] == null)
    {
    return new UserSession();
    }
    return state["_userSession"] as UserSession;
    }

    public static void SaveUserSession(HttpSessionState state, UserSession session)
    {
    state["_userSession"] = state;
    }
    }

    public class DBHelper
    {
    public static OracleDataReader ExecuteReader(string query, OracleParameter[] param)
    {
    OracleConnection connection = new OracleConnection("somestring");
    try
    {
    connection.Open();
    OracleCommand cmd = new OracleCommand(query, connection);
    foreach (var item in param)
    {
    cmd.Parameters.Add(item);
    }
    return cmd.ExecuteReader();

    }
    catch
    {
    return null;
    }
    }
    }

    public class SampleConstants
    {

    }
    public static class Roles
    {
    public const string ADMIN = "Admin";
    public const string DEVELOPER = "Developer";
    public const string TESTER = "Tester";

    }

    Saturday, June 20, 2020 4:52 PM

All replies

  • User475983607 posted

    Please use the standard APIs rather than building your own if you are not familiar with writing a secure application.   

    This feature is built into the Identity API.  Identity is a full featured security API with a data store. 

    https://docs.microsoft.com/en-us/aspnet/identity/overview/getting-started/introduction-to-aspnet-identity#aspnet-identity

    If you do not wish to use Identity and have a custom data store, then you can implement the standard Authentication Cookie that comes with ASP.NET.  All you have to do is set the user's roles/claims during login.  The roles/claims are cached in the authentication cookie and the standard MVC [Authorize] attributes will function as expected.

    https://stackoverflow.com/questions/31511386/owin-cookie-authentication-without-asp-net-identity

    If you still wish to build customer security then I'm afraid you'll be on your own.

    Saturday, June 20, 2020 6:18 PM
  • User1094269964 posted

    Thanks for the reponse. This is an existing application and we cannot change everything. But I need to fix the above scenario issue

    Sunday, June 21, 2020 9:04 AM
  • User475983607 posted

    Thanks for the reponse. This is an existing application and we cannot change everything. But I need to fix the above scenario issue

    I think the main issue is the ValidateUser method does not validate the user it tries to populate a UserSession object but never sets the HasUserAuthenticated property if validation is successful.  At least I do not see the code.   This is an easy bug to find if you were using the Visual Studio debugger.

    There are other issues with the code and design. The UserTable is not normalized.  It is a well known fact that denormalized tables in a relational database cause complex downstream code.  The data access classes do not clean up connections or reader resources properly. 

    There is not much to change if you go with the OWIN Cookie Authentication.  It a simple matter of replacing your code with the standard MVC libraries.  the benefit is the standard libraries have been tested and are known to function properly.

    Sunday, June 21, 2020 1:28 PM