As we mentioned in the last chapter, two linked attributescalled member and memberOfcontrol group membership. The group object always holds the forward-linked member attribute; the memberOf attribute is a calculated back link held on the group member object itself. As such, group membership is always managed from the group object side of the relationship and the back link is updated by the system automatically.
We have a couple of options for adding and removing group members. We can use the IADsGroup.Add and IADsGroup.Remove methods using the Invoke syntax, or we can directly manipulate the member attribute. The only situation we really need to worry about is an error condition that can arise when adding a user that is already a member, or removing a user that is not already a member of the group. To avoid this error condition, we should either check the target object's membership or catch this error explicitly.
We can use the IADsGroup.IsMember method to check group membership instead of relying on the PropertyValueCollection.Contains method. We will typically prefer to use the former method because as we learned in Chapter 6, the member attribute can often be quite large and will require range retrieval to return all the values. The Contains method is inadequate for large groups, as the collection will not contain all of the member values.
Listing 11.4 demonstrates a simple class that inherits from DirectoryEntry and provides two new methods to add and remove memberships. This example is taken from a much larger class that is more comprehensive, but has been pared down to fit in this book. The full implementation will be available with the sample code on this book's web site.
Listing 11.4. Strongly Typing the Group Membership
public Group : DirectoryEntry { public bool Remove(DirectoryEntry obj) { if (IsMember(obj)) { this.Invoke( "Remove", new object[]{ obj.Path } ); return true; } return false; } public bool Add(DirectoryEntry obj) { if (!IsMember(obj)) { this.Invoke( "Add", new object[]{ obj.Path } ); return true; } return false; } public bool IsMember(DirectoryEntry obj) { return (bool) this.Invoke( "IsMember", new object[]{ obj.Path } ); } } |
Using this class is as simple as creating something like this:
//group represents a Group object for the group //and user represents a DirectoryEntry object for the user if (group.Add(user)) { Console.WriteLine( "{0} added successfully", user.Name ); }
Of course, we can always directly update the group's member attribute to add or remove membership. For completeness, Listing 11.5 shows an example of directly adding to the member attribute.
Listing 11.5. Using the member Attribute to Manage Membership
class Group : DirectoryEntry { const int ERR_DS_ATTRIBUTE_OR_VALUE_EXISTS = -2147016691; const int ERR_DS_NO_ATTRIBUTE_OR_VALUE = -2147016694; private bool Add(string memberDN) { try { this.Properties["member"].Add(memberDN); this.CommitChanges(); } catch (System.Runtime.InteropServices.COMException ex) { if (ex.ErrorCode != ERR_DS_ATTRIBUTE_OR_VALUE_EXISTS) throw; return false; //already a member } return true; } private bool Remove(string memberDN) { try { this.Properties["member"].Remove(memberDN); this.CommitChanges(); } catch (System.Runtime.InteropServices.COMException ex) { if (ex.ErrorCode != ERR_DS_NO_ATTRIBUTE_OR_VALUE) throw; return false; //not a member } return true; } } |
Listing 11.5 demonstrates how to catch the error that will occur when we add a duplicate member. Similarly, we need to catch the error that will occur when we try to remove a member that does not exist. These two new method overloads in Listing 11.5 would complement the methods shown in 11.4.
The question that naturally arises is which one should I use? Choosing between the two methods is a bit of a balancing act. In a number of circumstances, it is actually easier to revert to invoking the IADsGroup methods. This is especially true when determining group membership. The thing we need to keep in mind is that using the IsMember method and then adding the member requires two network trips. Manipulating the member attribute directly will save us one network trip by skipping the membership check. However, depending on our usage, we could also end up throwing a lot of exceptions that might offset this advantage.
If we are rebuilding group membership from the ground up, it is generally going to be faster to just add directly to the member attribute and call CommitChanges one time. We will be able to do this because we know that duplicates won't exist. In any other scenario, we should choose between the two methods shown in Listings 11.4 and 11.5 as our data demands.
Part I: Fundamentals
Introduction to LDAP and Active Directory
Introduction to .NET Directory Services Programming
Binding and CRUD Operations with DirectoryEntry
Searching with the DirectorySearcher
Advanced LDAP Searches
Reading and Writing LDAP Attributes
Active Directory and ADAM Schema
Security in Directory Services Programming
Introduction to the ActiveDirectory Namespace
Part II: Practical Applications
User Management
Group Management
Authentication
Part III: Appendixes
Appendix A. Three Approaches to COM Interop with ADSI
Appendix B. LDAP Tools for Programmers
Appendix C. Troubleshooting and Help
Index