Mike H. - Another Geek In Need...

WebLog

  Home  |   Contact  |   Syndication    |   Login
  58 Posts | 6 Stories | 195 Comments | 294 Trackbacks

News

Archives

Post Categories

Image Galleries

Development

Favorite Blogs

Hosting

User Groups

.Net Directory Services Programming – C# - Part 3

 

Topics

DirectorySearcher – the other critical class in the DirectoryServices namespace.

 

 

Review

Because a lot of your Directory Services (DS) development will involve querying DS for data, it makes sense that this is a powerful class offered in the namespace, and below are some of the features:

 

  • DirectorySearcher – Performs the initial queries against AD
  • SearchResult – A single object reference from a search performed by DirectorySearcher
  • ResultPropertyCollection – A collection of properties of the SearchResult instance
  • ResultPropertyValueCollection – The values of properties in a SearchResult instance.
  • SearchResultCollection – Basically a collection of SearchResult instances returned from a query by DirectorySearcher
  • SortOption – Allows a means to sort a result set.

 

 

 

We have briefly touched on DS by introducing some basic DirectoryEntry information and a couple simple examples, and we have introduced some basic properties and examples on how to interact with those.

 

I have not taken a lot of time to review a directory structure – basically assuming that you should have some of this understanding already. As I delve into the next lesson, this assumption holds fast. If you have any issues or are dealing with something that I am not elaborating enough on, please feel free to email me directly at (mikeh AT thedataworks.net) and I will respond as quickly as I can. Please forgive the notation there – but we all are familiar with the latest in SPAM bots and whatnot.

 

Show me the Money!!!

Or at least the code. Lets get started…

 

Parent – Child Relationships

DirectoryEntry objects (DE’s) can be a root object in AD, or an object within another – hence, a child object of another. We reviewed in Part 1 a few of the common references to object types (CN, OU, DC). These are the most prevalent you will deal with.

 

DC is the root level context. OU’s can be off the root, or a child of other OU’s and even Containers. CN’s can be off the root, within OU’s and often are found within Containers.

 

A word about Containers and Security

When planning your DS application, keep this in mind: Containers are non-secure objects! Let me explain. AD security is multi-faceted, yes, but the real security is managed using Group Policy (something we are not going to review here).

 

Containers are basically folders in your directory tree. AD comes with a couple by default. If you promote a server and check your directory tree, you’ll find a Users, a Built-In, and a couple other folders off the root tree (or context/server name) object. These are not OU’s – they are containers.

 

Group Policy (GP) cannot be applied to a Container object in AD. You can apply GP to objects within Containers – depending on what those objects are – but not the Container itself. This is important because a lot of administration can be simplified by applying the GP to the entire Container. So if not the Container, then what? Organizational Units – OU’s. Alas, OU’s are going to be your preferred folder or container (for lack of a better way to put it) for object storage.

 

Basic Searches

In Part 1 we introduced a server name and basic OU’s. To re-cap that:

 

Server: developer.hamilton.com

OU=Accounts – off the root of developer.hamilton.com

OU=Developers – within the OU=Accounts

CN=Mike Hamilton – within the OU=Developers

 

Within the OU=Accounts I might also create service accounts – basic user accounts that are used to run services such as IIS Application Pools, SQL Server, or other server / service applications. I might also have OU’s that represent other departments that pertain to development: BA’s for the Business Analyst, QA’s, etc. You can nest OU’s and accounts in just about any way you want to. NOTE: In AD – if you have not already – you will run into one of the inherent limitations of the AD Users & Computers (dsa.msc) snap-in: It can only display the first 2,000 objects within a Container or OU. So if you anticipate a large volume of users for a system you are designing, keep this in mind. You will want to present 1) a more suitable customized user interface piece for administration of this DS, or 2) design your object hierarchy so that users are more spread out within the DS. I will touch base on this topic in a later Part and introduce a couple ideas for addressing this limitation.

 

DirectorySearcher and the SearchResult

When using the DirectorySearcher class, you have the FindOne() and FindAll() methods that build your result set. As they infer, one finds one occurrence, the other finds all occurrences.

 

Let’s say I had more than one Mike on my team, and I wanted to find all entries that began with Mike?

 

