6

I am new to MVC. I searched and found no solution that fits for my requirements.

I am developing a web portal for our team's internal use which uses Windows AD authentication for login. However, for role based access I have created a local database which can return the Role details for the user. I have created a custom Authorization filter that allows me to handle authorization based on the user's role. This filter is querying details from the DB however the issue with this approach is, it will try to get details from DB for every request to the controller which makes it expensive.

How can I save the user details fetched from the DB in a Token such that I don't have to query DB for every request and use the Token values inside the Authorization filter. Also, I can use the values retrieved for the user from the database, anywhere else in the app. (There are some other details for the user we have in the Database).

If someone can suggest a better way to achieve this, please help.

Here is the code that I am currently using inside Authorization filter:

public class AuthorizeRole : AuthorizeAttribute
{
    private bool _authenticated;
    private bool _authorized;

    public string InRoles { get; set; }

    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
    {
        base.HandleUnauthorizedRequest(filterContext);

        if (_authenticated && !_authorized)
        {
            filterContext.Result = new RedirectResult("/account/error");
        }
    }

    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        _authenticated = base.AuthorizeCore(httpContext);

        if (_authenticated)
        {
            if (string.IsNullOrEmpty(InRoles))
            {
                _authorized = true;
                return _authorized;
            }

            if (!string.IsNullOrEmpty(httpContext.User.Identity.Name))
            {
                string NTID = httpContext.User.Identity.Name.Split('\\')[1];
                var roles = InRoles.Split(',');

                using (Models.UserAuthEntities userAuthEntities = new Models.UserAuthEntities())
                {
                    try
                    {
                        ObjectResult<Models.Validate_User_Login_Result> userResults = userAuthEntities.Validate_User_Login(NTID);
                        var user = userResults.FirstOrDefault(all => all.NTID == NTID);

                        if (user == null)
                        {
                            _authorized = false;
                            return _authorized;
                        }
                        else
                        {
                            if (roles.Contains(user.Role))
                            {
                                return _authorized;
                            }
                        }
                    }
                    catch (Exception)
                    {
                        _authorized = false;
                        return _authorized;
                    }
                }
            }
            else
            {
                _authorized = false;
                return _authorized;
            }
        }

        _authorized = false;
        return _authorized;
    }
}

Please suggest at which section to use the code you will be suggesting (like, inside controller, inside filter or somewhere else.)

I found this solution at: this site but there it is used for AD groups.

ArunPratap
  • 4,816
  • 7
  • 25
  • 43
SavindraSingh
  • 878
  • 13
  • 39
  • Im relatively new to Azure AD myself, but what about saving the role details into TempData and then passing it to each page where you might need it ? Perhaps too simple, or I am missing what you want to do ? – AxleWack Mar 13 '19 at 09:22
  • This has nothing to do with Azure AD. This is on-prem AD. Regarding saving role details to TempData, can you please guide me on how to do that (sorry I am new to this) with some sample code and where to use that code. – SavindraSingh Mar 13 '19 at 10:04
  • Ah sorry... I am busy working on Azure AD at the moment, so when you said Active Directory, I read wrong. My apologies. So in your ActionResult, you can set TempData like this(Just an example) : `TempData["RoleName"] = "User";` You can then use it like this: `string userRoleName = TempData["RoleName"].ToString();` - Again, not sure if this is what you are looking for, but this way you can pass data from one controller to another without it being included in your URL. – AxleWack Mar 13 '19 at 10:10
  • But we need to check role before we reach action result, right! In the code I posted, I am checking in `AuthorizeAttribute` if the user is in a role or not, and that happens before even reaching the action result for any controller. Now, the question is, where should I set the `TempData`. – SavindraSingh Mar 13 '19 at 10:46
  • @SavindraSingh are you using .NET Core? – Søren Mar 13 '19 at 11:28
  • No. I am using Asp.net MVC only. – SavindraSingh Mar 13 '19 at 15:06
  • @SavindraSingh are you using a SignInManager or something similar to manage user login? – Canica Mar 15 '19 at 14:38
  • I don't know what is `SignInManager`. I am using Windows authentication for authenticating the user but I need to check a database table for checking role based access. – SavindraSingh Mar 16 '19 at 01:44

2 Answers2

2

I have checked the cookie in override version of method AuthorizeCore it is working now:

    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        string cookieName = FormsAuthentication.FormsCookieName;
        HttpCookie authCookie = httpContext.Request.Cookies[cookieName];
        _authenticated = base.AuthorizeCore(httpContext);
        string authToken = httpContext.Request.Headers["Auth-Token"];

        if (_authenticated)
        {
            if (authCookie == null)
            {
                if (string.IsNullOrEmpty(InRoles))
                {
                    _authorized = true;
                    return _authorized;
                }

                if (!string.IsNullOrEmpty(httpContext.User.Identity.Name))
                {
                    string NTID = httpContext.User.Identity.Name.Split('\\')[1];
                    var roles = InRoles.Split(',');

                    using (Models.UserAuthEntities userAuthEntities = new Models.UserAuthEntities())
                    {
                        try
                        {
                            ObjectResult<Models.Validate_User_Login_Result> userResults = userAuthEntities.Validate_User_Login(NTID);
                            var user = userResults.FirstOrDefault(all => all.NTID == NTID);

                            if (user == null)
                            {
                                _authorized = false;
                                return _authorized;
                            }
                            else
                            {
                                if (roles.Contains(user.Role))
                                {
                                    _authorized = true;
                                    return _authorized;
                                }
                            }
                        }
                        catch (Exception)
                        {
                            _authorized = false;
                            return _authorized;
                        }
                    }
                }
                else
                {
                    _authorized = false;
                    return _authorized;
                }
            }
        }

        _authorized = false;
        return _authorized;
    }
SavindraSingh
  • 878
  • 13
  • 39
1

First off, you might want to look at using AD Security Groups to manage access. That way OPS can continue to maintain access in a familiar time-tested platform and you dont have to write your own security definition interface.

As for the MVC security persistence All you have to do is add a manual log in to perform your above logic and then you use the built-in Membership Provider (for whatever version of MVC you are using) to log the user in. MVC will handle maintaining the logged in state and tokenising for you and you can specify things like timeout in the web.config (or settings.json in Core).

Sorry i dont have any sample code at hand for illustration.

Stark
  • 444
  • 5
  • 15
  • Earlier I had plans to use AD groups. However, our company already have a database which was used for some other applications and as per the requirements, I have to use the same. Can we use the role details from the database, if yes then I don't know how. – SavindraSingh Mar 02 '19 at 10:23