13.14 Restrict Which Users Can Execute Your Code


Problem

You need to restrict which users can execute elements of your code based on the user 's name or the roles of which the user is a member.

Solution

Use the permission class System.Security.Permissions.PrincipalPermission and its attribute counterpart System.Security.Permissions.PrincipalPermissionAttribute to protect your program elements with RBS demands.

Discussion

The .NET Framework supports both imperative and declarative RBS demands. The class PrincipalPermission provides support for imperative security statements, and its attribute counterpart PrincipalPermissionAttribute provides support for declarative security statements. RBS demands use the same syntax as CAS demands, but RBS demands specify the name the current user must have, or more commonly the roles of which the user must be a member. An RBS demand instructs the runtime to look at the name and roles of the current user, and if they do not meet the requirements of the demand, the runtime throws a System.Security.SecurityException .

This code fragment shows the syntax of an imperative security demand.

 // Imperative role-based security demand syntax. public static void SomeMethod() {          PrincipalPermission perm =          new PrincipalPermission("UserName", "RoleName");              perm.Demand();      } 

You must first create a PrincipalPermission object specifying the user name and role name you want to demand, and then you must call its Demand method. You can specify only a single user and role name per demand. If either the user or the role name is null , any value will satisfy the demand. Unlike with code access permissions, an RBS demand doesn't result in a stack walk; the runtime evaluates only the user name and roles of the current user.

This code fragment shows the syntax of a declarative security demand.

 // Declarative role-based security demand syntax. [PrincipalPermission(SecurityAction.Demand, Name = "UserName",     Role = "RoleName")] public static void SomeMethod() { /*...*/} 

You can place declarative RBS demands at the class or member level. Class-level demands apply to all members of the class unless a member-specific demand overrides the class demand.

Generally, you are free to choose whether to implement imperative or declarative RBS demands. However, imperative security demands allow you to integrate RBS demands with code logic to achieve sophisticated RBS demand behavior. In addition, if you do not know the role or user names to demand at compile time, you must use imperative demands. Declarative RBS demands have the advantage that they are separate from code logic and easier to identify. In addition, you can view declarative RBS demands using the tool Permview.exe (discussed in recipe 13.6). Whether you implement imperative or declarative RBS demands, you must ensure that the runtime has access to the name and roles for the current user to evaluate the demand correctly.

The System.Threading.Thread class represents an operating system thread running managed code. The static property CurrentPrincipal of the Thread class contains an IPrincipal instance representing the user on whose behalf the managed thread is running. At the operating system level, each thread also has an associated Windows access token, which represents the Windows account on whose behalf the thread is running. It's important you understand that the IPrincipal instance and the Windows access token are two separate entities. Windows uses its access token to enforce operating system security, whereas the .NET runtime uses its IPrincipal instance to evaluate application level RBS demands. Although they may, and often do, represent the same user, this is by no means always the case.

By default, the Thread.CurrentPrincipal property is undefined . Because obtaining user- related information can be time consuming and only a minority of applications use this information, the .NET designers opted for lazy initialization of the CurrentPrincipal property. The first time code gets the Thread.CurrentPrincipal property, the runtime assigns an IPrincipal instance to the property using the following logic:

  1. If the application domain in which the current thread is executing has a default principal, the runtime assigns this principal to the Thread.CurrentPrincipal property.

    By default, application domains do not have default principals. You can set the default principal of an application domain by calling the method SetThreadPrincipal on a System.AppDomain object that represents the application domain you want to configure. Code must have the ControlPrincipal element of SecurityPermission to call SetThreadPrincipal . You can only set the default principal once for each application domain; a second call to SetThreadPrincipal results in the exception System.Security.Policy.PolicyException .

  2. If the application domain doesn't have a default principal, the application domain's principal policy determines which IPrincipal implementation to create and assign to Thread.CurrentPrincipal .

    To configure principal policy for an application domain, obtain an AppDomain object that represents the application domain and call the object's SetPrincipalPolicy method. The SetPrincipalPolicy method accepts a member of the enumeration System.Security.Principal.PrincipalPolicy , which specifies the type of IPrincipal object to assign to Thread.CurrentPrincipal . Code must have the ControlPrincipal element of SecurityPermission to call SetPrincipalPolicy . Table 13.4 lists the available PrincipalPolicy values; the default value is UnauthenticatedPrincipal .

  3. If your code has the ControlPrincipal element of SecurityPermission , you can instantiate your own IPrincipal object and assign it to the Thread.CurrentPrincipal property directly. This will stop the runtime assigning default IPrincipal objects or creating new ones based on principal policy.

    Table 13.4: Members of the PrincipalPolicy Enumeration

    Member Name

    Description

    NoPrincipal

    No IPrincipal object is created, Thread.CurrentPrincipal returns a null reference.

    UnauthenticatedPrincipal

    An empty System.Security.Principal.GenericPrincipal object is created and assigned to Thread.CurrentPrincipal.

    WindowsPrincipal

    A WindowsPrincipal object representing the currently logged-on Windows user is created and assigned to Thread.CurrentPrincipal .

