Two Approaches to User-Based Security


Two Approaches to User -Based Security

There are two major approaches to working with user-based security in .NET. The first way is known as the imperative approach, which involves explicit decision making in code. The second way is known as the declarative approach, which involves the use of attributes.

There are actually two slightly different styles that can be used in the imperative approach. The old-style imperative approach is basically the same as that used in conventional Win32 security programming, where you determine who the user is and explicitly choose the execution path using an if statement. Typically, the decision is made between two execution branches, where one is successful and the other throws a SecurityException . Although this technique is quite familiar to many programmers, the additional code required makes it slightly cumbersome.

In the new-style imperative approach, you create a PrincipalPermission object representing the user or role that you wish to discriminate on, and then you call on that PrincipalPermission object's Demand method to test it against the current user. The Demand method automatically makes the security decision and throws the SecurityException for you if there is no match. The advantage of doing it this way is that the code is a little more simple and clean looking, since there is no visible if statement and exception-throwing code.

Alternatively, you can implement user-based security using the declarative approach. In general, the difference between imperative and declarative programming is that in the imperative case, you write explicit code that makes things happen. In declarative programming, you passively define attribute data that will, at runtime, have an effect on the behavior of the program. In the case of declarative user-based security programming, the desired behavior is produced by applying the PrincipalPermission attribute to the desired method. Let's now look at two examples that demonstrate these imperative and declarative approaches to user-based security programming.

Imperative User-Based Security

As you are probably aware, Windows defines several built-in users, such as Administrator and Guest, and groups, such as Administrators, Users, and Guests. The administrator typically defines many others as well. The ImperativeUserBasedSecurity code example demonstrates how simple it is to implement security decisions that are based on the identity or role membership of these users and role. Near the top of the ImperativeUserBasedSecurity.cs source file, you will find the following using statements that enable the use of short names for the required security- and thread- related classes that are used in the program.

 using System.Security; using System.Security.Principal; using System.Threading; 

