Beruflich Dokumente
Kultur Dokumente
PART 1
ARTICLE
FRAMEWORK INTRODUCTION
PROJECT:
AUTHOR:
REPORT DATE:
Contents
Introduction ...................................................................................................................................................................... 3
Background ....................................................................................................................................................................... 3
Using default ASP.NET Roles and Membership API .......................................................................................................... 3
Custom Controller/Action Authorization .......................................................................................................................... 4
Authorization ................................................................................................................................................................ 4
Roles Based Access Control (RBAC) .............................................................................................................................. 4
Preparing the Database .................................................................................................................................................... 5
Retrieving User Application Permissions from our RBAC Tables .................................................................................. 5
User Roles/Permissions Class Mapping ............................................................................................................................ 6
A Basic Overview of MVC Controller Action Methods ...................................................................................................... 7
Roles and MVC Controller/Action Associations ................................................................................................................ 8
Action Filters ................................................................................................................................................................... 10
Custom Action Filters .................................................................................................................................................. 10
Restricting Access to MVC Controller Action Methods using Action Filters ............................................................... 12
Performing Conditional Processing using RBAC during Controller Action Execution ................................................. 13
Using RBAC in a Controllers Action Method .............................................................................................................. 15
Using RBAC in a Controllers View .............................................................................................................................. 16
Using RBAC to Dynamically Control Menus ................................................................................................................ 17
Persisting RBAC Data via ADO.NET Entity Framework (EF)............................................................................................. 18
Creating an Entity Framework (EF) RBAC Model ............................................................................................................ 19
Generated Entity Framework (EF) RBAC Model ......................................................................................................... 20
Extracting Data from our Entity Framework (EF) RBAC Model ................................................................................... 21
Reading User Roles/Permissions from our Database ................................................................................................. 22
Code Listing for Entity Framework (EF) RBAC Data Retrieval ................................................................................. 22
Extending the RBAC Framework ..................................................................................................................................... 23
Sample Project ................................................................................................................................................................ 24
Application Permissions .............................................................................................................................................. 24
Application Roles ........................................................................................................................................................ 25
Application Users ........................................................................................................................................................ 26
Adding RBAC to Existing MVC Applications ................................................................................................................ 27
jQuery User Interface .................................................................................................................................................. 29
Removing jQuery User Interface References .............................................................................................................. 30
RBAC Authentication/Authorisation Overview ............................................................................................................... 31
Conclusion ....................................................................................................................................................................... 32
Page 2
Introduction
In this post, I shall cover implementing custom Roles Based Access Control (RBAC) and subsequent roles
maintenance in the context of an intranet based ASP.NET MVC web application using Windows Authentication.
ASP.NET Roles and Membership provides almost all features required to perform authentication and authorization but
adding a new role and assigning it to a particular user seems to have been lost. This solution forms a self-contained
framework independent of default out of the box providers. The framework allows us to focus on which features/areas
in our application are restricted to the user, including menus, and what information to make visible/invisible to the user
without concerning ourselves with the underlying technicalities. The framework offers RBAC functionality inside the
controller action and controller view at a granular level whilst using minimum code syntax and the framework can be
extended to incorporate custom RBAC methods. It is especially suited for corporate intranet applications where there
is restricted access to the hosting web server once your web application has been deployed or the administration of
user roles including role assignment cannot be directly undertaken by the applications system administrator or owner.
Background
Developing intranet applications within an organization based on Windows Authentication has been around since the
dawn of the intranet. In most organizations, its typical that intranet based applications not only require to permit
access to a subset of users defined in the organizations Active Directory server, but to also define roles which are
assigned to the applications users thus restricting access to certain features/areas within the application. Again,
Roles Based Access Control isnt a new concept and there are numerous examples posted that exemplify this concept
in one form or another. ASP.NET MVC aligns itself well for RBAC and the examples posted on the web in their
various guises either over engineer the concept or are too simplistic averting extendibility. Its for this reason that I
wrote this article.
Without recompiling and re-deploying my application, how do I create new custom roles and bind them
dynamically to controller methods once my application has been deployed?
How do I dynamically associate users with multiple roles where the highest application permission takes
access precedence?
How do I dynamically control menus or controller view rendering based on the requesting users role(s) and
associated application permissions?
When developing corporate solutions, we generally find ourselves in situations where the applications users role data
needs to be stored in the applications own database. If a database server fails due to hardware failure, restoring an
earlier backed-up copy of the applications database will contain all the role data thus aligning well for database
replication for the purpose of a hot standby database server.
Page 3
Authorization
Role based applications are where users in the system are assigned specific roles. In our system, each role
determines which areas of the application the role can access via application permissions. Application permissions
define MVC controller names and controller action names represented as a string concatenation of the two properties
in the format controller-action (eg admin-index). Application permissions are unique which can be traced back to
their controller-action references. It's easy to get confused with the difference between user authentication and user
authorization. In summary, authentication is verifying that users are who they say they are, using some form of login
mechanism (username/password, Windows Authentication, and so on something that says this is who I am).
Authorization is verifying that they can perform tasks as part of their job role with respect to your site. This is usually
achieved using some type of role-based system.
Page 4
USER
Is Assigned
ROLE
Is Assigned
PERMISSION
Our Entity-Relationship diagram implies that an application user can be assigned zero or many application roles. An
application role can be assigned zero or many application permissions. Application permission represents controller
action methods.
Subsequently, we derive the following database tables from our Entity-Relationship diagram.
USERS
User_Id
Username
LNK_USER_ROLE
User_Id
Role_Id
ROLES
LNK_ROLE_PERMISSION
Role_Id
RoleName
IsSysAdmin
Role_Id
Permission_Id
PERMISSIONS
Permission_Id
PermissionName
We would clearly use more table properties in our real-world application allowing for flexible customization but the
illustrated tables provide the minimum properties required to form the basis of an RBAC framework. Integrating our
custom authentication/authorization mechanism into existing MVC applications should be relatively straight forward
since no additional databases or Identity Management providers (IdMs) are needed. It would be highly unlikely that
any MVC application wishing to introduce RBAC would be operating without a backend database in the first place
therefore we could simply add the above tables to the existing database. However, we do have the option to separate
our RBAC tables away from the main application database since our RBAC tables are independent and based on a
loose coupling design. Any MVC application operating without a backend database will generally not need RBAC,
examples being unit/currency conversion websites.
Page 5
The RBACUser class encapsulates custom user authentication/authorization functionality and will be executed in an
action filter which supports pre-action behaviour to controller action methods. Action Filters are explained in the
following section.
Page 6
http://localhost/Admin
The Admin verb in the URL signifies the controller name due to its position in the URL path. In this example, the
AdminController class is invoked; MVCs controller class naming convention is to append the keyword Controller
to the controllers name. An MVC controller class is responsible for processing and responding to browser requests.
Every controller class should expose controller action methods that get invoked via URL references or other paths.
The following URL specifies both a controller name and controller action method.
http://localhost/Admin/Create
MVC will attempt to invoke the Create controller action method in the AdminController class. Every controller
action returns an action result in response to a browser request even if the referenced controller or controller action
doesnt exist. Before executing an invoked controller action method, pre-processing can be instructed by using an
Action Filter where logic can be placed to determine if the action method should be executed or directed to another
part of the system instead. An Action Filter is an ideal candidate for checking a users authorization for the invoked
functionality.
Page 7
Page 8
Now that we have defined an application role and assigned one application permission to the new role, we need to
create an application user who is assigned the Administrator role. To keep things simple, we shall assume that our
application is running as an intranet based system using Integrated Windows authentication via IIS where the user
requesting the resource has already be authenticated. We simply use the IPrincipal object to identify the
requesting users name to which we map their unique username to our USERS table.
It goes without saying that only users permitted to access the intranet site would be added to the USERS table unless
you are adopting a user registration mechanism where users would be assigned standard user role. This also
works perfectly well for web sites not based on Integrated Windows authentication where authentication is achieved
and tracked using authentication tokens. In either case, the user is identified via the IPrincipal object.
Lets assume that the user to which we shall assign the application administration role has the Windows username
swloch and that the IPrincipal.Identity.Name property for this user evaluates to somedomain\swloch.
We need to add the user to the USERS table using Windows username (without the prepended domain name) as the
table Username field and assign the Administrator role to the user.
--Create the user 'swloch'
INSERT INTO USERS(Username) VALUES('swloch')
--Associate the 'Administrator' Role with user
INSERT INTO LNK_USER_ROLE VALUES(
(SELECT User_Id FROM USERS WHERE Username = 'swloch'),
(SELECT Role_Id FROM ROLES WHERE RoleName = 'Administrator'))
The following tables display the content view of the USERS and LNK_USER_ROLE
tables respectively in relation to the above SQL INSERT commands.
.
Before a controllers action is executed, the requesting users role is determined to check whether the role contains
the requested controller/action combination. If the role does contain the controller/action association then execution of
the controller action is permitted resulting in the
action result page from that controller/action being
returned. If the role does not contain the required
http://localhost/admin/create
controller/action association, an invalid authorisation
page is returned instead of the action result page.
User Request
(Invoke
authorization filter)
Authorisation Check
Does users role(s) contains
required controller-action
association?
RBAC
RBAC Database
Database
YES
NO
Action Result
Unauthorized Page
Response
Admin Page
Page 9
Action Filters
In MVC, controllers define action methods that usually have a one-to-one relationship with possible user interactions,
such as clicking a link or submitting a form. Occasionally, you want to perform logic either before an action method is
called or after an action method runs. To support this, MVC provides action filters. Action filters are custom attributes
that provide a declarative means to add pre-action and post-action behaviour to controller action methods.
ASP.NET MVC provides the following types of action filters:
Authorization filter, which makes security decisions about whether to execute an action method, such as
performing authentication or validating properties of the request. The AuthorizeAttribute class is one
example of an authorization filter.
Action filter, which wraps the action method execution. This filter can perform additional processing, such as
providing extra data to the action method, inspecting the return value, or cancelling execution of the action
method.
Result filter, which wraps execution of the ActionResult object. This filter can perform additional processing
of the result, such as modifying the HTTP response. The OutputCacheAttribute class is one example of a
result filter.
Exception filter, which executes if there is an unhandled exception thrown somewhere in action method,
starting with the authorization filters and ending with the execution of the result. Exception filters can be used
for tasks such as logging or displaying an error page. The HandleErrorAttribute class is one example of
an exception filter.
The RBACUser object exposes the HasPermission method that accepts a permission parameter returning a bool
value denoting the existence of that permission in any of the users assigned roles. If you derive a class from the
AuthorizeAttribute class, the derived class must be thread safe. Therefore, do not store state information in an
instance field in an instance of the class unless that state information is meant to apply to all requests. Instead, store
state information per request in the Items property, which is accessible through the context objects passed to
AuthorizeAttribute.
Page 10
In the event where the users role(s) do not contain the required application permission, a customized 401
Unauthorized access error is returned instead of the intended controllers action result view. The customized error
simply returns a view, as detailed below, via the Index controller action defined in the Unauthorized controller
invoked by the RedirectToRouteResult.
The error text is defined in the Index.cshtml file corresponding to the Unauthorized controller and you should modify
this file to change the pages aesthetics.
UnauthorizedController.cs
public class UnauthorisedController : Controller
{
// GET: Unauthorised
public ActionResult Index()
{
Session.Abandon();
return View();
}
}
Page 11
A good approach to security is to always place the security check as close as possible to the resource you are
securing. You may have additional checks higher up the stack, but ultimately, you need to secure the actual resource.
This way, no matter how the user gets to the resource, there will always be a security check in place. In this case, you
dont want to rely on routing and URL authorization to secure a controller; you really need to secure the controller
itself.
Our custom RBACAttribute authorization attribute serves this purpose.
If you dont specify any roles or users, the current user must simply be authenticated in order to call the action
method. This is an easy way to block unauthenticated users from a particular controller action.
If a user attempts to access an action method with this attribute applied and fails the authorization check, the
filter causes the server to return a 401 Unauthorized HTTP status code.
Page 12
Page 13
We can now call our exposed functionality in any controller action and/or corresponding view through the controllers
context object as illustrated below.
Controller Action (EmployeeController.cs)
RBACUser functionality exposed via our RBAC_ExtendedMethods class can be used in controller actions.
RBACUser functionality exposed via our RBAC_ExtendedMethods class can be used in views.
Page 14
Without having to alter our EmployeeController class, we now have our RBACUser functionality exposed through the
controllers context object using extension methods. This is also true for the controllers view.
Page 15
@{
if (ViewContext.Controller.HasRole("HumanResourcesManager"))
{
<p>
Use this area to provide additional information and/or display
additional data provided in the model/viewbag by the controller's action
as the user has the "HumanResourcesManager" role assigned.
</p>
}
if (ViewContext.Controller.HasPermission("ViewRestrictedHRData"))
{
<p>
Use this area to provide additional information and/or display
additional data provided in the model/viewbag by the controller's action
as the user has the "ViewRestrictedHRData" permission assigned.
</p>
}
}
<p>Use this area to provide standard information.</p>
Our RBAC_ExtendedMethods class provides us with a flexible framework which enables us to extend our custom
RBAC functionality with minimal effort. Our extended RBAC methods are automatically exposed to every controller
action and corresponding view in our application through the controllers context object without having to change the
controller classes in anyway (except for the use of our newly exposed functionality).
Page 16
If we need to dynamically display menu items based on the requesting users role permissions, we can simply refer to
the menus target controller name and controller action as the permission in the format controller-action.
For example, if we require the Import Data menu to be visible only to users allowed access to the underlying
controllers action, we would simply wrap our custom HasPermission method around the menu item definition
(generally found in the _Layout.cshtml view) as illustrated below passing the menus target controller name and
controller action as the permission to be checked.
<body>
<div class="page">
...
<div id="menucontainer">
<ul>
@{
if (ViewContext.Controller.IsSysAdmin())
{
<li>
<a href="#" class="arrow">System Administration</a>
...
</li>
}
}
@{
if (ViewContext.Controller.HasPermission("data-import"))
{
<li>@Html.ActionLink("Import Data", "Import", "Data")</li>
}
}
<li>@Html.ActionLink("About", "About", "Home")</li>
<li>@Html.ActionLink("Contact", "Contact", "Home")</li>
<li>@Html.ActionLink("Home", "Index", "Home")</li>
</ul>
</div>
</div>
...
</body>
The custom HasPermission method will check the requesting users role(s) for the permission data-import and
display accordingly. Additionally, we can also make use of the IsSysAdmin method to check whether the requesting
user has a role that has the IsSysAdmin property enabled. Therefore, a user that doesnt have a System
Administrator role nor a role defining the data-import permission will see the following menu items displayed (based
on the illustrated code snippet) as opposed to the menu items displayed above.
However, if a user enters a URL directly (eg http:///Data/Import) and does not have the required permission, a
customized 401 Unauthorized access error is returned instead of the intended controllers action result view
providing we have decorated the controllers action or controller class with our RBACAttribute. Associating the
data-import permission to the users role will automatically display the corresponding menu.
Page 17
Page 18
Page 19
The generated model has identified and defined three entities (User, Role and Permission) from our RBAC database
and defined the RBAC_Model context model which links the entity relationships together. The OnModelCreating
method on the RBAC_Model context model class is called during the database context model creation where model
entity relationships are created which correspond to the underlying database tables including table index keys and
referential integrity constraints.
NOTE: If you already have an Entity Data Model (EDM) in your existing application, simply add the generated entity
classes (Users, Roles and Permissions) to your project and define the corresponding DbSet for these entities in your
model including the entity relationships defined in the OnModelCreating method. So long as the RBAC tables are
added to your existing database, there will be no need to add a database connection string to the applications
Web.Config since your application will already have a connection string defined for your existing EDM.
The first time a DbContext is created, is pretty expensive but once the object has been created much of the
information is cached so that subsequent instantiations are significantly quicker. You are more likely to see
performance problems from keeping a context object around than you are from instantiating one each time you need
access to your database. If you keep a context object around it will keep track of all the updates, additions, deletes
etc and this will slow your application down and may even cause subtle bugs to appear in your application.
The context object should be created per request. Create the context object, do what you need to do with the object
and then get rid of it. Do not try and have a global context (this is not how web applications work).
Page 21
Page 22
In order to expose our new functionality to the applications controller actions and controller views, we must wrap our
new functionality in new methods in our RBAC_ExtendedMethods class (ie our extension methods).
public static class RBAC_ExtendedMethods
{
public static bool IsDoctor(this ControllerBase controller)
{
bool IsDoctor = false;
try
{
//Check if the requesting user has the specified role...
IsDoctor = new RBACUser(controller.ControllerContext.HttpContext.User.Identity.Name).IsDoctor();
}
catch { }
return IsDoctor;
}
...
...
}
We have now extended our RBAC framework with our customized method IsDoctor(). We can now use the new
method in our controller action via this.IsDoctor() syntax or in our controller view via
ViewContext.Controller.IsDoctor() syntax.
Note: Code snippets used in the above examples are minimal highlighting additional code only in order to focus on
the topic at hand.
Page 23
Sample Project
The sample project available for download implements the AdminController which provides the necessary RBAC
administration. Simply adding this controller, accompanying views contained in the Admin folder and RBAC model to
any existing MVC project will provide the necessary RBAC administration functionality. Integrating the RBAC
administration functionality into an existing ASP.NET MVC project is discussed in the next section.
The RBAC administration is exposed via the System Administration menu as displayed below and will only be
visible to a user having a role that has the IsSysAdmin option enabled; the
menu item definition must be contained within the IsSysAdmin function to
display dynamically as discussed in the Using RBAC to Dynamically Control
Menus section. The menu style is driven by CSS and can be easily modified to
follow any application theme.
Before we create any application roles, we need to create permissions
associated with our application. Depending on which areas of your application
you need to restrict using role based access, there may be a large number of
controller action methods which translate to application permissions. Entering
each controller action as a permission into your application can be a dull and time consuming task. To aid in the
creation of your application permissions, the Permissions screen contains a button labeled Import Permissions.
Application Permissions
The import permissions function uses the .NET Frameworks Reflection API which enables the fetching of assembly
type information at runtime. The function iterates through the assemblys MVC controller methods and saves each
controller-action to the permissions database table.
Page 24
Application Roles
Once the applications permissions have been defined, we are in a position to create user roles. User roles are
typically associated with one or more application permissions. Your application business rules should define which
roles in your application should access which areas. Clicking on the Roles menu will display your applications roles
(where defined) and enable CRUD actions on the roles to be undertaken.
Once an application role has been created, application permissions can then be assigned to the role. Permissions
can be associated and disassociated with a role at any time. Associating permissions with roles can be a time
consuming task. To aid in the role-permission association process, the Add All Permissions button associates all
permissions with the role in a single action; unwanted permissions can then be disassociated using the trash icon.
Alternatively, individual permissions can be selected from the dropdown and added via the Add Permission button.
Page 25
NOTE: Permanently deleting an application permission via the Permissions screen will automatically remove the
permission from associated roles.
Application Users
Once the applications roles have been defined, Users can be created and assigned roles.
Roles can be associated and disassociated with a user at any time. Individual roles are assigned to a user by
selecting the role from the dropdown and pressing the Add Role button; unwanted roles can be unassigned using
the trash icon.
NOTE: Permanently deleting an application role via the Roles screen will automatically remove the role from
associated users.
Page 26
changing to reference the newly named controller. These links are contained in both the controller itself and
the corresponding views.
7. Using Solution Explorer in Visual Studio, create a new folder in your applications Views folder called
Unauthorised and add the view file Index.cs by right-clicking on the newly created folder and selecting
Add >> Existing Item.
8. Include the System Administration menu in your _Layout.cshtml file using your existing menu CSS
styling. If you prefer the illustrated menu styling, copy the necessary CSS from the sample Site.css file to
your projects Site.css file. This also applies to the views; incorporate your own styling.
9. The Scripts folder references the jquery-ui-1.10.3.custom.min.js file but you can freely download your own
custom themed version from http://jqueryui.com and reference accordingly.
http://jqueryui.com
If you do download your own custom version of jQuery UI, using Solution Explorer in Visual Studio, add the file
jquery-ui-1.10.x.custom.min.js to the Scripts folder otherwise the jQuery dialog will not render correctly.
Page 28
Page 29
Removing the applied theme will prevent dialog boxes (used for deleting grid items) from rendering correctly.
Although the dialog box will display and still operate, it will be displayed incorrectly. The following screenshot
illustrates an incorrectly displayed dialog box due to a theme being incorrectly configured or missing.
However, you may wish to remove the jQuery User Interface completely so that the user is not prompted before
deleting the item or you can provide an alternative interface for the dialog box.
The trash icon displayed in the grid will now call the controller action method directly passing the required parameters.
However, no dialog warning will be displayed to the user thus preventing the user from cancelling the delete operation.
Alternatively, you could invoke a JavaScript function, utilizing the JavaScript alert keyword, via the onclick event
which will display a basic dialog warning not reliant on the jQuery User Interface. It really comes down to how
sophisticated you wish to make your User Interface.
There are pros and cons to each of these methods, and depending on your product, market, and niche, one may be
more suitable for you than the others.
Page 30
IIS
ASP.NET MVC
RBAC DATABASE
ENTITY
FRAMEWORK
http://localhost/admin/create
PERMISSIONS
Permission_Id
Is User
Authenticated?
User Request
(Invoke
authorization filter)
User IS
Authenticated
LNK_ROLE_PERMISSION
Authorisation Check
Does users role(s) contains
required controller-action
association?
Display User
Authentication
dialog box
Role_Id
Permission_Id
User Data
YES
Action Result
RBACUser
Object
NO
Database
Database
Create User Page
Unauthorized Page
Response
ROLES
Role_Id
User Data
Admin
Create User
Filename
Transfer Date
HTTP RESPONSE
Create User
Page Response
LNK_USER_ROLE
Username
Unauthorized Request
User_Id
Role_Id
Unauthorized
Page Response
User_Id
IsSysAdmin
Username
An inbound request to our web application is initially handled by IIS which authenticates the user against the active
directory group via an Authentication Login dialog box if not authenticated. If the user is authenticated, the request is
forwarded to the MVC web application which checks the users rights and roles. A users right will decide whether the
requested controller/action can be processed. A user is stored as an object in the Entity Framework layer which
populates the users data from the database tables.
Page 31
Conclusion
This solution forms an ideal framework for any intranet application that requires dynamic self-contained Roles Based
Access Control (RBAC) that is specific to the application and independent of ASP.NET Roles and Membership and
Identity Management providers (IdM) such as Microsoft Identity Integration Server (MIIS). The framework can be
added to existing projects as well as new developments and once deployed will be self-maintaining and regulating via
the applications system administrator with little or no reliance on the application developer.
This solution is particularly suited for corporate intranet applications where limited access to the deployed web server
is granted or the administration of user roles including role assignment is delegated away from the application system
administrator or owner.
In the next post, the framework will be extended to incorporate role based reporting.
Page 32