|
Role-Based Security in Windows Apps
|
|
What's in a role?? Role is a term that is used to mean a lot of things in the world of software applications. The definition that we will stick to will be: A role is a context that a user will play in an application. For example, if you are writing an accounts application, you will probably allow the application to be used by clerks, managers, department heads and so on. Each of these constitute a role and there will be users who are members of the role. When users of a particular role access the system, the system responds by providing them with a specific set of permissions and the user is restricted to only those permissions. For example, if a user logs in as a clerk, he/she may be allowed to do only ledger entry, but not see the balance sheet of the company. Roles provide a convenient way of grouping users together
and managing them in a single unit. Permissions can be granted or denied to the
role and the users in the role get affected. Roles have different
meaning and similar equivalents in various applications. Some of the similar
concepts that I know are:
Roles are also called responsibilities in some systems, but the concept is the same. For example, the Oracle Applications Framework has the concept of responsibilities that can be used to provide access to various parts of the system. OK, having seen what roles are, how do we implement them in our system?? For example, can we define our own roles and then have permissions assigned to them?? Can our application intelligently modify itself based on who is accessing the system?? If all this is possible, how can I implement this in .NET?? These are the questions that this article will answer. In this article we are going to see the various .NET objects that help us to implement role-based security and we will see an example of how to implement one such system. We will also see some of the important methods and usages of the .NET objects. All the examples are based on Windows (smart-client) applications. Role-based security in .NET can be implemented if you understand the following objects and what they are used for:
All the above objects have a permissions object called PrincipalPermission. This object represents an particular identity and a particular membership (or role) that a principal object must have so that your code can execute its checks. OK, those are some of the basics that you need to be aware of and its quite simple!! One question that you might now be having is, how do I implement all these concepts in my code?? There are two ways by which you can implement these concepts:
In this article, we will see how to use the imperative mode. OK, let's now see some implementation. First, we will see how to use the Windows* objects for implementing role-based security. After this, we will see how to use the Generic* objects. Assume that you have the following interface for your application. ![]() Let us assume that the application provides two functions.
One for computing payroll and the other for viewing the
salaries of employees. Obviously viewing salaries requires more
permissions than computing payroll. Since we are going to initially see
how to use Windows permissions, we will define the following condition.
Let us now see the code to implement these conditions. Since we want the buttons to be (en)disabled as soon as the application loads, the best place for us to write this code is in the form load event. Here is the source code (note that the designer generated code has been removed).
The code is quite simple. We import a couple of namespaces that provide access to the objects that we talked about earlier. We then declare a couple of variables that represent a Windows's identity and principal. We then get the current user who is running the application by using the GetCurrent method of the WindowsIdentity object. We then create a principal object using the identity that we just retrieved. At this point of time, the principal object represents the current user identity and the set of roles (or groups) to which the user belongs. Every button has an enabled property that can be set to True / False. We set the enabled property to the result of the IsInRole method of the principal object. Note that standard enumerations are provided for standard Windows groups. If you have a group that is not in this list, you can specify it as a string parameter. The ouput of this method call is either True / False indicating that the user belongs to the role or not. This, when set to the button enabled property will enable / disable the button. That's all there is to it!! Now, if you run this program, based on your role membership in your system, either both the buttons are enabled or one of them (or both) might be disabled. For example, in my laptop (where I'm writing this article and running the sample), I'm not a member of the power users group and thus, the View Salaries button will be disabled. Having seen how to use Windows* objects to implement role-based security, let us now see how to use Generic* objects to implement the same. When you use Generic* objects, the assumption is that the users of your application are not Windows users, but rather stored in your application itself (maybe a database). Also, roles for the user are also stored inside your application. In this context, you will have to create your own identity and principal objects and populate the roles by yourself. Let us assume that your application has the following interface:
Since users do not belong to Windows, we provide our own user-name and password text boxes for the user to enter information. When the user clicks on the Authenticate button, we will determine the user and assign the appropriate role to the user account. Now, when the user clicks any of the button in the operations area, the following conditions are to be implemented:
The black text box at the bottom of the screen, provides feedback when the user clicks the button. Let us now see the source code for the application. Since its a slightly complex application, we will see the source code in bits. First, let us see the list of namespaces that we have to import.
After the user enters the user-name and password, the user will click on the Authenticate button. Let us see what happens when the user clicks this button.
Based on the user-name that is entered, we assign static roles to the strRoles array. In reality, at this point you will communicate with the database and check whether the user exists and if so, fetch the set of roles for the user. Since our example, is to just illustrate the concept, we just hardcode some values. After the array is populated, we then create a GenericIdentity object passing in the user-name that has been entered. Next, a GenericPrincipal object is created passing in the identity object and roles for that user as parameters. We then assign the principal object of the current thread to the created principal object, indicating that the user whom we created is running the application . Finally, the feedback text box is populated with a string indicating that the user has been authenticated. Now the user can click any of the two operation buttons. Here is the source code for both the buttons.
For the Payroll button, what we do is, create a PrincipalPermission object and pass to it two parameters. The first parameter indicates the identity that you want to check. Since we do not want to check for a specific identity (but rather any user), we pass Nothing as the parameter. This has the effect of checking for any identity. The second parameter is the role that the identity should have that we want to check. For the Payroll button, the role that we expect the identity to have is the Managers role. After creating the permission objectc, we use the demand method to check whether the identity has the appropriate role. This check is put inside a Try...Catch block. If the demand fails, we print out an error message that the current authenticated user (got by using Thread.CurrentPrincipal.Identity.Name property) does not have enough permissions, otherwise, we print a positive message indicating success. The logic for the Reports button is the same expept that the role used is different. When the program is now executed, the following output will be observed for the user-name srinivas.
Note that I have captured the failure message. If you click on the Payroll button, you will get a success message. You can test the program using the other user name that we have hardcoded in the application. That brings us to the end of this article. Just to summarize, we have seen how to use role-based security for Windows based users and custom users using the Windows* and Generic* objects. The implementation logic is quite simple and you can use it to provide useful functionality in your applications. The logic is the same for web-based applications except for certain differences. We will deal with implementing role-based security for web-based applications in a future article. A good exercise for you to try would be to replace the hadrcoding in the application to database calls so that the example becomes more realistic. Have fun!! |
| Home |