This simple example program has two Button controls. The Test On User Name button demonstrates how to make a security decision based on a username that you provide in a TextBox control. The Test On Role button works in a similar manner except that it makes its security decision based on the role that you have selected with the Guest, User, and Administrator RadioButton controls. Let's first look at the buttonTestOnUserName_Click method.

 private void  buttonTestOnUserName_Click  (    object sender, System.EventArgs e) {    //set default principal for appdomain threads    AppDomain appdomain = AppDomain.CurrentDomain;    appdomain.  SetPrincipalPolicy  (  PrincipalPolicy.WindowsPrincipal  );    //get current principal object  WindowsPrincipal principle =   (WindowsPrincipal)Thread.CurrentPrincipal;  //get specified user name string    string userName = textUserName.Text;    //execute code according to specified user name  if (!principle.Identity.Name.Equals(userName))  {  throw new SecurityException  (          "Specified user is " +          userName +          ".\n" +          "Therefore current user " +          principle.Identity.Name +          " is NOT permitted to proceed.");    }    MessageBox.Show(       "Specified user is " +       userName +       ".\n" +       "Therefore current user " +       principle.Identity.Name +       " is permitted to proceed."); } 

SetPrincipalPolicy Throws SecurityException

This example program calls the SetPrincipalPolicy method, which throws a SecurityException if the code does not have the permission to manipulate the AppDomain object's security policy. To keep things simple in this example, we do not concern ourselves with the possibility of this exception being thrown; we simply call SetPrincipalPolicy without any precaution. In your own programs, you should probably check for this permission before attempting to call SetPrincipalPolicy . The following code snippet checks for this by calling the Demand method on a specially constructed SecurityPermission object. The SecurityPermission class controls "metapermissions" that govern the CLR security subsystem. We talk more about permission objects and the Demand method shortly.

 SecurityPermission sp = new SecurityPermission(    SecurityPermissionFlag.ControlPrincipal); try {    sp.Demand(); } catch(SecurityException se) {    ...//cannot call SetPrincipalPolicy } //can call SetPrincipalPolicy 

The first thing this example code does is call SetPrincipalPolicy , which sets the type of principal that is to be associated with the current application domain to be a WindowsPrincipal type of principal object. This is necessary because only a WindowsPrincipal object carries the required information about the current user that was authenticated by the Windows logon facility. [15] Without this information, we would not be certain about whom we are dealing with. Next, the current principal object is obtained by calling the Thread.CurrentPrincipal static method.

[15] If we did not call SetPrincipalPolicy to specify that we want a WindowsPrincipal , we would get a GenericPrincipal by default, which would not contain the user information that we are interested in. Since the user was authenticated by the Windows logon facility, the user's name and role are provided by a WindowsPrincipal but not by a GenericPrincipal .

 //set default principal for appdomain threads AppDomain appdomain = AppDomain.CurrentDomain; appdomain.  SetPrincipalPolicy  (  PrincipalPolicy.WindowsPrincipal  ); //get current principal object WindowsPrincipal principle =  (WindowsPrincipal)Thread.CurrentPrincipal;  

Next, the program gets the specified username from the user interface. Finally, this string is compared against the Identity object's Name property to see if the specified username matches the actual user's name. If it matches, then the program permits the desired action. Otherwise, the action is avoided by throwing an exception. This program is only for demonstration purposes, so the action is just simulated by displaying an appropriate message box.

 //get specified user name string string userName = textUserName.Text; //execute code according to specified user name  if (!principal.Identity.Name.Equals(userName))  ... //throw exception 

The buttonTestOnUserName_Click method we have just looked at makes its security decision on a specified username. Users may belong to one or more groups, which can greatly simplify matters, allowing us to make decisions on the generalized characteristics of a set of users rather than on individuals. Let's now look at the buttonTestOnRole_Click method, which makes its security decision on a specified group .

 private void  buttonTestOnRole_Click  (    object sender, System.EventArgs e) {    //set default principal for appdomain threads    AppDomain appdomain = AppDomain.CurrentDomain;    appdomain.SetPrincipalPolicy(       PrincipalPolicy.WindowsPrincipal);    //get current principal object    WindowsPrincipal principal =        (WindowsPrincipal)Thread.CurrentPrincipal;    //get specified role  WindowsBuiltInRole role  = 0;    if (radioButtonGuest.Checked == true)       role = WindowsBuiltInRole.Guest;    if (radioButtonUser.Checked == true)       role = WindowsBuiltInRole.User;    if (radioButtonAdministrator.Checked == true)       role = WindowsBuiltInRole.Administrator;    //execute code according to specified role  if (!principal.IsInRole(role))  {  throw new SecurityException  (          "Specified role is " +          role +          ".\n" +          "Therefore current user " +          principle.Identity.Name +          " is NOT permitted to proceed.");    }    MessageBox.Show(       "Specified role is " +       role +       ".\n" +       "Therefore current user " +       principle.Identity.Name +       " is permitted to proceed."); } 

This method starts off the same way, establishing the type of principle object that we need to work with by calling SetPrincipalPolicy . Again, we do this to specify that we want a WindowsPrincipal type of principal object. Then, we make a security decision, but this time it is based on the role of the user rather than the name of the user. The specified role is obtained from the user interface, and then the IsInRole method is used to choose between performing the desired action and throwing an exception.

 //execute code according to specified role  if (!principle.IsInRole(role))  ... // throw exception 

Figures 7-9 and 7-10 show the two possible results for this example program. These results assume that the currently logged on user is named Administrator, who is in the roles of Administrators and Users, but not in the role of Guests. You should substitute the appropriate machine and domain name accordingly when you do this on your own system.

Figure 7-9. Valid username Administrator is permitted.

graphics/07fig09.gif

Figure 7-10. Invalid username SantaClaus is not permitted.

graphics/07fig10.jpg

USING PRINCIPALPERMISSION IN IMPERATIVE USER-BASED SECURITY

The ImperativeUserBasedSecurity example that we just looked at is slightly cumbersome in that it uses an if statement and it explicitly throws an exception. This program could be slightly simplified by using the PrincipalPermission class instead. The test of the specified user against the actual current user can be replaced with code that uses the PrincipalPermission class. For example, notice how the following code taken from the PrincipalPermission example creates a PrincipalPermission object with the desired username and/or role, and then calls on the Demand method. Just like the previous ImperativeUserBasedSecurityexample , this PrincipalPermission example is also considered to be an imperative rather than a declarative approach to user-based security.

 PrincipalPermission pp =    new  PrincipalPermission  (       strUserName, strUserRole); //can throw SecurityException if wrong user pp.  Demand  (); //if we got this far, then user is OK MessageBox.Show(    "Specified user matches current user." +    "User  permitted  to proceed."); 

The two string parameters to the PrincipalPermission constructor, which represent the username and role that you want to test against, are of course established beforehand. If either of these parameters is null, it is ignored for security comparison purposes. Then, in place of the if statement used in the previous example, you simply call the Demand method, which performs the comparison and automatically throws a SecurityException if the specified user does not match properly.

PrincipalPermission Not Derived from CodeAccessPermission

Like other permissions, PrincipalPermission does implement the IPermission interface, but, unlike other permissions, it does not derive from CodeAccessPermission . This is because PrincipalPermission is not based on the identity of the executing assembly (i.e., not used for CAS). Instead, it is based on the identity of the current user. We will look at permissions in more detail in Chapter 8.

Declarative User-Based Security

The DeclarativeUserBasedSecurity example shows how to accomplish user-based security in a declarative manner. As you can see, the code is very simple here, because there is no code that explicitly tests against the identity of the current user. By applying the PrincipalPermission attribute to the method as a whole, the method can simply go directly about its business without any security concerns whatsoever. If the current user is not consistent with that described by the PrincipalPermission attribute, a SecurityException is automatically thrown.

  [PrincipalPermission(   SecurityAction.Demand,   Name="HPDESKTOP\Administrator")]  private void  buttonTest_Click  (    object sender, System.EventArgs e) {    MessageBox.Show(       "Specified user is permitted to proceed."); } 

The square brackets in the preceding code declare a PrincipalPermission attribute for the method buttonTest_Click . This attribute specifies the Demand action, and the HPDESKTOP\\Administrator username. For more information on this attribute, please see the documentation on the PrincipalPermissionAttribute class, which encapsulates this attribute.

If you run this program, depending on whether or not you are logged on as HPDESKTOP\\Administrator, you will get one of the results shown in Figure 7-11 and Figure 7-12.

Figure 7-11. PrincipalPermission: Valid user is permitted.

graphics/07fig11.gif

Figure 7-12. PrincipalPermission: Invalid user is not permitted.

graphics/07fig12.gif

Note that it is too much trouble to log out and back in again to test this program with different usernames. Instead, you can go to a command prompt and use the runas command, as shown in the following command lines to accomplish this. When you do this, you will be prompted for the password associated with the username that you provide.

 C:\...>runas /user:HPDESKTOP\Administrator   DeclarativeUserBasedSecurity.exe C:\...>runas /user:HPDESKTOP\CodeMeister   DeclarativeUserBasedSecurity.exe 

When using declarative security, we still need to establish WindowsPrincipal as the principal for the application domain, just as we did in the case of imperative security. However, because the PrincipalPermission attribute is being applied to the method as a whole, it is too late to call SetPrincipalPolicy within that actual method. For this reason, the DeclarativeUserBasedSecurity example establishes this beforehand with the following code, found in the Main method. Note that this time, we do not actually have to obtain the current principal object from the Thread class. That will be done automatically from the effect of using the PrincipalPermission attribute on the buttonTest_Click method.

 static void Main() {    //set default principal for appdomain threads    AppDomain appdomain = AppDomain.CurrentDomain;  appdomain.SetPrincipalPolicy(   PrincipalPolicy.WindowsPrincipal);  Application.Run(new DeclarativeUserBasedSecurityForm()); } 


.NET Security and Cryptography
.NET Security and Cryptography
ISBN: 013100851X
EAN: 2147483647
Year: 2003
Pages: 126

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