Role Based Access Control in ASP.NET MVC

Role Based Access Control in ASP.NET MVC is pretty straight forward. There is also a way to do Claims access control, but the most common way is the authorization of a user based on the roles they have in an organization.

This blog post only explains RBAC using ASP.NET Model-View-Controller framework for web applications.

As a developer, to show or hide action links in a View, depending on the user role you can use the following Razor syntax:

@if (User.IsInRole("Administrator"))
{
...
}

On the Controller class, to avoid access to an action if the user types in the URL directly on the browser, we can annotate the action with the Role check tags.

For example, the following code would limit access to any actions on the AdministrationController to users who are  members of the Administrator group.

[Authorize(Roles = "Administrator")]
public class AdministrationController : Controller
{
}

You can specify multiple roles as a comma separated list;

[Authorize(Roles = "HRManager,Finance")]
public class SalaryController : Controller
{
}

The SalaryController  class above will be only accessible by users who are members of the HRManager role or theFinance role.

If you apply multiple attributes then, a user’s HTTP request, accessing the methods on the controller  must be a member of all the roles specified. The following sample requires that a user must be a member of both the PowerUser and ControlPanelUser role before authorization is granted.

[Authorize(Roles = "PowerUser")]
[Authorize(Roles = "ControlPanelUser")]
public class ControlPanelController : Controller
{
}

You can further limit access by applying additional role authorization attributes at the action level;

[Authorize(Roles = "Administrator, PowerUser")]
public class ControlPanelController : Controller
{
    public ActionResult SetTime()
    {
    }
 
    [Authorize(Roles = "Administrator")]
    public ActionResult ShutDown()
    {
    }
}

In the previous code snippet members of the Administrator role or the PowerUser role can access the controller and the SetTime action, but, only members of the Administrator role can access the ShutDown action.

You can also lock down a controller but allow anonymous, unauthenticated access to individual actions.

[Authorize]
public class ControlPanelController : Controller
{
    public ActionResult SetTime()
    {
    }
 
    [AllowAnonymous]
    public ActionResult Login()
    {
    }
}

There is also a way to use Policies for limiting access, but to keep it simple, since we already have the roles defined, we can use the common RBAC for now until we need something more complex.

When the user requests the URL directly they will get a nasty 401 Unauthorized page from IIS if their request is not Authorized.

The Razor code shown on the first code snippet can be used in the View to show the elements on the View, if the User is part of the Administrators role, but if the requestor (user) is not part of the role, he or she will receive a 401 HTTP Unauthorized response.

We can give them a more friendly page explaining they don’t have permissions to access the resources requested and link them to a request access page.

This is what could be done:

For a 401 you will probably be seeing the standard 401 Unauthorized page, even if you have added 401 to the customerrors section in your web.config. When using IIS and Windows integrated Authentication, the check happens before ASP.NET MVC even sees the request.

By editing the Global.asax file you can redirect to a route created for 401 Unauthorized HTTP response errors, sending the user to the “Unauthorized to see this” View (friendly page). The use case for this scenario would be if someone received a link for a View that requires the user to be authorized but  the user has not completed other steps in the process, such as paperwork needed prior to accessing the secure resource.

In the Global.asax:

void Application_EndRequest(object sender, System.EventArgs e)
{
    // If the user is not authorized to see this page or access this function, send them to the error page.
    if (Response.StatusCode == 401)
    {
        Response.ClearContent();
        Response.RedirectToRoute("ErrorHandler", (RouteTable.Routes["ErrorHandler"] as Route).Defaults);
    }
}

and in the Route.config:

     routes.MapRoute(
               "ErrorHandler",
               "Error/{action}/{errMsg}",
                new { controller = "Error", action = "Unauthorized", errMsg = UrlParameter.Optional }
     );

and in the ErrorController class:

public ViewResult Unauthorized()
{
        //Response.StatusCode = 401; 
        // Do not set this or else you get a redirect loop
        return View();
        //where View is the friendly .cshtml page
}

 

Voila!

Happy coding.

Localization of XAML, WPF applications.

