Reputation: 49049
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 membership 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.
Why does User.IsInRole()
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 [email protected]
I can also verify that from above #3 NEVER calls the IsUserInRole code. This includes right after a clear browser cache.
A quick web search 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.
How can I get the above 2 or 3 options working?
Using Visual Studio 2013,and creating a standard asp.net web site with vb.net (frame work 4.5)
As a follow up ALL OF MY ROLE functions works.
eg: Roles.GetAllRoles() - this works (returns all roles correct)
Roles.GetRolesForUser() - this works, returns 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 original 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(). This does NOT work.
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 = [email protected]
User.Identity.IsAuthenticated = True
User.IsInRole("Portal") = False < -- still broken!!!
Upvotes: 0
Views: 2685
Reputation: 352
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")
Upvotes: 0
Reputation: 49049
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.
Upvotes: 1
Reputation: 124706
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.
Upvotes: 4