While I was dealing with some potential vulnerability issue affecting to some of "my" MVC 5.0 application in .Net Framework 4.5 and coming from a wrong management of user roles, it turns out that I didn't remember clearly how role management was related to User
principal or even, where roles came from in order to IsInRole
method were able to work properly to check roles for users. So, let me add this post as a reminder to clarify and shed light on this.
What is user property of Current HTTP Context?
System.Web.HttpContext.User
property gets or sets security information for the current HTTP request context.
Before going ahead with User
property, let me reminder you some important information about the HttpContext
class. It encapsulates all the information for a given HTTP request and is created by the ASP.Net run-time whose code is mainly stored in the system.web
assembly. Here are some great articles explaining the ASP.Net application life cycle and the ASP.Net page life cycle. See the picture below for a quick reference:
The value for the User
property in HttpContext
must be an instance class implementing System.Security.Principal.IPrincipal
interface:
Properties
Identity
-> Gets the identity of the current principal.
Methods
IsInRole(String)
-> Determines whether the current principal belongs to the specified role passed as input param.
Where do roles come from?
To begin with, "Role Manager" feature was added in ASP.Net 2.0 and provides basic functionality to create IPrincipal
-based objects linked to roles. Its main goal is to make easier for developers to provide authorization based on roles.
The "Role Manager" feature has a static class Roles
that can be used as an easy way to access all the functionality provided by this feature. Basically, it provides public properties and methods that expose data from configuration (<roleManager />
in web.config
).
So, getting straight to the point, when "Role Manager" is enabled (enabled
attribute set to true
in <roleManager />
node), early during the request lifecycle the RoleManagerModule
HttpModule
creates and assigns a RolePrincipal
instance to the User
property of the CurrentHttpContext
. This task occurs during the PostAuthenticateRequest
event.
The RolePrincipal
class implements the IPrincipal
interface and besides, provides additional properties and methods. For instance, not only it implements the IsInRole
method for determining if a given user belongs to a specific role but a new method for returning the complete list of roles for a user as well (GetRoles
method). It could be very useful in some cases.
Therefore, in this situation ("Role Manager" enabled), roles and related information are obtained by means of the RolePrincipal
class.
To enable "Role Manager", the web.config
file must be configured accordingly as explained in detail in Roles
class documentation. Basically, all you have to do is using the <roleManager />
node under <system.web />
node in web.config
and set the attribute enabled
to true
. There are other properties you can use to set up further features but it is not the goal of this post. Take a deeper look to the references and official documentation if needed.
How do RolePrincipal class deal with roles?
As said before, the RolePrincipal
class exposes the security identity for the current HTTP request and additionally performs checks for role membership. In addition to this, if CacheRolesInCookie
property is set to true
, then the RolePrincipal
object manages the cached list of roles within a cookie and looks up role membership for the current user in the cached list first, and if not present, calls the proper methods in the linked RoleProvider
class. If CacheRolesInCookie
is false
, the RolePrincipal
object always looks up role membership using the RoleProvider
.
Therefore, RolePrincipal
gets roles primarily from RoleProvider
and optionally stores and retrieve those roles from a cookie due to performance considerations. For instance, both the IsInRole
and GetRoles
methods from RolePrincipal
rely on the RoleProvider
associated to the RolePrincipal
to carry out the work of determining belonging to roles or return the list of roles for a user.
How to configure "Role Manager" to use a given RoleProvider?
In the web.config
file you can add providers as displayed in the code sample below:
<roleManager defaultProvider="CustomRoleProvider"
enabled="true"
cacheRolesInCookie="true"
cookieName=".ASPXROLES"
cookieTimeout="30"
cookiePath="/"
cookieRequireSSL="false"
cookieSlidingExpiration="true"
cookieProtection="All" >
<providers>
<clear />
<add
name="CustomRoleProvider"
type="YourNameSpace.CustomRoleProvider" />
</providers>
</roleManager>
Notice the <providers />
node and the <add />
section within it. You can add a predefined provider or even create your own custom provider as done above. This provider will be the responsible for dealing with roles.
How to implement a custom RoleProvider?
Eventually, if you decide to implement your own provider you have different options. You can implement it from the scratch (see here for more information) or you can do it easier and simpler by inheriting from an existing RoleProvider
and then overriding the methods you need to:
public class CustomRoleProvider : System.Web.Security.RoleProvider
{
public override string[] GetRolesForUser(string username)
{
// To do
}
public override bool IsUserInRole(string username, string roleName)
{
// To do
}
// To do
}
The details of the implementation are not the most important and can be omitted here. You can retrieve the roles from a database or your custom store by using the methods from your custom role provider. The details of the implementation are up to you and depends on your requirements.
To summarize, whenever the IsInRole
method of the User
principal object is called for the current HTTP request, the associated RolePrincipal
object will return the boolean
result based on the comparison between the input param and the list of roles returned from the RoleProvider
for the authenticated user.
More References
Professional ASP.NET 3.5 Security, Membership, and Role Management with C# and VB