As you may know, localization and globalization of user interfaces have been a topic I like to discuss. Being bilingual, I like to give the opportunity to applications to show their glory in several languages.

In my previous jobs, I have localized ASP.NET Web Forms and ASP.NET MVC forms, and also Windows Forms, but the other day someone asked me about WPF/XAML and MVVM. How to localize a XAML markup.

This is what this post is all about. Back in 2007, a coworker and I wrote a Localization Toolkit and published all the code Open Source at CodePlex, after writing an article about the new features in .NET 2.0 and Visual Studio 2005 that allowed you to generate resources files from the literal strings embedded on an ASP.NET web form.

Fast forward a few years to the end of 2007 and beginning of 2008, with the outcome of WPF, are resources files still in use?

Yup, they still are, and they are selected by the same mechanism that selects a given satellite assembly given the UI Thread Culture. The same mechanism that was available in the .NET world in 2005 for ASP.NET Web Forms.

Satellite assemblies are nothing but key/value pairs stored in a resource file that the ResourceManager type can search by Key.

Now, let’s say I generated resource file (.resx) for a given locale and region (fr-FR or fr-CA) and I want the resource file key bound to the XAML view after the UI Culture Thread is set to the OS locale and region, or after the user selects the locale and region he would like. Notice there are two settings to set with the locale and region: the UI Thread and the current thread. They are different.

            CultureInfo ci = new CultureInfo("fr-CA");
            Thread.CurrentThread.CurrentCulture = ci;
            Thread.CurrentThread.CurrentUICulture = ci;

 

How are the literal values bound to the proper resources file holding the locale and regional translation?

Within the Window tag of your XAML window file, add the following line of code:

xmlns:properties="clr-namespace:YourAssemblyName.Properties"

For each control within our grid (Button, Labels and Window Title) do not set the Title or Content values to static text. Instead use the resources file value.

<Label Content="{x:Static properties:Resources.LabelFirstName}"…/>
<Button Content="{x:Static properties:Resources.ButtonSubmit}"…/>

There are also other projects at GitHub that have been added recently specifically for WPF: WPFSharp.Globalizer which offers a better option than the LocBalm tool to generate and  maintain the resources files for translation.

LocBaml is the de-facto tool documented on MSDN for WPF resource generation, but there are other projects such as the one mentioned above and the NuGet package ResXManager that offers more features.

LocBalm will generate UiDs on your XAML markup that will link automatically to the regional resource after the Current UI Thread is set. LocBalm can also be used to generate the different resource files after the application is developed, using the option to parse the main assembly for XAML markup.

Parlez vous Français? Oui, mais mon interface ne parler pas.

Should I use sessionID to uniquely identify users?

Should I use sessionID to uniquely identify users?
NO, that’s why UserId/UserName or LoginID and password combinations are for.
SessionID is a “random” string and can be repeated (e.g. when IIS is restarted or the server is rebooted, the sequencing scheme that generates SessionID values is reset). So if you store information for a user based on the SessionID value, be very aware that a new person next week might happen to get the same SessionID value–this will either violate a primary key constraint, or mix two or more people’s data.

However in ASP.NET, the SessionID is 120 bits in length, so like a GUID, is virtually guaranteed to never repeat.

But in classic ASP, this built-in mechanism is not a good strategy for identifying users over the long term. A better methodology would be to generate a key value in a database that is guaranteed to be unique (e.g. IDENTITY or AUTOINCREMENT) and store that in a cookie on the client. Then you can identify them not only for the life of their current session, but during future visits as well (of course, only until the next time they delete their cookies).

As usual, it depends, on the technology you inherit, how the old modules were built and so on.

Storing ASP.NET SessionId and at the same time storing a SessionGUID generated from the SessionId in ASP.NET doesn’t make much sense.

L.

IIS 6.0 does not serve aspx pages out of the box

I have used ASP.NET for quite some time. I have probably always been lucky that the infrastructure or deployment person always enabled aspx on IIS for me. I use a Windows XP with IIS 5 sometimes or Vista with IIS 7. They do serve aspx pages by default, on XP once the .NET framework is downloaded and installed, there is nothing else to tweak.

