Applying Directory Services


Because the IssueTracker application is intended for deployment in large corporate enterprises , supporting directory services will be essential. You will now extend the IssueTracker application to leverage an existing Active Directory system to manage user information. You will accomplish this by implementing the User and UserCollection business objects. Rather than relying upon the standard business object manager developed in Chapter 2, "Accessing Data in ADO.NET," you will create a specialized business object manager to manage user data maintained within an Active Directory system. Figure 3-5 illustrates how this new object manager relates to the standard business object manager.

click to expand
Figure 3-5: Extending the application framework to support directory services

Adding Active Directory User Attributes

As mentioned, you can add custom object attributes to any Active Directory entity, including its user definition. The IssueTracker application can benefit from this by tightly integrating its application security with an enterprise's Active Directory deployment. Instead of managing its own application login accounts, IssueTracker can validate user credentials against an existing Active Directory deployment. This way, as new employee accounts are created within the enterprise, access to the IssueTracker application can more easily be configured. Furthermore, Active Directory can have attributes added to its user definition that maintain additional user information specific to the IssueTracker application, such as a unique user identifier and a user role.

To add the unique identifier and user role attributes to the user definition, open the Active Directory Schema Editor snap-in created earlier. Select the Attributes folder in the left pane to display a complete list of available object attributes in the right pane. Next , select Action ˜ Create New Attribute from the menu to display the Create New Attribute dialog box. Enter the custom object attribute as illustrated in Figure 3-6, paying close attention to the values entered.


Figure 3-6: Creating a new custom attribute

Again, you must perform this step with care because changes to the Active Directory schema are permanent. The Common Name field is the readable label for the attribute, whereas the LDAP Display Name field is the internal label that will be later queried. The X.500 OID value is a unique identifier, ideally assigned by the ANSI organization. Finally, the Syntax field identifies the attribute's underlying data type. In the case of IssueTrackerRole, the Syntax field is Integer. This value relates to a numeric identifier that in turn relates to a specific IssueTracker user role. Click OK to permanently save the attribute to the Active Directory schema.

To add the new custom attribute to the existing user definition, return to the Active Directory Schema Editor. Expand the Classes folder and select the user node in the left pane. All associated attributes are populated in the right pane. Next, select Action ˜ Properties from the menu to display the user Properties dialog box. Select the Attributes tab to display the existing mandatory and optional user attributes. Next, click the Add button and select the IssueTrackerRole attribute from the pop-up dialog box and click OK. The user Properties dialog box updates to include the new attribute, as shown in Figure 3-7.


Figure 3-7: The updated user definition displaying a new application attribute

Creating the Application User Object

The User object is the next major application business object to be implemented. It must also inherit from the BusinessObject abstract base class and implement a supporting collection class. All supporting data elements should also be present, including its data table and stored procedures. However, instead of being managed by the business object manager, like any other business object, the User object will be managed by a new specialized object manager tied into Active Directory. Listing 3-1 outlines the code that implements the User business object.

Listing 3-1: The User Business Object Class
start example
 public class User : BusinessObject {     private int _UserID = 0;     private string _Password = "";     private string _Firstname = "";     private string _Lastname = "";     private string _EmailAddress = "";     private int _UserRole = 0;     private DateTime _CreateDate = DateTime.Now;     public enum UserRoleType     {         Guest = 0,         TypicalUser = 1,         Manager = 2,         Administrator = 3     }     public User()     {     }     public User( string strFirstname, string strLastname )     {         _Firstname = strFirstname;         _Lastname = strLastname;     }     public User(string strFirstname, string strLastname, string strEmailAddress)     {         _Firstname = strFirstname;         _Lastname = strLastname;         _EmailAddress = strEmailAddress;     }     public int UserID     {         set         {             _UserID = value;         }         get         {             return _UserID;         }     }     public string Password     {         set         {             _Password = value;         }         get         {             return _Password;         }     }     public string Firstname     {         set         {             _Firstname = value;         }         get         {             return _Firstname;         }     }     public string Lastname     {         set         {             _Lastname = value;         }         get         {             return _Lastname;         }     }     public string EmailAddress     {         set         {             _EmailAddress = value;         }         get         {             return _EmailAddress;         }     }     public int UserRole     {         set         {             _UserRole = value;         }         get         {             return _UserRole;         }     }     public DateTime CreateDate     {         set         {             _CreateDate = value;         }         get         {             return _CreateDate;         }     }     public bool ValidateLogin()     {         return false;     } } 
end example
 

Again, the User business object adheres to the framework's style and naming conventions for implementing a business object. Unlike the Issue object, the User object also includes an enumerated type definition that identifies the user as a guest, typical user, manager, or administrator.

Creating the UserObjectManager Object

UserObjectManager will be responsible for validating user credentials against Active Directory and returning a populated User business object. For environments that do not have Active Directory installed, the existing business object manager can still be used. Because the UserObjectManager extends the existing business object manager functionality, it can be implemented in the DataAccess project within this solution. Select the DataAccess project within the Solution Explorer. To access the .NET Framework support for directory services, a new reference will need to be added. Select Add Reference from the project's context menu and include the System.DirectoryServices.dll component, as shown in Figure 3-8.

click to expand
Figure 3-8: Adding a new reference to the directory service namespace

Although an enterprise can access information about any networked resource, the IssueTracker application leverages Active Directory only to manage user information within the network domain.

Validating a User Login

You can add a small method to the application that quickly evaluates if usersupplied credentials are valid. This validation is performed against the Active Directory repository using the DirectoryEntry and DirectorySearcher objects provided by the .NET Framework (see Listing 3-2). This method is only intended to be used by the application for quick validation. A separate method will actually extract the user profile.

