0

I am trying to lock my application down to each method where by users and admin can see the whole controller but only administrators can create, delete, and edit.

I have created a CustomAuthorize class which holds the method:

 public class CustomAuthorize : AuthorizeAttribute
    {
        public override void OnAuthorization(AuthorizationContext filterContext)
        {
            base.OnAuthorization(filterContext);
            Debug.WriteLine("Show me the filterContext " + filterContext);
            if (filterContext.Result is HttpUnauthorizedResult)
            {
                Debug.WriteLine("Why is this going to the redirect to AccessDenied?");
                filterContext.Result = new RedirectResult("~/AccessDenied/Index");
            }
        }
    }

My RoleProvider Class:

namespace Office.WebUI.Security {
    public class OfficeRoleProvider : RoleProvider
    {

        public override void AddUsersToRoles(string[] usernames, string[] roleNames)
        {
            throw new NotImplementedException();
        }

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

        public override void CreateRole(string roleName)
        {
            throw new NotImplementedException();
        }

        public override bool DeleteRole(string roleName, bool throwOnPopulatedRole)
        {
            throw new NotImplementedException();
        }

        public override string[] FindUsersInRole(string roleName, string usernameToMatch)
        {
            throw new NotImplementedException();
        }

        public override string[] GetAllRoles()
        {
            throw new NotImplementedException();
        }

        public override string[] GetRolesForUser(string username)
        {
            Debug.WriteLine("Get the username" + username);
            using (EFDbContext db = new EFDbContext()) >           
            {
                User user = db.Users.FirstOrDefault(u => u.UserName.Equals(username, >stringComparison.CurrentCultureIgnoreCase) || u.Email.Equals(username, StringComparison.CurrentCultureIgnoreCase));
                //User user = db.TaskPrivilege.FirstOrDefault(u => >u.UserName.Equals(username, StringComparison.CurrentCultureIgnoreCase) || u.Email.Equals(username, StringComparison.CurrentCultureIgnoreCase));

                if (user != null)
                {
                    var roles = from ur in user.UserRoles
                                from r in db.Roles
                                where user.role.RoleID == r.RoleID
                                //where ur.RoleID == r.RoleID
                                select r.Name;
                    if (roles != null)
                        return roles.ToArray();
                    else
                        return new string[] { }; ;
                }
                else
                {
                    return new string[] { }; ;
                }
            }
        }

        public override string[] GetUsersInRole(string roleName)
        {
           throw new NotImplementedException();
        }

        public override bool IsUserInRole(string username, string roleName)
        {
            Debug.WriteLine("The username is " + username + " And the Role it is using is >" + roleName);
            using (EFDbContext db = new EFDbContext())
            {
                User user = db.Users.FirstOrDefault(u => u.UserName.Equals(username, StringComparison.CurrentCultureIgnoreCase) || u.Email.Equals(username, StringComparison.CurrentCultureIgnoreCase));

                if (user != null)
                {
                    var roles = from ur in user.UserRoles
                                from r in db.Roles
                                where user.role.RoleID == r.RoleID
                                //where ur.RoleID == r.RoleID
                                select r.Name;
                    if (user != null)
                        return roles.Any(r => r.Equals(roleName, >StringComparison.CurrentCultureIgnoreCase));
                    else
                        return false;
                }
                else
                {
                    return false;
                }
            }
        }

        public override void RemoveUsersFromRoles(string[] usernames, string[] roleNames)
        {
            throw new NotImplementedException();
        }

        public override bool RoleExists(string roleName)
        {
            throw new NotImplementedException();
        }
    }

}

In my Database I have a Roles table that holds Role and RoleID as columns, with 2 values Administrator and User as roles with a roleID as the PK, 1 = Administrator & 2 = User. Another table called Users, that contains UserID, Username and role_RoleID

When I assign a user to role_RoleID as 1 (administrator) and lock down a controller using