On Friday afternoon, I had to deploy to an IIS 6 box on windows 2003, to my surprise, it didn’t serve any of the aspx pages. Even a small Hello World project on an simple label. I missed the happy hour with my colleagues and went home completely puzzle. Why? ASP.NET was already available when Windows 2003 saw the light. To my surprise there was nothing on the event log.

The answer came a few hours later and after few Google queries:

IIS 6.0: ASP.NET Is Not Automatically Installed on Windows Server 2003

1. Open IIS Manager, expand the master server node (that is, the Servername node), and then select the Web service extensions node.
2. In the right pane of IIS Manager, right-click the extension that you want to enable. In this example, this is Active Server Pages.
3. Click to select the Allow check box.
Add a New Web Service Extension to IIS 6.0

To permit IIS to serve content that requires a specific ISAPI or CGI extension that is not already listed in the Web service extensions list, follow these steps:
1.Open IIS Manager, expand the master server node, and then select the Web service extensions node.
2.In the right pane of the IIS Manager, click Add a new Web service extension under Tasks.
3.In the Extension name box, type a friendly name for the extension that you want to add (for example, FrontPage Server Extensions).
4.In the Required files box, click Add, and then select the path and the name of the file that will handle requests for the specific extension. After you select the path and the file name, click OK.
5. If the extension must be enabled immediately, click to select the Set extension status to allowed check box.
6. Click OK to save your changes.

Hopefully Mono will run seamless  on Apache one of these days…

To nvarchar or to varchar in SQL Server to accept French text

I found one single character from the French language that its binary representation is not the same for Unicode and Windows-1252. The oe ligature. Let me rant about it…

create table test_table
(name1 varchar (1) ,
name2 nvarchar (1) )

insert test_table
values (‘œ’, ‘œ’)

select * from test_table

select ASCII(name1), ASCII(name2), Unicode(name1), unicode(name2) from test_table

select COL_LENGTH(‘test_table’,’name1′) as Length1, COL_LENGTH(‘test_table’,’name2′) as Length2 from test_table

select char(156), char(339)

drop table test_table

This character is used in the word “eggs” in French
‘des œufs’ means some eggs…

These first two sets of characters from the Latin alphabet have identical Unicode and Windows-1252 (ASCII) character code.
(Basic Latin)
http://en.wikipedia.org/wiki/Basic_Latin_Unicode_block
and
Latin-1:
http://en.wikipedia.org/wiki/Latin-1_Supplement_Unicode_block

Their binary representation is the same as they all fit in one single byte or octet.

However, the extended Latin characters do not have the same Windows-1252 (ASCII) code and Unicode code.
http://en.wikipedia.org/wiki/Latin_Extended-A_Unicode_block

From this character set, French only uses the oe ligature though.

French accents and ligatures and how to type them with the number pad:
a with grave accent
à ALT + 133 À ALT + 0192

a with circumflex
â ALT + 131 Â ALT + 0194

a with tréma
ä ALT + 132 Ä ALT + 142

a e ligature
æ ALT + 145 Æ ALT + 146

c with cedilla
ç ALT + 135 Ç ALT + 128

e with acute accent
é ALT + 130 É ALT + 144

e with grave accent
è ALT + 138 È ALT + 0200

e with circumflex
ê ALT + 136 Ê ALT + 0202

e with tréma
ë ALT + 137 Ë ALT + 0203

i with circumflex
î ALT + 140 Î ALT + 0206

i with tréma
ï ALT + 139 Ï ALT + 0207

o with circumflex
ô ALT + 147 Ô ALT + 0212

o e ligature
œ ALT + 0156 Œ ALT + 0140

u with grave accent
ù ALT + 151 Ù ALT + 0217

u with circumflex
û ALT + 150 Û ALT + 0219

u with tréma
ü ALT + 129 Ü ALT + 154

French quotation marks
« ALT + 174 » ALT + 175

Euro symbol
€ ALT + 0128

