views:

1878

answers:

5

I currently have some code that pulls down a list of users in a group and then iterates through that group to determine if a given account exists, but it seems like there ought to be a more concise (and perhaps faster) way to accomplish this.

This code (VB.NET) attempts to use the member property of the group object, but it is returning false even when the user is a member of that group. Can anyone see what I am doing wrong here?

Dim group As DirectoryEntry =  GetNetworkObject(GroupDomanName, NetworkObjectType.NetworkGroup, GroupName)
Dim user As DirectoryEntry =GetNetworkObject(UserDomainName, NetworkObjectType.NetworkUser, Login)

Return group.Properties("member").Contains(user.Path)

FYI: The GetNetworkObject calls just return a directoryEntry object, I have confirmed that the correct object is being returned for both the group and user object.

+9  A: 

If you are on .NET 3.5 stack, System.DirectoryServices.AccountManagement.dll assembly has a nice API on top of AD. The following method can be implemented to solve your issue:

static bool IsUserMemberOf(string userName, string groupName)
{
    using (var ctx = new PrincipalContext(ContextType.Domain))
    using (var groupPrincipal = GroupPrincipal.FindByIdentity(ctx, groupName))
    using (var userPrincipal = UserPrincipal.FindByIdentity(ctx, userName))
    {
        return userPrincipal.IsMemberOf(groupPrincipal);
    }
}

// Usage:
bool result = IsUserMemberOf("CONTOSO\\john.doe", "CONTOSO\\Administrators");

I don't know how this method performs but it is a clean solution.

huseyint
Thanks. I didn't even realize that 3.5 added that (much needed) abstraction layer for AD. I'll give you a vote on the answer because it is excellent, but alas my deployment environment is still NET 2.0.
JohnFx
+1  A: 

Here is what I've used in the past in a VBS Script that worked very well:

Set wshNet = CreateObject("WScript.Network")       'Setup connection to the Network
Set fso = CreateObject("Scripting.FileSystemObject")     'Create File System Object for any file manipulations

Set ADSysInfo = CreateObject("ADSystemInfo")       'Setup connection to Active Directory
Set CurrentUser = GetObject("LDAP://" & ADSysInfo.UserName) 'Setup current user to look for in Active Directory
strGroups = LCase(Join(CurrentUser.MemberOf))      'Grabs all the groups the current user is a member of

I then use an InStr to see if the user is part of that group:

If InStr(strGroups, "MyGroup") Then MyGroupSub

You might be able to adapt the above in your project.

By the way, I noticed that in your code you have groupdoman as your last parameter for 'group' Not sure if you wanted that to be groupdomain or not:

Dim group As DirectoryEntry = GetNetworkObject(GroupDomanName, NetworkObjectType.NetworkGroup, GroupName, groupdoman)

vs

Dim group As DirectoryEntry = GetNetworkObject(GroupDomanName, NetworkObjectType.NetworkGroup, GroupName, groupdomain)

Let me know if this helps! JFV

JFV
Good catch on the groupdomain parameter. Actually that was part of some code that was unrelated to the problem that I only half deleted (oops, fixed now) it allowed me to pass in the OU for the group.
JohnFx
+1  A: 

I found an answer that seems to work in NET 2.0, is relatively quick and overcomes the possible issue of groups containing more than 100 items (which require range searching)

Here is the code that I wound up with:

Dim DSearcher As New DirectorySearcher(group, "(&(objectClass=user)(cn=" + Login + "))", New String() {"member;Range=0-5000"}, SearchScope.OneLevel)                  
group = GetNetworkObject(GroupDomanName, NetworkObjectType.NetworkGroup, GroupName)
user = GetNetworkObject(UserDomainName, NetworkObjectType.NetworkUser, Login)
DSearcher.AttributeScopeQuery = "member"
Return (DSearcher.FindOne() IsNot Nothing)
JohnFx
Heads up. I'll tweak this entry when I am done testing, but I have discovered it fails in two cases:1) If the user exists in the group, but is from another trusted forest.2) If a user with the same name but different domain is a member of the group.
JohnFx
Does this work recursivly as well? I don't think it will.
benPearce
+1  A: 

Here's another way using the directory searcher and memberOf. This is using the current users objectSID but you could change that to some other identifier.

dSearch.Filter = String.Format("(&(memberOf={0})(objectSid={1}))", groupDN, WindowsIdentity.GetCurrent.User)

Return dSearch.FindOne() IsNot Nothing

if your going to use user input which might contain invalid characters, you should always escape them...

searchName = searchName.Replace("\", "\5c"). _
                                Replace("/", "\2f"). _
                                Replace("*", "\2a"). _
                                Replace("(", "\28"). _
                                Replace(")", "\29")
dotjoe
LDAP filter parameters must be escaped or at some point your filter will break. See my answer to: http://stackoverflow.com/questions/190516/getting-all-direct-reports-from-active-directory#190570
Tomalak
A: 

Hey,

Can this be done in c# also?

Many Thanks

Kieran

78lro
Go to http://www.developerfusion.com/tools/convert/vb-to-csharp/ and paste the VB code. Click the Convert to C# button. It's not foolproof, but it's pretty sound.
BenAlabaster