Listing 3-2: Validating a User Login Against Active Directory
start example
 public DirectoryEntry ValidateLogin( string strUsername, string strPassword ) {     DirectoryEntry dirResult = null;     try     {         //initialize the root search object         DirectoryEntry dirEntry = new DirectoryEntry( "LDAP://srv-enterprise",             strUsername, strPassword );         //initialize the search object         DirectorySearcher dirSearcher = new DirectorySearcher( dirEntry );         //set the filter to retrieve the specific user         dirSearcher.Filter = "(&(objectClass=user)(mail=" +strUsername+ "))";         dirSearcher.SearchScope = SearchScope.Subtree;         //execute the search         SearchResult searchResult = dirSearcher.FindOne();         dirResult = searchResult.GetDirectoryEntry();     }     catch( Exception x )     {          EventLog systemLog = new EventLog();          systemLog.Source = "IssueTracker";          systemLog.WriteEntry( x.Message + " for user " + strUsername,              EventLogEntryType.Error, 0 );     }     return dirResult; } 
end example
 

The result of this method is a DirectoryEntry object that is either valid or null, rather than a boolean value of true or false. This approach is in the interest of application performance. Although this quick login validation method is helpful, it will often need to be followed up with a method call to retrieve the user profile. Rather than having the GetUserProfile method begin its search from the top of the directory tree, supplying it with the returned DirectoryEntry object gives it a significant head start.

Evaluating a User Profile

Validating a user login against Active Directory is a good start to application security. In many cases, however, that might not be enough. It might be necessary to know more about a logged-in user, such as their full name, e-mail address, and designated role within the application. You can capture all of this into a single user profile maintained by Active Directory. Of all these different application attributes, the user role is of significant interest. There are two different approaches to storing and retrieving the user in Active Directory: by group membership and by custom attribute.

In the case of group membership, application roles are defined as Active Directory groups, of which any added user can be a member. Figure 3-9 shows the three newly added groups: IssueTrackerUser, IssueTrackerManager, and IssueTrackerAdministrator.

click to expand
Figure 3-9: Specifying application user roles with Active Directory groups

In Listing 3-3, the User object is populated with an application role value that comes from one of the default Active Directory user attributes, memberOf. Based on the value of this attribute, the User object is assigned a role value during application use.

Listing 3-3: Evaluating the User Profile Against Active Directory Values
start example
 public User GetUserProfile( DirectoryEntry entryUser ) {     User objUser = new User();     try     {         //extract default user attributes         objUser.Firstname = entryUser.Properties["givenName"][0].ToString();         objUser.Lastname = entryUser.Properties["sn"][0].ToString();         objUser.EmailAddress = entryUser.Properties["mail"][0].ToString();         //extract application specific user attribute         if( entryUser.Properties["memberOf"][0].ToString().IndexOf(             "IssueTrackerUser" ) >= 0 )             objUser.UserRole = (int)User.UserRoleType.TypicalUser;         if( entryUser.Properties["memberOf"][0].ToString().IndexOf(             "IssueTrackerManager" ) >= 0)             objUser.UserRole = (int)User.UserRoleType.Manager;         if( entryUser.Properties["memberOf"][0].ToString().IndexOf(             "IssueTrackerAdministrator" ) >= 0)             objUser.UserRole = (int)User.UserRoleType.Administrator;     }     catch( Exception x )     {         EventLog systemLog = new EventLog();         systemLog.Source = "IssueTracker";         systemLog.WriteEntry( x.Message, EventLogEntryType.Error, 0 );     }     finally     {     }     return objUser; } 
end example
 

This method begins with a DirectoryEntry key as a starting point that resulted from the ValidateLogin method defined in Listing 3-2. This helps reduce the time-consuming overhead in searching out this User object within the Active Directory object tree. Next, specific properties are retrieved and assigned to related attributes of the application's User object. One of the more important attributes, IssueTrackerUserRole, will become more significant when implementing the application user interface. This attribute will define what functions a user is capable of performing as role-based security comes into light. The populated User object is returned to the caller while thrown exceptions are logged.

Listing 3-4 takes a different approach to retrieving the user role value. Rather than infer the user role value based on group membership, the role value can be retrieved from the custom user attribute defined earlier.

Listing 3-4: Evaluating the User Profile Against Active Directory Values
start example
 public User GetUserProfile( DirectoryEntry entryUser ) {     User objUser = new User();     try     {          //extract default user attributes          objUser.Firstname = entryUser.Properties["givenName"][0].ToString();          objUser.Lastname = entryUser.Properties["sn"][0].ToString();          objUser.EmailAddress = entryUser.Properties["mail"][0].ToString();          //extract application specific user attribute          objUser.UserRole = (int)entryUser.Properties["IssueTrackerRole"][0];     }     catch(Exception x )     {         EventLog systemLog = new EventLog();         systemLog.Source = "IssueTracker";         systemLog.WriteEntry(x.Message, EventLogEntryType.Error, 0);     }     return objUser; } 
end example
 

This method demonstrates an alternative approach to capturing user role information. In this case, the user role is stored as an integer value within the IssueTrackerRole custom attribute. You can quickly modify this alternative approach to implementing the GetUserProfile method in order to retrieve any other application setting stored as a custom user attribute. Again, this approach includes an element of risk in that any modifications to the Active Directory schema are permanent.




Developing. NET Enterprise Applications
Developing .NET Enterprise Applications
ISBN: 1590590465
EAN: 2147483647
Year: 2005
Pages: 119

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