Role-Based Security

 
Chapter 23 - .NET Security
bySimon Robinsonet al.
Wrox Press 2002
  

As we have seen, code access security gives the CLR the ability to make intelligent decisions behind the scenes as to whether code should run or not and with what permissions based on the evidence it presents . In addition to this, .NET provides role-based security that specifies whether code can perform actions on the basis of evidence about the user and their role, rather than just the code. You'll probably be glad to hear that it does this without walking the stack!

Role-based security is especially useful in situations where access to resources is an issue, a primary example being the finance industry, where employees ' roles define what information they can access and what actions they can perform.

Role-based security is also ideal for use in conjunction with Windows 2000 accounts, Microsoft Passport, or a custom user directory to manage access to web-based resources. For example, a web site could restrict access to its content until a user registers their details with the site, and then additionally provide access to special content only if the user is a paying subscriber. In many ways, ASP.NET makes role-based security easier because much of the code is based on the server.

For example, if we want to implement a web service that requires authentication, we could use Windows 2000's accounts subsystem and write the web method in such a way that it ensures the user is a member of a specific Windows 2000 user group before allowing access to the method's functionality.

The Principal

.NET gives the current thread easy access to the application user, which it refers to as a Principal . The principal is at the core of the role-based security that .NET provides, and through it, we can access the user's Identity , which will usually map to a user account of one of these types:

  • Windows account

  • Passport account

  • ASP.NET cookie-authenticated user

As an added bonus, the role-based security in .NET has been designed so that you can create your own principals by implementing the IPrincipal interface. If you are not relying on Windows authentication, Passport, or simple cookie authentication, you should look at creating your own using a custom principal class.

With access to the principal we can make security decisions based on the principal's identity and roles. A role is a collection of users who have the same security permissions, and is the unit of administration for users. For example, if we're using Windows authentication to authenticate our users, we will use the WindowsIdentity type as our choice of Identity . We can use that type to find out whether the user is a member of a specific Windows user account group, and we can then use that information to decide whether to grant or deny access to code and resources.

You'll generally find that it's much easier to manage security if you allow access to resources and functionality on the basis of roles rather than individual users. Imagine a scenario where you have three methods that each provides access to a feature over which you need tight control to ensure only authorized personnel can access it. If the application had, say, four users, we could quite easily specify within each method which users can and which users cannot access the method. However, imagine a time in the future where the number of features has extended to nine; to allow access to an additional user potentially requires changing every one of the nine methods even though this is an administrative task! Even worse , as users move between roles in the company we would need to change the code each time that happens too. If we had instead implemented the system using roles, we could then simply add users to and remove users from roles, rather than adding and removing individual users to and from the application. This simplifies the application, as for each method we simply request that the user be a member of a specific role. It also simplifies the management of roles, as the administrator can do it rather than the application developer. Put simply, the developer should be concerned with ensuring that, for example, managers but not secretaries can access a method; that Julie and Bob can, but not Conrad.

.NET's role-based security builds on that provided in MTS and COM+ 1.0, and provides a flexible framework that can be used to build fences around sections of the application that need to be protected. If COM+ 1.0 is installed on a machine, its role-based security will interoperate with .NET; however, COM is not required for .NET's role-based security to function.

Windows Principal