The Windows-1252 encoding can be seen here:
http://www.visibone.com/htmlref/char/webascii.htm

For more, see Joel on Software rant :-p

As most questions in technology, the answer is: it depends.

http://stackoverflow.com/questions/35366/varchar-vs-nvarchar-performance

Performance wise, varchar is more efficient, less memory space, 20% to 30% smaller indexes.
Most database drivers will interpret the incoming stream and convert to Windows-1252 encoding, if the server code page is Windows 1252.
If you use way too many characters in the extended Latin group, you have no choice but using nvarchar…if you deal with other languages that are not Romance Languages, you have no choice but nvarchar.

Cheers!

Triple hop issue with ASP.NET delegation Part II: Fixing our remote users.

Where did I left the previous part?… ah yes, our Terminal Server users. Well the XP users were okay and happy but the remote users were not… what could possibly be different for both?

First, the OS, our remote users connect to windows servers (Windows 2000 Standard servers ) so we checked the kerberos.dll version and nope, everything was up to date…

We also checked the kerberos tickets for the logged user and there they were…We again checked our SPN on the domain (setspn), both the SQL Server service and our web service URL were registered in AD with the proper ports.

With the help of a tech support call we landed on this KB article:
Unable to negotiate Kerberos authentication after upgrading to Internet Explorer 6

Windows Server 2000 ships with Internet Explorer 5, it turns out that when IE5 is upgraded to IE6 the advanced option Enabled Integrated Security Option (requires restart) is checked off by default. This option is normally checked on on Windows Server 2003, Windows XP, Vista and 2008.

It turns out that this setting indicates to IE to use NTML as the authentication protocol when the option is unchecked.

This option is equivalent to the following registry key:

HKEY_CURRENT_USER\Software\Microsoft\Windows
\CurrentVersion\Internet Settings

and administrators can enable Integrated Windows Authentication by setting the EnableNegotiate DWORD value to 1

The name of the option is very misleading. You can find more information on this setting here, unfortunately I didn’t find much on the official Microsoft curriculum.

On our web server, we do not allow protocol transition which means the authenticated user should use Kerberos in order to enable the credentials to be delegated to a second hop:

The PROBLEM:

With the client set up to disable Kerberos authentication and with the IIS box set up to disable protocol transition, the credentials passed to the back end were not the end user credentials. Challenge protocols such as NTML do not allow delegation of credentials to a second hop.
A great article regarding the difference between Kerberos and the NTLM, WWW-Authentication such as Basic, Digest etc can be found on Larry Osterman “the Ping of Death”‘s amazing blog post.

The SOLUTION:

Enabling Protocol Transition on our web box at the domain controller would have done the trick:

Or setting up a Group Policy for our end users to have the IE setting checked up. See how to create a Group Policy here.

Cheers!

Triple hop issue with ASP.NET delegation Part I: Our Windows XP Pro desktops

Last Friday we had an issue in production: we have a very simple web application with one single page on our intranet that consumes an array of web services. These web services talk to a back end SQL Server.

All in all this is a very typical scenario and like most companies with .NET technology we have web applications using ASP.Delegation in the intranet, the only particular point regarding this web page is that it is called inside an old legacy windows application (not a .NET app). For remote users, this old legacy application is used via Terminal Services.

For our remote users also, the application didn’t work and our DBA was registering a bunch of anonymous requests coming from the web server box…

On the other hand we set up our web services tracing to debug and were able to see the end user credentials on each HTTP request, so the end user had managed to authenticate using Integrated Windows Security on our web box and the web service trying to open a SQL connection to the back end.

We used impersonation and Integrated Windows Authentication on our web application and web services (this is an intranet after all). ASP.NET impersonation gave us the chance to restrict the access on the back end based on AD groups and at the same time gave us the ability to audit the user’s actions to a very fine grained degree (user name).

The PROBLEM with our Windows XP Pro desktop users

The application worked for our desktop users if and only if they had logged off and on their desktops in the past 48 hours. If the desktops users hadn’t logged on for a while, like me, that I lock my computer instead of logging myself off, the application didn’t work either and the sql box passed an anonymous login attempt back to our web tier. The web services then passed a SOAP Exception with the NT Service/Anounymous user error message to our web app…