[CustomAuthorize(Roles = "Administrator"]

This works.

When I assign a role_RoldID = 2 and lock down using:

[CustomAuthorize(Roles = "User"]

This doesn't work?

Also another question is, how do I assign 2 roles to a controller?, I then want to lock down each method to 1 User Role.

Hope that makes sense and I hope someone can help me please.

Thanks

Steven

Basically I have a controller which I have put [CustomAuthorize(Roles = "Administrators, Support")] to lock it down so both roles can see the controller.

Then I have create/delete and edit methods I have:

[CustomAuthorize(Roles = "Administrators")]

so basically I want to show the whole controller to both roles but only administrators can create/edit/delete.

When I assign a roleID in the user table = 2(User) the whole application breaks for that user. Assigning RoldID = 1 (Administators) it works for all the people that is assigned roldID 1.

Currently I have the methods

public override string[] GetRolesForUser(string username)
{

    using (EFDbContext db = new EFDbContext())  

    {
        User user = db.Users.FirstOrDefault(u => u.UserName.Equals(username, StringComparison.CurrentCultureIgnoreCase) || u.Email.Equals(username, StringComparison.CurrentCultureIgnoreCase));


        if (user != null)
        {
            var roles = from ur in user.UserRoles
                        from r in db.Roles
                        where user.role.RoleID == r.RoleID

                        select r.Name;
            if (roles != null)
                return roles.ToArray();
            else
                return new string[] { }; ;
        }
        else
        {
            return new string[] { }; ;
        }
    }
}


> public override bool IsUserInRole(string username, string roleName)
>         {
>             
>             using (EFDbContext db = new EFDbContext())
>             {
>                 User user = db.Users.FirstOrDefault(u => u.UserName.Equals(username, StringComparison.CurrentCultureIgnoreCase)
> || u.Email.Equals(username,
> StringComparison.CurrentCultureIgnoreCase));
> 
>                 if (user != null)
>                 {
>                     var roles = from ur in user.UserRoles
>                                 from r in db.Roles
>                                 where user.role.RoleID == r.RoleID
>                                 //where ur.RoleID == r.RoleID
>                                 select r.Name;
>                     if (user != null)
>                         return roles.Any(r => r.Equals(roleName, >StringComparison.CurrentCultureIgnoreCase));
>                     else
>                         return false;
>                 }
>                 else
>                 {
>                     return false;
>                 }
>             }
>         }

I know I have to change the IsUserInRole method so it handles a array parameter like so, public override bool IsUserInRole(string username, string[] roleName)

But don't know how to handle that in the linq and mainly in the IsUserInRole method:

return roles.Any(r => r.Equals(roleName, >StringComparison.CurrentCultureIgnoreCase));

Currently it is failing at the GetRolesForUser method, at the where clause i believe,

where user.role.RoleID == r.RoleID

the error message is displayed below:

There is already an open DataReader associated with this Command which must be closed first. Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: System.InvalidOperationException: There is already an open DataReader associated with this Command which must be closed first.

Source Error:

Line 66: var roles = from ur in user.UserRoles Line 67: from r in db.Roles Line 68: where user.role.RoleID == r.RoleID

Thanks

Steven
  • 91
  • 1
  • 9

1 Answers1

2

Multiple roles can be done as such: [CustomAuthorize(Roles = "Administrator, User")] But seeing you've written this code I'd think you'd know this already?

If all you want to do is keeps Users from getting in your create/edit/delete functions, why not use the default authorisation functions? meaning, put [CustomAuthorize(Roles = "Administrator")] above edit/create/delete and [CustomAuthorize(Roles = "Administrator, User")] above anything else...

I'm guessing the IsUserInRole gets called on authorisation? This method should take a string[] roleName array, and not just a string.

Edit: change

                        var roles = from ur in user.UserRoles
                        from r in db.Roles
                       where user.role.RoleID == r.RoleID
                        //where ur.RoleID == r.RoleID
                       select r.Name;

to

var roles = user.UserRoles.select(q=>q.Roles.Name);
Stefanvds
  • 5,868
  • 5
  • 48
  • 72
  • Thanks for prompt reply, I have tried [CustomAuthorize(Roles = "Administrator, User")] but the code doesn't accept this and it doesn't work. Is there something wrong with my RoleProvider Class that is only accepting 1 role? – Steven Nov 13 '12 at 10:18
  • Could it be something to do with: There is a UserRoles column in my entity which holds a description for the roleID _var roles = from ur in user.UserRoles from r in db.Roles where user.role.RoleID == r.RoleID //where ur.RoleID == r.RoleID select r.Name; if (roles != null) return roles.ToArray(); else return new string[] { };_ – Steven Nov 13 '12 at 10:34
  • The methods GetRolesForUser and IsUserInRole both get called, I am getting errors: There is already an open DataReader associated with this Command which must be closed first. Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code. Exception Details: System.InvalidOperationException: There is already an open DataReader associated with this Command which must be closed first. Source Error: where user.role.RoleID == r.RoleID – Steven Nov 13 '12 at 10:52
  • Only just seen your edit post, sorry. I have changed the parameter for the method to string[] roleName and it now errors on _r.Equals(roleName, StringComparison.CurrentCultureIgnoreCase))_ Error 1 Member 'object.Equals(object, object)' cannot be accessed with an instance reference; qualify it with a type name instead – Steven Nov 13 '12 at 11:15
  • obviously you'll need to edit that line of code for it to take an array... you can use `.contains()` – Stefanvds Nov 13 '12 at 11:41
  • Sorry I am a beginner If you couldn't tell already. I know i need to manage the array but how? _ return roles.Any(r => r.Contains(roleName, StringComparison.CurrentCultureIgnoreCase));_ Doesn't seem like I can still use the StringComparision.CurrentCultureIgnoreCase? – Steven Nov 13 '12 at 11:58
  • you can easily use `toLower()` and forget about the `CultureIgnoreCase`. you're not using any funky characters in your role names. – Stefanvds Nov 13 '12 at 20:04
  • `public override bool IsUserInRole(string username, string[] roleName) { using (EFDbContext db = new EFDbContext()) { User user = db.Users.FirstOrDefault(u => u.UserName.Equals(username, StringComparison.CurrentCultureIgnoreCase) || u.Email.Equals(username, StringComparison.CurrentCultureIgnoreCase)); if (user != null) { var roles = from ur in user.UserRoles.ToLower() from r in db.Roles where user.role.RoleID == r.RoleID select r.Name; if (user != null) return roles.Any(r => r.Contains(roleName, StringComparison.ToLower(roles))); else return false; }else {return false;` – Steven Nov 14 '12 at 09:20
  • Above is the IsUserInRole method which isn't working, I know I am going wrong somewhere stupid and it is obvious. But as I am a beginner and haven't touch this code for a while, It is not clear to me. Sorry – Steven Nov 14 '12 at 09:22
  • I think you should google on some default code. Debug the whole thing. see where things do work and where they dont. – Stefanvds Nov 14 '12 at 09:34
  • I have been struggling for a day so far using google and debugging but I really appreciate your help Stefanvds, Thank You. Apart from google or this forum, I have the pro asp.net mvc 3 framework book from Apress Adam Freeman and Steven Sanderson book, is there any other blogs or forums you recommend improving and understanding asp.net mvc? – Steven Nov 14 '12 at 09:44
  • asp.net/mvc is a good start! tell me exactly what your code is doing, preferably, edit your top-post. – Stefanvds Nov 14 '12 at 12:44
  • Thanks Stefanvds for being patient with me , please see my edited post. – Steven Nov 14 '12 at 15:49