Let's create a console application that gives us access to our principal in an application, where we want access to the underlying Windows account. We'll need to reference the System.Security.Principal and System.Threading namespaces. First of all, we must specify that we want .NET to automatically hook up our principal with the underlying Windows account, as .NET does not automatically populate the thread's CurrentPrincipal property for security reasons. We do that like this:

   using System;     using System.Security.Principal;     using System.Security.Permissions;     using System.Threading;     namespace SecurityApp7     {     class Class1     {     static void Main(string[] args)     {     AppDomain.CurrentDomain.SetPrincipalPolicy(     PrincipalPolicy.WindowsPrincipal);   

It's possible to use WindowsIdentity.GetCurrent() to access the Windows account details; however, that method is best used when you're only going to look at the principal once. If you want to access the principal a number of times it is more efficient to set the policy so the current thread provides access to the principal for you. When we use the SetPrincipalPolicy method we are specifying that the principal in the current thread should hold a WindowsIdentity object for us. All identity classes like WindowsIdentity implement the IIdentity interface. The interface contains three properties ( AuthenticationType , IsAuthenticated , and Name ) for all derived identity classes to implement.

Let's add some code to access the principal's properties from the Thread object:

   WindowsPrincipal principal =     (WindowsPrincipal)Thread.CurrentPrincipal;     WindowsIdentity identity = (WindowsIdentity)principal.Identity;     Console.WriteLine("IdentityType:" + identity.ToString());     Console.WriteLine("Name:" + identity.Name);     Console.WriteLine("'Users'?:" + principal.IsInRole("BUILTIN\Users"));     Console.WriteLine("'Administrators'?:" +     principal.IsInRole(WindowsBuiltInRole.Administrator));     Console.WriteLine("Authenticated:" + identity.IsAuthenticated);     Console.WriteLine("AuthType:" + identity.AuthenticationType);     Console.WriteLine("Anonymous?:" + identity.IsAnonymous);     Console.WriteLine("Token:" + identity.Token);     }     }     }   

The output from this console application will look something like this depending on your machine configuration and the roles associated with the account under which you're signed in:

 IdentityType:System.Security.Principal.WindowsIdentity Name:MACHINE\alaric 'Users'?:True 'Administrators'?:True Authenticated:True AuthType:NTLM Anonymous?:False Token:256 

Clearly, it is enormously beneficial to be able to access details about the current user and their roles so easily, and using this information we can make decisions about what actions to permit and to deny. The ability to make use of roles and Windows user groups provides the added benefit that administration can be done using standard user administration tools, and we can usually avoid altering the code when user roles change. Let's look at roles in more detail.

Roles

Imagine a scenario where we have an intranet application relying on Windows accounts. The system has a group called Manager and one called Assistant; users are assigned to these groups dependent upon their role within the organization. Let's say our application contains a feature that displays information about employees that we only want those in the Managers group to access. We can easily use code that checks whether the current user is a member of the Managers group and permit or deny access based on this.

However, if we later decide to rearrange our account groups and introduce a group called Personnel that also has access to employee details, we have a problem. We have to go through all the code and update it to include rules for this new group.

A better solution would be to create a permission called something like ReadEmployeeDetails and assign it to groups where necessary. If our code applies a check for the ReadEmployeeDetails permission, to update the application to allow those in the Personnel group access to employee details is simply a matter of creating the group, placing the users in it, and assigning the ReadEmployeeDetails permission.

Declarative Role-Based Security

Just as with code access security, we can implement role-based security requests ("the user must be in the Administrators group") using imperative requests (as you saw in the preceding section), or using attributes. We can state permission requirements declaratively at the class level like this:

   using System;     using System.Security;     using System.Security.Principal;     using System.Security.Permissions;     namespace SecurityApp8     {     class Class1     {     static void Main(string[] args)     {     AppDomain.CurrentDomain.SetPrincipalPolicy(     PrincipalPolicy.WindowsPrincipal);     try     {     ShowMessage();     }     catch (SecurityException exception)     {     Console.WriteLine("Security exception caught (" +     exception.Message + ")");     Console.WriteLine("The current principal must be in the local"     + "Users group");     }     }     [PrincipalPermissionAttribute(SecurityAction.Demand,     Role = "BUILTIN\Users")]     static void ShowMessage()     {     Console.WriteLine("The current principal is logged in locally ");     Console.WriteLine("(they are a member of the local Users group)");     }     }     }   

The ShowMessage() method will throw an exception unless we execute the application in the context of a user in the Windows 2000 local Users group. For a web application, the account under which the ASP.NET code is running must be in the group, although in a real-world example you would certainly avoid adding this account to the administrators group!

If you run the code above using an account in the local Users group, the output will look like this:

 The current principal is logged in locally (they are a member of the local Users group) 

For more information on role-based security in .NET, your first stop should be the MSDN documentation for the System.Security.Principal namespace.

  


Professional C#. 2nd Edition
Performance Consulting: A Practical Guide for HR and Learning Professionals
ISBN: 1576754359
EAN: 2147483647
Year: 2002
Pages: 244

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net