DirectoryEntry rootEntry = new DirectoryEntry();                // Binds root context

// Now find every CN that has Mike at the beginning of the name…

DirectorySearcher searcherResults = new DirectorySearcher(rootEntry,”(CN=Mike*)”);

 

This is a simple example, and the result set will be one or more object references that begin with Mike. Notice that I did a bind on the root of the DS. I could have narrowed the focus of the search to only the developer’s OU by doing the following:

 

DirectoryEntry developersEntry = new DirectoryEntry(“LDAP://developer.hamilton.com/OU=Accounts,OU=Developers,DC=developer,DC=hamilton,DC=com”);

 

This would return an entry object where the parent would be the OU=Developers, and not the default root of the DS tree.

 

Lets say my DS was structured like the following:

 

developer.hamilton.com à root of the DS

            OU=Accounts à First OU of accounts of users.

                        OU=Developers à Key development personnel

                        OU=Bas à Business Analyst

                        OU=QA à Quality Assurance / Test personnel

                        OU=Users à General users on this server.

 

Now, we want to find all users that are in the Users OU, not returning the developers or other personnel within the directory. That’s pretty straight forward.

 

DirectoryEntry developersEntry = new DirectoryEntry(“LDAP://developer.hamilton.com/OU=Accounts,OU=Users,DC=developer,DC=hamilton,DC=com”);

 

// Now return all users in this OU…

DirectorySearcher searcherResults = new DirectorySearcher(rootEntry,”(CN= *)”);

 

As you get more familiar with DS programming, you will find there are other ways to accomplish the same result – I am simply trying to open those doors for you.

 

It is important that you understand how to narrow your search filter in this way because you will find it greatly improves the performance of your application when you know where to search for content and you keep your search filter narrowly defined. For example, if I have 15,000 employees, they would very likely be grouped in AD by their department, and possibly further grouped by sub-departments. I would want to know this information beforehand so I can plan my DS application to be as efficient as possible. I would not want to search the rootDSE each time for a given employee or group of employees, if I know the departments those employees are in.

 

More on the Search Filter

The filter is a LDAP string that allows you to search for objects based on specific criteria. You will also use relational operators to better refine your search. The following list those operators:

 

=                      Equal to

~=                    Is approximately equal to (or like)

<=                    Less than or equal to

>=                    Greater than or equal to

 

NOTE: You cannot use ‘<’ or ‘>’ individually in LDAP – you must use the notation <= or >=.

 

The * character works as a wildcard in your search, as in the example above we searched for CN=Mike* or CN=* - in this case, get all Canonical (common) Names beginning with Mike, or get all names period (respectively).

 

To find all users with a surname of Hamilton you would use

 

(sn = Hamilton)

 

To find all users where the email ends in @hamilton.com

 

(&(objectCategory = person) (objectClass = user) (mail = *@hamilton.com))

 

Notice the Polish notation used here – the operator comes before the condition. LDAP uses this notation in the Filter. The above example would be more familiar possibly as

 

(objectCategory = person) AND (objectClass = user) AND (mail = *@hamilton.com)

 

The following are allowed logical operators:

 

&         AND

|           OR

!           NOT

 

You set the filter for a search using the DirectorySearcher.Filter property. The following code will create a binding to the root context, and return everything from the root down:

 

DirectoryEntry rootEntry = new DirectoryEntry();                // Binds root context

DirectorySearcher rootSearcher = new DirectorySearcher(rootEntry);

 

Let’s say we have a binding to the rootDSE as above, but only want to return all users with a surname of Hamilton? Change the DirectorySearcher to the following:

 

DirectorySearcher rootSearcher = new DirectorySearcher(“(sn=Hamilton)”);

 

You can either pass the DirectoryEntry object, or you can specify a filter in this fashion when creating a DirectorySearcher instance.

 

Loading Specific Properties

By default, when you create a DirectorySearcher instance, you return all properties for that binding (which can be a lot of data if you have a lot of items in your returned result set).

 

You can refine your result set by specifying the exact properties to be returned. This is especially important when you could be returning large collections of users.

 

Following the above example I could create my rootSearcher and then do:

 

rootSearcher.PropertiesToLoad.Add(“name”);

rootSearcher.PropertiesToLoad.Add(“sn”);

rootSearcher.PropertiesToLoad.Add(“mail”);

rootSearcher.PropertiesToLoad.Add(“telephoneNumber”);

 

Here I have specifically limited the result set to only include the name, sn (surname), mail and telephoneNumber properties.

 

Finally, when we define our searcher, we can stipulate the search scope.

 

Search Scope

The search scope determines the extent of the search in a directory, defaulting to beginning at the root.

 

In the System.DirectoryServices.SearchScope you will find the following 3 enumerations specified:

 

Base – Limits the scope to the base, or only 1 object.

 

OneLevel – Searches 1 level of the immediate child objects, excluding the base object.

 

Subtree – Searches all child objects under the base object, including the base object.

 

When creating a DirectorySearcher instance, the following table lists default constructor behaviors:

 

PROPERTY

DEFAULT VALUE

DESCRIPTION

SearchRoot

Null

Searches the root of the domain controller (rootDSE).

Filter

(objectClass=*)

The search retrieves all objects.

PropertiesToLoad

An empty string array

The search retrieves all properties.

SearchScope

Subtree

The search will be performed on the complete subtree of the base object (or entire domain in this case).

 

 

 

 

 

Constructors of the DirectorySearcher Class

The above table displays the default constructor when you create a DirectorySearcher instance like:

 

 

DirectoryEntry rootEntry = new DirectoryEntry();                // Binds root context

DirectorySearcher rootSearcher = new DirectorySearcher(rootEntry);

 

The above specifies the Search Root.

 

Specifying the Filter Only

We showed this earlier,

 

DirectorySearcher rootSearcher = new DirectorySearcher(“(sn=Hamilton)”);

 

Specifying the Search Root and Filter

Create our base / root reference:

 

DirectoryEntry rootEntry = new DirectoryEntry(“LDAP://developer.hamilton.com/OU=Accounts,OU=Developers,DC=developer.DC=Hamilton,DC=com”);

 

// Now specify the filter…

DirectorySearch rootSearcher = new DirectorySearcher(rootEntry, “(sn=Hamilton)”);

 

Specifying the Filter and a List of Properties to load

 

// Create the set of properties to load…

string [] searchProperties = new string[4];

searchProperties[0] = “name”;

searchProperties[1] = “sn”;

searchProperties[2] = “mail”;

searchProperties[3] = “telephoneNumber”;

 

// Get all users that have an email ending in hamilton.com…

DirectorySearcher rootSearcher = new DirectorySearcher(“(mail=*hamilton.com)”, searchProperties);

 

The above example will return all users from the rootDSE with an email ending in hamilton.com. Let’s refine that.

 

Specifying the Root, Search Filter, and Properties to Load

 

// Specify the properties to load… Here we get Full Name, Company, Phone…

string [] searchProperties = new string[3];

searchProperties[0] = “name”;

searchProperties[1] = “co”;

searchProperties[2] = “telephoneNumber”;

 

// Now bind to a more refined root – developers only…

DirectoryEntry rootEntry = new DirectoryEntry(“LDAP://developer.hamilton.com/OU=Accounts,OU=Developers,DC=developer,DC=hamilton,DC=com”);

 

// No get all users from this root with an email ending in hamilton.com…

DirectorySearcher rootSearcher = new DirectorySearcher(rootEntry, “(mail=*hamilton.com)”, searchProperties);

 

You should have a better idea now how to use the various constructors of the DirectorySearcher class.

 

Methods of the DirectorySearcher

When you have created a binding and set filter values, you will use 1 of the 2 DirectorySearcher methods:

 

FindOne() – Returns the first, and only 1 object of the search criteria.

FindAll() – Returns a SearchResultCollection of all objects that matched the search criteria.

 

We’ll do a brief sample for each method and wrap up this series with the Sorter class.

 

Return using FindOne()

 

// The following demonstrates that FindOne() returns only the first entry that matches the search critieria…

DirectoryEntry rootEntry = new DirectoryEntry(“LDAP://developer.hamilton.com/OU=Accounts,OU=Developers,DC=developer,DC=hamilton,DC=com”);

DirectorySearcher rootSearcher = new DirectorySearcher(rootEntry);

 

// What to search for…

rootSearcher.Filter = “(CN=*)”;

 

// Specific properties to load…

rootSearcher.PropertiesToLoad.Add(“name”);                      // Full name…

rootSearcher.PropertiesToLoad.Add(“mail”);                        // Primary email addy…

rootSearcher.PropertiesToLoad.Add(“telephoneNumber”);  // Phone #...

 

SearchResult searchResults = rootSearcher.FindOne();

 

// Extract out the properties returned and display in a console…

ResultPropertyCollection propertiesCollection;

propertiesCollection = searchResults.Properties;

 

// Cycle through and display (or add to a listbox, etc)…

Foreach (string currentProperty in propertiesCollection.PropertyNames)

{

            foreach (Object thisCollection in propertiesCollection[currentProperty])

            {

                        Console.WriteLine(currentProperty + “ = “ + thisCollection);

            }

}

 

Return using FindAll()

Unlike above, the ResultPropertyCollection being returned with FindOne(), FindAll() comes back in a SearchResultCollection object.

 

We would process the result set similar to the following:

 

// Get all objects returned…

foreach (SearchResult searchResults in rootSearcher.FindAll())

{          // Process…

            foreach (string propertName in searchResults.Properties.PropertyNames)

            {

                        foreach (Object retEntry in searchResults.Properties[propertyName])

                        {

                                    Console.WriteLine(propertyName + “ = “ + retEntry);

                        }

            }

            Console.WriteLine(“”);

}

 

If the root of the search is not properly set, a InvalidOperationException exception will be thrown. Also, if the provider you are trying this against is not supported, a NotSupportedException exception is thrown.

 

Sorting the Result Sets

Finally, the SortOption class specifies how to sort the result set of a search. The class also allows you to specify either ascending or descending sort order.

 

Let’s do a search on all users that have an email ending in @hamilton.com and that exist in the Developers OU. Further, restrict the properties returned, sort the list, and using the code snippet above, list the results in a console window.

 

DirectoryEntry rootEntry = new DirectoryEntry(“LDAP://developer.hamilton.com/OU=Accounts,OU=Developers,DC=developer,DC=hamilton,DC=com”);

DirectorySearcher rootSearcher = new DirectorySearcher(rootEntry);

 

// Set our search filter and properties to load…

rootSearcher.Filter = “(mail=*hamilton.com)”;

rootSearcher.PropertiesToLoad.Add(“name”);          // Full name…

rootSearcher.PropertiesToLoad.Add(“mail”);            // Primary email addy…

rootSearcher.PropertiesToLoad.Add(“telephoneNumber”);  // Phone #...

 

// Now sort the result set…

SortOption sortedResults = new SortOption();

sortedResults.PropertyName = “name”;                     // Sort by full name…

sortedResults.Direction.SortDirection.Ascending;

 

// Perform the search and output the results..

rootSearcher.Sort = sortedResults;

foreach (SearchResult searchResults in rootSearcher.FindAll())

{          // Process…

            foreach (string propertName in searchResults.Properties.PropertyNames)

            {

                        foreach (Object retEntry in searchResults.Properties[propertyName])

                        {

                                    Console.WriteLine(propertyName + “ = “ + retEntry);

                        }

            }

            Console.WriteLine(“”);

}

 

Final notes on the following classes: SearchResult, SearchResultCollection, ResultPropertyCollection, and ResultPropertyValueCollection.

 

The SearchResult class consist of the first entry returned during a search. It retrieves the data from the SearchResultCollection, and the FindOne() method of the DirectorySearcher class returns an instance of the SearchResult class.

 

The SearchResultCollection class is actually a collection of SearchResult instances and these are returned using the FindAll() method of the DirectorySearcher class. You use the Count property to get the number of items returned in the collection. Use the Item property to index through the SearchResultCollection to enumerate each property. Finally, the PropertiesLoaded property returns a string array containing the names of the properties loaded for the original search criteria.

 

The ResultPropertyCollection class is accessed as a Properties property of the SearchResult class. It has an Item property you use to index the collection, which is a string value. This index retrieves the values of the property matching the specified index name as a ResultPropertyValueCollection. The following snippet will demonstrate getting all returned properties and outputting the result to a console window:

 

DirectoryEntry rootEntry = new DirectoryEntry(); // Entire domain…

DireectorySearcher rootSearcher = new DirectorySearcher(rootEntry, “(sn=Hamilton)”);

 

SearchResult searchResults = rootSearcher.FindOne();

 

// Get the properties returned…

ResultPropertyCollection propertyCollection =  searchResults.Properties;

 

// Output…

foreach (string thisProperty in propertyCollection.PropertyNames)

{

            foreach (Object propertyValue in propertyCollection[thisProperty])

            {

                        Console.WriteLine(thisProperty + “ = “ + propertyValue);

            }

}

 

In the future I will introduce ADSI API and application code and we will see about developing a small application that will take some of what we have reviewed in these three parts and create a directory browser sample application.

 

I want to note that for the sample code so far, if you are not executing this on an actual AD domain controller, you will need to change the new DirectoryEntry() code to include the root, username and password of the context you are trying to connect as (we have reviewed this in previous lessons).

 

Finally, please forgive any typo’s – I try hard to make sure there are none.

 

I have received a great deal of feedback over the little I have posted so far, and I hope this posting is helpful for some. I apologize again for taking so long to get this series posted.

posted on Saturday, July 15, 2006 2:47 PM

Feedback

# re: .Net Directory Services Programming - C# - Part 3 8/16/2006 10:46 AM Patrik
Hi Mike,

Let me start by saying that your series regarding AD programming have been most helpful to me. I'm fairly new to C# programming so your tutorials have been very helpful reading for me.

I have one question for you. I've been asked to create a function to query a MS Exchange server to list:
-All the storagegroups that exists
-All the mailstores that exists in theese storagegroups
-Wich mailstore has the least number of mailboxes.

I have read some snippets on the web, but none of them has done the trick. I know I should query the msExchPrivateMDB, msExchStorageGroup and the homeMDBBL attributes, but I have never really got it all together.

TIA

Patrik

# re: .Net Directory Services Programming - C# - Part 3 12/14/2006 9:51 AM JamieL
Mike,

Thanks for the posting. It helped me out quite a bit with getting started on AD / Web integration.

Jamie.


# re: .Net Directory Services Programming - C# - Part 3 2/28/2007 12:45 PM Vel
Mike,

This article is much helpful, but I have an issue. I have multiple OU's as like Accounts, Developers. I can only able to access Accounts level, when I am trying to access Developers level, I am getting "There is no such object on the server" error. Any help will be much appreciated.

Thanks,
Vel


# re: .Net Directory Services Programming - C# - Part 3 3/1/2007 4:33 AM MikeH
Hi Vel,

If you can post your code here - perhaps I can better respond to what is happening.

Thanks...

# re: .Net Directory Services Programming - C# - Part 3 4/12/2007 1:16 PM Satish
Hi Mike,

I moving my code to the VS2005 from VS 2003 and I have a project which references the activeds which like i posted earlier is giving me these 39 warnings.

(http://geekswithblogs.net/mhamilton/archive/2005/10/04/55920.aspx?Pending=true)

Anyway I was thinking may be I should go away with the interop and see if I can just use the system.directoryservices.

The things I do in the code using the interop are: ActiveDs.LargeInteger liMaxAge = domainDE.Properties["MaxPwdAge"].Value as LargeInteger;
maxAge = (((long)(liMaxAge.HighPart) << 32) + (long) liMaxAge.LowPart);
LargeInteger li = userDE.Properties["pwdLastSet"].Value as LargeInteger;
IADsUser nativeUser = (IADsUser)userDE.NativeObject;
pwdLastSet = nativeUser.PasswordLastChanged.ToUniversalTime();
badLogonCount = nativeUser.BadLoginCount

Is there a way to do the same without the activeds reference?

Thank You,
Satish

# re: .Net Directory Services Programming - C# - Part 3 4/12/2007 1:23 PM MikeH
i Satish,

At this time - no - ActiveDS is your API for ADSI level programming. Remember, DirectoryServices namespace is just a wrapper for some 'basic' - but most core-level - LDAP stuff. When you want to get down/dirty - you have to use the ActiveDs.dll. Now, that's not to say that you cannot write your own set of classes that wrapper the COM components yourself. I have done this for one engagement - it's not too difficult. The real problem is the obvious one - there is NO documentation to help - I mean zero, zilch, nada - so most of my work is by trail/error. Or, I learn by immersion by fire - if that makes sense?

# re: .Net Directory Services Programming - C# - Part 3 4/12/2007 1:29 PM Satish
Hi Mike,

Ah.

Ok did you get a chance to look at the warnings am getting which I posted in the link. The warnings are not hurting me but am pushing hard to try and remove them.

Thank you for the help Mike.

Satish

# re: .Net Directory Services Programming - C# - Part 3 4/15/2007 6:46 AM MikeH
Hi Satish,

I did look at what you sent and nothing conveys why you are seeing the warnings. I suspect there is much more to the solution that I am not seeing #1, and I strongly suspect it has something to do with a light-weight assembly, 1 that is not strongly names/keyed #2 - if that makes sense?

You may be able to do away with the direct calls to ActiveDs.dll - but you may find that you need some of the features of that API that are not provided by DirectoryServices alone.

# re: .Net Directory Services Programming - C# - Part 3 6/28/2007 5:21 AM Waseem
Hi Mike,

Very comprehensive and nicely worked examples - thanks.

I can't quite figure out how to put this into my real world scenario:

I have 2 problems.

1) I have an intranetUser class that I want to hold each user.
I have a list collection that will then hold all intranetUser objects.

How do I dynamically iterate through the properties of my intranetUser matching with the SearchResult?

e.g
public class IntranetUser
{
// private members
string m_strSAM;
string m_strPayroll;
string m_strSurname;
string m_strForename;
string m_strEmail;
string m_strJobTitle;
string m_strCostCentre;
string m_strDepartment;
DateTime m_dtStartDate;
DateTime m_dtEndDate;
string m_iExtNo;
string m_strReportsTo;
}

pseudo-something:
in a loop
get the sAMAccountName (and all other propertiesToLoad) and populate the appropriate field(s) in the intranetUser object


2) How to distinguish between similar CN - we have some users that appear twice in our AD (freelancer left and re-joines as permanent) - Is the objectguid the property to use?

I probably haven't made much sense but my purpose is to populate our intranet personnel database with info from the AD and refresh it every night.

I thought i'd make an intranetUser class that would handle the user data and I could then do some business logic that would update (or not) personnel info to SQL.

It's a horrible interim but we can't re-write all our apps to intergrate with AD.

Thanks
Waseem

# re: .Net Directory Services Programming - C# - Part 3 6/28/2007 5:27 AM MikeH
Hi Waseem,

Have you tried a little different approach???

Get your collection of users and then check their [memberOf] property to see if they are a member of a security group you added them to in an effort to uniquely group / identify these users?

Mike...

# re: .Net Directory Services Programming - C# - Part 3 6/28/2007 8:27 AM Waseem
Hi Mike,

We have an intranet that is classic ASP with some .NET apps linked off of it. The users have to log in and their details are looked up against a database (manually updated!!) which sets some info in a cookie.

This cookie is read by various apps.

I want that database to be updated from the AD.

I don't see how the "memberOf" helps or what I should "test" this against.

Thanks for your help
Waseem

# re: .Net Directory Services Programming - C# - Part 3 6/28/2007 8:45 AM MikeH
Hey Waseem,

You mention using classic ASP but it appears that you're binding to an instance of the AD user object? Is this correct? So you're doing 'some' .Net programming where you do something like DirectoryEntry userEntry = new DirectoryEntry()? or are you doing something totally different?

If you are binding to AD - then you have the means of doing what I'm talking about.

If you are NOT using AD - then I am at a loss - based solely on your first email, which conveyed that it appears that you're working with AD. Perhaps you could share more of the architecture and how you have been working to date?

# re: .Net Directory Services Programming - C# - Part 3 6/29/2007 7:10 AM Waseem
Hi Mike,

Okay, bear with me please.

We have a Full AD with about 1000 users (500 on site)
Our intranet is a bespoke classic ASP app written in-house.
There is a login page that looks up against a PERSONNEL database and stores info in a cooke - but No security at all.

It is the DATABASE that I wish to update automatically by creating a .NET console app in C# that will dump the AD info into similar columns in the Personnel database.

1) Is that a reasonable approach?
2) Can you iterate though an object's properties and compare the "name" of the property

Thanks very much
Waseem


# re: .Net Directory Services Programming - C# - Part 3 6/29/2007 7:47 AM MikeH
He Waseem,

It sounds like you have inherited a job that I do not envy you for.

With that said, can you simply write a .Net (C#) application that handles the AD authentication and whatnot, and expose methods for the classic ASP to call into? I've done this before, and it sounds much cleaner and easier than what you're dealing with.

In fact, in .Net 2.x - you can create a new logon.aspx object that actually uses the ADMembershipProvider that comes with .Net 2.x. Here, much like you would use the AspNetSqpMembershipProvider - you can authenticate directly against AD - obviating the need for the replicated database of users.

Next, wrapper the directory services pieces you specifically need to leveral in a C#.Net application that'll take care of those calls - and deploy that aspx object in the web site.

I'm just rambling some thoughts here - my apologies. But I would try the shortest, simplest path - and implementing a new forms based auth (FBA) form that knocks out the authentication is the first thing I'd do - then I'd move onto how to read specific properties from those AD users - hence, a new aspx object altogether that can include the appropriate class references and do the work for you - and once done - simply pass back to your previous page(s).

# re: .Net Directory Services Programming - C# - Part 3 6/29/2007 8:57 AM Waseem
Hi Mike,

Eureka!

That sounds like a very good plan.
I'll do the .NET bit, store the details (cookie, db etc)
and flip back out to the intranet.

What I really want to do over the next few months is completely redo our whole "NEW USER" process using .net 3.0 and Workflows but I'm such a noob!

Thanks for all your help Mike!
Waseem




# re: .Net Directory Services Programming - C# - Part 3 12/12/2007 12:02 PM Mark Toth
Hi Mike,
This is a curious question that perhaps you can answer. I have two sets of code. The first is in ASP.Net webpage and the other is in a Windows Console App. Both using C#.
I am trying to see if a user is in a role.

In the web app I use this
Page.User.IsInRole("RoleName")

and in the windows console app I use
IPrincipal currentUser = new System.Security.Principal.WindowsPrincipal(System.Security.Principal.WindowsIdentity.GetCurrent());

currentUser.IsInRole("RoleName")

the web page version works where the windows console appliction does not unless I do this
currentUser.IsInRolw(@"starhrg/RoleName");

can you possibly explain what the difference is why do I have to append the domain name in a Windows Application?

Thanks

# re: .Net Directory Services Programming - C# - Part 3 12/12/2007 3:57 PM MikeH
Hi Mark...

Without working with the actual code - there could be a number of reasons why. From what you provided - you are having to determine the Principal in the console application - using the COM interop piece more than likely, whereas in the IIS application - and assuming you may be running .Net 2.x - you're getting that automatically (the Principal and Identity) - this obviates checking the domain\userName - you have that already.

In .Net 1.1 it was much like you're doing with the console application and was not intuitive.

I'm not sure if this is it - just the ramblings of another Geek In Need...

# re: .Net Directory Services Programming - C# - Part 3 12/12/2007 5:01 PM Mark Toth
That's the nail on the head. The Web app uses 2.x and the console app used 1.1

Wow, Geek in need is a Geek indeed!

Thanks for the help! Brilliant catch!

# re: .Net Directory Services Programming - C# - Part 3 3/26/2008 1:17 AM PAtil
Hi Mike,
In our application we are querying the details of a user by passing the email-id.
The function used is "search.FindOne()" .
The application is working fine on Windows 2000
but it is throwing ""A local error has occurred." error when the same application is tested on Windows Vista.

The application is built using VS 2005.
Is there any compatibility issue on Vista ?

# re: .Net Directory Services Programming - C# - Part 3 3/26/2008 6:19 AM MikeH
Hi Patil,

I do not think Vista supports the same API / AD schema structure of true Active Directory. I am not certain that it supports the older LDAP as well - I would have to look into this.

Post Feedback

Title:
Name:
Email: (never displayed)
Url:
Comments: 
Please add 8 and 8 and type the answer here: