0

I have a simple role provider I created. (so forms authentication).

The standard built in asp.net logon controls when placed on a page work fine.

I am trying to determine why the standard 'tests' for role memebership seen to fail.

I have this so far:

Roles.Provider.IsUserInRole(Membership.GetUser.UserName, "Portal") returns true

Roles.IsUserInRole(Membership.GetUser.UserName, "Portal") returns false

Why does the second format fail? (note that the below IsInRole code is called by BOTH of the above!!).

User.IsInRole("Portal") returns false.

So, out of the 3 examples, only one works?

Anyone have a suggestion as to why User.IsInRole() does not work?

Note that User.IsInRole() for some reason does NOT call the following routine, but the above TWO codes do.

Public Overrides Function IsUserInRole(username As String, roleName As String) As Boolean

    Dim rst As DataTable
    Dim strSql As String
    MsgBox("Is user in Role test")
    strSql = "SELECT Email, RoleName FROM  dbo_ContactName " & _
             "LEFT JOIN Web_UsersInRoles ON Web_usersInRoles.User_ID = dbo_ContactName.Id " & _
             "LEFT JOIN Web_Roles on Web_Roles.ID = Web_UsersInRoles.Role_ID " & _
             "WHERE Email = '" & username & "' and RoleName = '" & roleName & "'"

    rst = Myrst(strSql)

    If rst.Rows.Count > 0 Then
        Return True
    Else
        Return False
    End If

End Function

Note that MOST confusing is this:

Roles.IsUserInRole(Membership.GetUser.UserName, "Portal") returns false

I have verified that the above causes my custom code to run but ALWAYS returns false.

In fact if I HARD CODE the above routine to return "true", then the above #2 STILL calls my code and STILL returns false!!!

So big question: Even with hard coding the above routine to always return true why does the this format fail?

Roles.IsUserInRole(Membership.GetUser.UserName, "Portal")

And worse, why does this fail?

User.IsInRole("Portal") again always returns false.

So what settings should I look for to enable 2 and 3 to work?

In summary:

1 - Roles.Provider.IsUserInRole(Membership.GetUser.UserName, "Portal") returns true

2 - Roles.IsUserInRole(Membership.GetUser.UserName, "Portal") returns false

3 - User.IsInRole("Portal") returns false

4 - User.Identity.Name returns the correct logged in user kallal@msn.com

I can also verify that from above #3 NEVER calls the IsUserInRole code. This includes right after a clear browser cache.

A quick BinGoolge shows large number of people find that User.IsInRole() stops working when adopting a custom role provider. There must some be "significant" issue here that being missed when writing custom role providers that causes this to fail.

Links or suggestions on how to get above 2 or 3 options working much appreciated.

Using Visual Studio 2013,and creating a standard asp.net web site with vb.net (frame work 4.5)

Edit: As a follow up ALL OF MY ROLE functions works.

eg: Roles.GetAllRoles() - this works (returns all roles correct)

Roles.GetRolesForUser() - this works, retruns  all roles for CURRENT user

Roles.GetUserInRole("Portal") - this works, returns all users in role group "portal"

NOT working:

User.IsInRole("Portal") - as noted 1000's of posts on internet have this issue!

Roles.IsUserInRole(Membership.GetUser.UserName, "Portal")

Here are the additional subs that I had not included in origal post:

Public Overrides Function GetUsersInRole(roleName As String) As String()

    Dim rst As DataTable
    Dim i As Integer
    Dim a As New List(Of String)
    Dim strSql As String
    MsgBox("get users in role code")
    strSql = "SELECT Email, RoleName FROM  dbo_ContactName " & _
             "LEFT JOIN Web_UsersInRoles ON Web_usersInRoles.User_ID = dbo_ContactName.Id " & _
             "LEFT JOIN Web_Roles on Web_Roles.ID = Web_UsersInRoles.Role_ID " & _
             "WHERE RoleName = '" & roleName & "' " & _
             "ORDER BY Web_Roles.RoleName"
    rst = Myrst(strSql)

    For i = 0 To rst.Rows.Count - 1
        a.Add(rst.Rows(i).ItemArray(0).ToString)
    Next

    Return a.ToArray

End Function

Public Overrides Function GetRolesForUser(username As String) As String()

    Dim rst As DataTable
    Dim i As Integer
    Dim a As New List(Of String)
    Dim strSql As String

    strSql = "SELECT Email, RoleName FROM  dbo_ContactName " & _
             "LEFT JOIN Web_UsersInRoles ON Web_usersInRoles.User_ID = dbo_ContactName.Id " & _
             "LEFT JOIN Web_Roles on Web_Roles.ID = Web_UsersInRoles.Role_ID " & _
             "WHERE Email = '" & username & "' " & _
             "ORDER BY Web_Roles.RoleName"

    rst = Myrst(strSql)

    For i = 0 To rst.Rows.Count - 1
        a.Add(rst.Rows(i).ItemArray(1).ToString)
    Next

    Return a.ToArray

End Function

Public Overrides Function GetAllRoles() As String()

    Dim rst As DataTable
    Dim i As Integer
    Dim a As New List(Of String)

    rst = Myrst("select RoleName from Web_Roles order by RoleName")

    For i = 0 To rst.Rows.Count - 1
        a.Add(rst.Rows(i).ItemArray(0).ToString)
    Next

    Return a.ToArray

End Function

ALL OF THE ABOVE features work.

Question still stands:

How do we wire up to ENABLE use of User.IsInRole(). Thiis does NOT work.

So in summary:

Roles.GetRolesForUser    - returns "current" roles for user - works fine

Roles.GetUsersInRole("Portal")  - works fine

Roles.GetAllRoles() - works fine.

User.IsInRole() - BROKEN!

More summary: here is some code with the resulting output:

    Debug.WriteLine(Roles.Provider.IsUserInRole(Membership.GetUser.UserName, "Portal"))

    Debug.WriteLine(Roles.IsUserInRole(Membership.GetUser.UserName, "Portal"))

    Debug.WriteLine(User.Identity.Name)

    Debug.WriteLine(User.Identity.IsAuthenticated)

    Debug.WriteLine(User.IsInRole("Portal"))

Output:

Roles.Provider.IsUserInRole  = True
Roles.IsUserInRole = False
User.Identity.Name = kallal@msn.com
User.Identity.IsAuthenticated = True
User.IsInRole("Portal")  = False   < -- still broken!!!
Albert D. Kallal
  • 42,205
  • 3
  • 34
  • 51

3 Answers3

4

You need to implement GetRolesForUser in your custom provider.

When you call Roles.IsUserInRole in an ASP.NET application for the current user, it will call System.Web.Security.RolePrincipal.IsUserInRole.

RolePrincipal.IsUserInRole will then call the Role Provider's GetRolesForUser method on first access, and cache the list of roles internally. It never actually calls the provider's IsUserInRole method.

Confusingly, it works differently when you use ASP.NET roles in a WCF application (principalPermissionMode="UseAspNetRoles"). In this case, instead of using the System.Web.Security.RolePrincipal class, it uses an internal class System.ServiceModel.Security.RoleProviderPrincipal.

RoleProviderPrincipal.IsUserInRole will call the Role Provider's IsUserInRole method, and doesn't attempt to cache the list of roles for the current user.