System.Web.Services.Protocols.SoapException: Server was unable to process request. —> System.Data.SqlClient.SqlException: Login failed for user ‘NT AUTHORITY\ANONYMOUS LOGON’.

At first we thought it was the same problem, but it turns out the TS users couldn’t use the application even when they logged of and back on, not even when the TS server was restarted, hrm….

By dividing and conquering we applied the kerbtray.exe tool on our web server and one of the desktops and enabled Kerberos logging on both boxes. We noticed that when the application worked the user logged in the web server box used Kerberos, but after a few days the logging defaulted to NTML.

SOLUTION for the Windows XP Pro Desktops

It turns out this was a bug in the kerberos.dll running on Windows XP SP2, SP3 has this problem solved. More information can be found on this MSDN thread. Also the hotfix for Windows XP Professional SP2 can be found on this Microsoft Knowledge Base article. Although this article describes a different problem the hotfix provided here contains the fixed kerberos dll.

There are quite a bit of articles regarding ASP.NET delegation

And quite a few MSDN forum threads, like this one I initiated and has a heated discussion with the moderator, my fault most of it.

The best resources I have found so far, and I hope this digested summary will help you if you have the same double/triple hop issue, are:

Ken Schaefer’s blog post regarding IIS and Kerberos Part 5 – Protocol Transition, Constrained Delegation, S4U2S and S4U2P.

Keith Brown’s article on MSDN: Credentials and Delegation
and
nunos’s Blog: Concerning the credentials double hop issue

and the best of all is a webcast by Yung Chou *all kudos to his explanation of Protocol Transition*

MSDN Webcast: Getting Delegation to Work with IIS and ASP.NET:
The Ins and Outs of Protocol Transition (Part 1 of 2) (Level 300)

This webcast specifically helped us troubleshooting and fixing the second part of our problem, our failed connection when the end users connected remotely via terminal servers.

I’ll post more of the problem and the resolution on Part II…

…stay tuned.

Forms authentication and client caching in ASP.NET 1.1

We got really sad news today, the type of news that makes you have sustained stomachache for a few days.
I’m not going to blog about the way my stomach feels but to remind me that I like what I do and this is not just a job, it’s a pleasure.

The Problem:

On one of our web projects that uses Forms authentication.
After the authentication process we create an encrypted ticket,
create the cookie that will be used by the FormsAuthentication
provider and redirect to the requested page:

Dim authTix As New
FormsAuthenticationTicket(1, UserName, DateTime.Now,
DateTime.Now.AddMinutes(60), isCookiePersistent, UserData)

Dim encryptedTix As String =
FormsAuthentication.Encrypt(authTix)

Dim authCookie As New
HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket)


authCookie.Expires = authTicket.Expiration

Context.Response.Cookies.Add(authCookie)
FormsAuthentication.RedirectFromLoginPage(UserName,
False)

During user log out we clear the session, call the
FormsAuthentication.SignOut() and redirect the user to the login page.

We had, however, an odd behavior. After the user has logged out of the
application, he could, by clicking the back button on the same browser
windows, navigate to the previous pages he opened. These pages were in
the secure area. These pages were not requested to the server, these
requests did not hit the server so I presumed the user was seing cached
pages in the browser.

The Solution:
To use the directive
Context.Response.Cache.SetNoStore()
on all the secure pages that shouldn’t be cached.
For more info on the framework class HttpCacheability go to MSDN

For more info on Cache-Control Headers on HTTP 1.1 go here

The Side Effect Problem:

It seems IE does not store the file in its temporary Internet files folder whenever the server specifies the “no-store” http cache directive ; as a consequence, it cannot feed Acrobat/Excel or whatever external application with the output of your page. if the application has excel or pdf reports on the fly, they will generate an error if the http directive is sent in the response.

http://support.microsoft.com/kb/243717/en-us

The Second Solution:
To remove this cache header whenever you need to send to the client a file to be opened in the browser.