Retrieve Security sample V1.1
Introduction
This sample has been created for the sole purpose of demonstrating the capabilities of the Microsoft .NET Frameork v1.1 and Visual Studio.NET 2003. It is not intendent for commercial use, reproduction or whatsoever. This example illustrates the use of the following functionaility:
This example is intented to show the power and flexibility of the .NET security model. It switches the standard IPrinicpal - which is using windows authentication - with a custom IPrincipal. It allows implements a custom IIdentity. The custom IPrincipal and IIdentity work against a database.
The sample uses a standard windows form, which ask for a user name and password. It then checks the username and password combination against the Users table in the database. It uses ADO.NET interface programming - so it programs against standard interfaces instead of data provider specific obejcts - and allows you to specify in the .config file which ADO.NET data provider should be used. It then creates a connection object from this data provider and from then on works only against the interfaces common to all .NET data provider objects. This makes it easy to use the power of each data provider while still maintaining one common code set.
When the user has been authenticated it reads the user information of the user, to which secuirty groups (or roles) the user belongs to and what security rights the user has (through all the security rights associated to all the secuirty groups the user belongs too). All the SQL statements are read from the .config file so it is easy to change them without neccessary code changes. With all that information it creates a custom IPrincipal and custom IIdentity obejct. These classes use the information read from the database. It is easy to add new security groups and secuirty rights to the database. No code changes are needed beside changing the SQL statements in the .config file.
Lastely the logon controls are hidden on the form and dynamically new controls are palced onto the form. Three list boxes which show the user information, the list of security groups the user belongs too and the list of all security rights the user has. The sample then places a number of buttons on the form which allow to showcase how to use the new IPrincipal and IIdentity to read user information, demand and check for security roles and then also to check for security rights (which does not follow the .NET security scheme of walking the stack - but that is not really needed for simple application security checks)
The sample then shows a log off button which shows how to restore the original form and the original IPrincipal and IIdentiy. This sample show cases very well the power of the .NET security classes. This sample is in itself a working membership-system. It allows to log on, log off, check for security roles and then check for secuirty roles. The part missing would be to allow to create new users and a remember password functionality. This can be easily added.
DataLayer - encapsulates all the data access logic of the sample
DataLayerException - whenever the data access logic is throwing an error it uses this exception class. This allows the caller to catch exceptions specific to the data access layer.
DataLayer - encapsulates all the data access logic itself. It is written generically so it can utilize different data providers based on configuration settings. Has a private constructor so no instance of this class can be created, because all members are static.
GetString - get a string from the resource file.
GetConnectionOject - creates a connection object using the data provider configured in the .config file. It returns the common interface IDbConnection so the rest of the sample can be written using generic data access code (interfaces).
CreateCommandObject - creates a command object and sets its proeprties - command text and type, which are read from the .config file.
AddParameter - adds a parameter to the command object. The name of the parameter is read from the .config file.
CheckUserNameAndPassword - checks whether the username and password exists in the users table.
ReadValuesIntoHashtable - executes a command object and reads all the informtion into a hashtable. This process can be customized by providing a AddRecordInfo delegate. The information is read through a data reader. Each record found is added to the hashtable (first column is used as key and second column is used as value) or if a AddRecordInfo delegate it just calls the delegate and lets the delegate take care of reading the record information.
RetrieveSecurityInformation - reads all the security information for a given users. We return alist of security groups (roles) the user is belonging to and a list of security rights (permissions) the user has. Both are returned as a hash table. Uses ReadValuesIntoHashtable to read the infromation into the has tables.
RetrieveUserInformation - reads all the user information for a given user and returns it as a hash table. Uses ReadValuesIntoHashtable to read the information into the hash table. By calling it specifies AddUserInfoToHashtable as the delegate to use.
AddUserInfoToHashtable - Delegate used to read the user information into a hash-table. Reads all columns and adds them.
SecurityLayer - encapsulates all the security related logic of the sample.
RetrieveSecurityException - whenever the security logic is throwing an error it uses this exception class. This allows the caller to catch exceptions specific to the security layer.
Resources - encapsultes any interaction with the resource file. Has a private constructor so no instance of this class can be created, because all members are static.
GetString - retrieves a string from the resource file
SecurityPrincipal - the custom security principal this sample implements. It implements the IPrincipal interface. Has a private constructor to avoid an instantiation of the class. You need to call the static member SetSecurityPrincipal.
SetSecurityPrincipal - creates an instance of this custom security principal and then sets it as the active security principal for the executing thread. Returns the previous security principal.
RestoreSecurityPrincipal - restores the previous security principal.
Identity - returns the Identity associated with this security principal. This identifies the user itself.
IsInRole - returns if the user is belonging to a secruity role or not.
HasPermission - returns if the user has a security permission or not.UserIdentity - the custom IIdentity this sample implements. Think of it as the user object which gives you access to user information. Has a private constructor to avoid an instantiation of the class. You need to call the static member CreateUserIdentity.
CreateUserIdentity - creates an instance of this class and returns it.
AuthenticationType - the autnetication type used. In our example we return database.
Name - the name of the user.
GetPropertyNames - returns a list of all properties this custom IIdentiy supports.
GetProperty - returns the value of the requested property.
SetProperty - sets the value of a property.
RetrieveSecurity - shows the windows form which intially shows the logon screen. After the user entered the user name and password and it has been verified we dynamically change the form to display user information, security group info and security info. It shows then also all the buttons to test the security features.
btnLogOn_Click - the user clicked the logon button. We call DataLayer.CheckUserNameAndPassword to check the username and password and if correct we call DataLayer.RetrieveSecurityInformation to retrieve the security information for the user and DataLayer.RetrieveUserInformation to retrieve the user information. We call then SecurityPrincipal.SetSecurityPrincipal to create and install our custom IPrincipal which in turn creates the custom IIdentiy. We pass along the user and security information we read from the database. At the end we re-arrange the form to display user information, security group info and security info. It shows then also all the buttons to test the security features.
CheckSecurityRole - Creates a principal permission for the required security role and then demands that security role. The .NET framework then finds the current IPrincipal (which is our custom security principal) and then checks if the user belongs to that security role. If the user does not belong to that security role it will throw a SecurityException exception.
User_Click, PowerUser_Click, Administrator_Click, SuperAdministrator_Click - used to check if the user belongs to the user, power user, administrator or super administrator security role. These event handlers call CheckSecurityRole in turn.
CheckSecurityPermission - gets the current principal, checks that it is the custom security principal we installed and then calls HasPermission on it to check whether the user has a certain permission. If the current principal is not our custom one we always return no.
ViewBudget_Click, ViewInventory_Click, ViewRevenue_Click, ViewProfit_Click - These event handlers call CheckSecurityPermission to check if the user has these view permissions.
ModifyBudget_Click, ModifyInventory_Click, ModifyRevenue_Click, ModifyProfit_Click - These event handlers call CheckSecurityPermission to check if the user has these modify permissions.
UserDetails_Click - checks if the current IIdentiy is our custom IIdentiy and if so gets the list properties we support and then displays all proeprty names and values.
LogOff_Click - logs the user off. Restores the original IPrincipal and IIdentiy and restores the form to its original state. This allows then another user to log on.
AddButtonWithEventHandler - dynamically adds a button to the form and associates a event handler with it.
AddListBoxWithValues - adds dynamically a list box to the form.
Copyright (c) 2004, Klaus Salchner
Klaus Salchner
1430 Chesterfield Ave., Suite 100
North Vancouver, BC, V7M 2N4
klaus.salchner@telus.net