The two different implementations have different performance characteristics.

  • The ASP.NET method (GetRolesForUser then cache roles internally) means that there is only one call to the provider even if IsUserInRole is called multiple times during processing a request. However for some providers (e.g. WindowsTokenRoleProvider), getting the list of all roles can be expensive, resulting in poor performance.

  • The WCF method (call the provider's IsUserInRole method every time) can result in multiple calls to the provider during the processing of a single request. But the complete list of roles for the current user is never retrieved unless explicitly requested, which can give better performance for providers such as WindowsTokenRoleProvider where this retrieval is expensive.

Community
  • 1
  • 1
Joe
  • 122,218
  • 32
  • 205
  • 338
  • I have all of the override stubs in place. I have added the additional stubs to the post. They WORK fine. If you take 1000 examples on the internet and setup a standard Role provider then the User.IsInRole fails for all the examples I been able to test. So 100, or 10,000 people trying this will have the SAME faiure. The simple issue is HOW to wire this up so that User.IsInRole() will work. – Albert D. Kallal Jan 15 '14 at 18:31
  • 1
    Where are you calling User.IsInRole? It won't work until after the user has been authenticated, and you have redirected from the login page. You should also check that you have "roleManager enabled='true' in your web.config. – Joe Jan 15 '14 at 19:01
  • Yes, I am on a new page. Yes of course role manager is set to enabled since all other Role manager functions work. I JUST edited the post and at the very end I have a series of debug.writeline to show the output. The one that fails is User.IsInRole(). As I said I find 1000's of posts having this same issue – there is SOME big detail being left out here and I just not sure what that "lost" detail is, but this is COMMON issue and problem – one that the community just has not yet figured out a common answer for. – Albert D. Kallal Jan 15 '14 at 19:27
  • As a follow up, here is a example role provider - again it works JUST like my code and AGAIN the IsInRole() FAILS exactly like my code. http://www.codeproject.com/Articles/27955/Developing-custom-ASP-NET-Membership-and-Role-prov.html Perhaps I should be asking if anyone can find ANY sample code in which IsInRole() works. – Albert D. Kallal Jan 16 '14 at 22:37
  • @AlbertD.Kallal - my advice would be to debug and examine HttpContext.Current.User. It should be of type RolePrincipal and have the expected username. If not, perhaps there is some code setting it to something else (e.g. in global.asax). Or perhaps you're calling it when you're not in the context of a request (HttpContext.Current is null). If this doesn't help, create a minimal sample project that exhibits the problem behavior. I'd be happy to look at it if you can make it available somewhere. In any case, I have frequently used a custom RoleProvider without problems. – Joe Jan 17 '14 at 10:38
  • Thanks kindly for the follow up. The "problem" as noted in my follow up post was the roles had some trailing blanks. Thus any and all code examples and setups failed. I mean this should have been simple and should have worked. My original SIMPLE code I wrote was fine. It was only have EXHAUSTIVE testing and as a SUPER LONG MOON SHOT did I decide to look for blanks and started placing trim() around bits of code. To my SPECTACULAR surprise the code started to work. No matter if I used my code or other code – it simply could not and would not work until the stray tailing blank was discovered. – Albert D. Kallal Jan 18 '14 at 19:49
  • @AlbertD.Kallal - I see the different now: your IsInRole method lets the database do the comparison, whereas with GetRolesForUser the comparison is done in code. So if the database ignores trailing spaces when comparing (or, for example, ignores case), you can get different results. Glad you've solved it anyway. – Joe Jan 18 '14 at 22:15
  • Thanks kindly. A colossal waste of time of time caused by silly trailing blanks. Only after EXTENSIVE and EXHAUSTING testing was this issue discovered. No rhyme or reason for my code or samples I tested from the web when changed to use my Roles would fail. I really don't care about the egg on my face but the spectacular amounts of time wasted on this will leave a bitter taste in my mouth for years to come. The ONLY consolation is my original code, original designs and original implementation was 100% fine. Why Users.IsInRole() was failing was thus resolved at GREAT cost and suffering. – Albert D. Kallal Jan 19 '14 at 23:17
1

The solution to this elusive problem is outlined here:

HttpContext.Current.User.IsInRole not working

The answer and follow up by the poster is contained in the comments section.

The suggested solution is to place a trim() on the Role names. When done the CODE STARTED working!

Role name had a trailing space, and as a long shot I placed a trim on the code that loads roles for a user and sure enough the code started working. Now that the original data providing the Role has been trimmed, then the in code in question now works.

I cannot take credit for this solution but the link to this issue and problem is contained in the above.

So this code (and other code tested) was failing due to a trailing blanks on some of the Role names.

MOST interesting is that

Roles.Provider.IsUserInRole(Membership.GetUser.UserName, "Portal")

Above works! with trailing spaces

Roles.IsUserInRole(Membership.GetUser.UserName, "Portal")

Above does NOT work with trailing spaces

User.IsInRole("Portal")

Above does NOT work with trailing spaces.

So the HUGE hint here is that Roles.Provider.IsUserInRole(Membership.GetUser.UserName, "Portal") does work! If the others fail, then REST assured then you have an issue with stray spaces in your role names.

With the stray space removed, then ALL of the above examples in ALL cases NOW all work.

Community
  • 1
  • 1
Albert D. Kallal
  • 42,205
  • 3
  • 34
  • 51
0

Thanks Joe -> "User.IsInRole" won't work until after the user has been authenticated

Solved the display-what-when issue for me! In _Layout.cshtml

 @if (HttpContext.Current.User.Identity.IsAuthenticated)
   {
    if (HttpContext.Current.User.IsInRole("Manager"))
      { 
       @Html.Partial("_ManageNav")
      }
    if (HttpContext.Current.User.IsInRole("Administrator"))
      { 
       @Html.Partial("_ManageNav")
      }
   }

Only displays "_ManageNav" if the user is assigned a Manager or Administrator role

Did not work for combined string ("Manager, Administrator")

Did not work for combined string ("Manager", "Administrator")

Terri
  • 354
  • 6
  • 18