Whatever method you use to establish the IPrincipal for the current thread, you must do so before you use RBS security demands, or the correct user ( IPrincipal ) information won't be available for the runtime to process the demand. Normally, when running on the Windows platform, you would set the principal policy of an application domain to PrincipalPolicy.WindowsPrincipal (as shown here) to obtain Windows user information.

 // Obtain a reference to the current application domain. AppDomain appDomain = System.AppDomain.CurrentDomain;          // Configure the current application domain to use Windows-based principals. appDomain.SetPrincipalPolicy(System.Security.Principal.PrincipalPolicy.WindowsPrincipal); 

The RoleBasedSecurityExample.cs file in the sample code for this chapter demonstrates the use of imperative and declarative RBS demands. The first excerpt included here shows three methods protected using imperative RBS demands. If the Thread.CurrentPrincipal object doesn't meet the demanded user name and role membership requirements, the demand will throw a SecurityException , which the method must handle or pass up for a method higher on the call stack to process.

 public static void ProtectedMethod1() {           // An imperative role-based security demand for the current principal     // to represent an identity with the name "Anya", the roles of the     // principal are irrelevant.     System.Security.Permissions.PrincipalPermission perm =          new System.Security.Permissions.PrincipalPermission         (@"MACHINE\Anya", null);              perm.Demand(); } public static void ProtectedMethod2() {      // An imperative role-based security demand for the current principal     // to be a member of the roles "Managers" OR "Developers". If the      // principal is a member of either role, access is granted. Using the      // PrincipalPermission you can only express an OR type relationship.     // This is because the PrincipalPolicy.Intersect method always      // returns an empty permission unless the two inputs are the same.     // However, you can use code logic to implement more complex      // conditions. In this case, the name of the identity is irrelevant.     System.Security.Permissions.PrincipalPermission perm1 =          new System.Security.Permissions.PrincipalPermission         (null, @"MACHINE\Managers");     System.Security.Permissions.PrincipalPermission perm2 =          new System.Security.Permissions.PrincipalPermission         (null, @"MACHINE\Developers");     perm1.Union(perm2).Demand(); } public static void ProtectedMethod3() {      // An imperative role-based security demand for the current principal     // to represent an identity with the name "Anya" AND be a member of the     // "Managers" role.      System.Security.Permissions.PrincipalPermission perm =          new System.Security.Permissions.PrincipalPermission         (@"MACHINE\Anya", @"MACHINE\Managers");              perm.Demand(); } 

The second excerpt shows three methods protected using declarative RBS demands, equivalent to the imperative demands just shown.

 // A declarative role-based security demand for the current principal // to represent an identity with the name "Anya", the roles of the // principal are irrelevant. [PrincipalPermission(SecurityAction.Demand, Name = @"MACHINE\Anya")] public static void ProtectedMethod1() { /*...*/} // A declarative role-based security demand for the current principal // to be a member of the roles "Managers" OR "Developers". If the  // principal is a member of either role, access is granted. You  // can only express an OR type relationship, not an AND relationship.  // The name of the identity is irrelevant. [PrincipalPermission(SecurityAction.Demand, Role = @"MACHINE\Managers")] [PrincipalPermission(SecurityAction.Demand, Role = @"MACHINE\Developers")]     public static void ProtectedMethod2() { /*...*/} // A declarative role-based security demand for the current principal // to represent an identity with the name "Anya" AND be a member of the // "Managers" role.  [PrincipalPermission(SecurityAction.Demand, Name = @"MACHINE\Anya",      Role = @"MACHINE\Managers")] public static void ProtectedMethod3() { /*...*/} 



C# Programmer[ap]s Cookbook
C# Programmer[ap]s Cookbook
ISBN: 735619301
EAN: N/A
Year: 2006
Pages: 266

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