I'm trying to write an LDAP query which will discover if a user is a member of a group which matches a wildcard query and I'm trying to use the LDAP_MATCHING_RULE_IN_CHAIN OID to do this. I'm basically following example 2 on this page:
http://support.microsoft.com/kb/914828
I've found that this method works well within a domain i.e. if user1 is in group1 and group1 is in group2 then I can write a query matching "*2" and the LDAP query will find the nested relationship and match the user against the group.
However, now I've been asked to support relationships between domains in the same forest. So now I've got:
user1 is a member of group1 in domain 1
group1 in domain 1 is a member of group2 in domain 2
And I want to be able to match user1 against group2.... I can't work out how to make LDAP_MATCHING_RULE_IN_CHAIN do this:
I've tried setting the base of the query to the following:
Domain 1, but this just returns groups in domain 1
The parent domain of domain 1 and domain 2, but this returns no results.
The GC, found by querying "rootDSE" property but this just returns groups inside the domain 1 (which is the GC server)
Anyone know how I can make this work?
As far as I understand, one way of doing that is :
From the RootDSE, look for the configuration NamingContext.
In the configuration NamingContext looking for objects of class crossRef with an attribute nETBIOSName existing.
From these entries use the algorithm you are discribing by using dnsRoot and nCName attributs. A working forest DNS allows you to join a domain controler of dnsRoot. nCName allows to search from the root.
Be careful to do this as a member of the enterpreise administrators group.
Here is an example of the code.
/* Retreiving RootDSE
*/
string ldapBase = "LDAP://WM2008R2ENT:389/";
string sFromWhere = ldapBase + "rootDSE";
DirectoryEntry root = new DirectoryEntry(sFromWhere, "dom\\jpb", "PWD");
string configurationNamingContext = root.Properties["configurationNamingContext"][0].ToString();
/* Retreiving the root of all the domains
*/
sFromWhere = ldapBase + configurationNamingContext;
DirectoryEntry deBase = new DirectoryEntry(sFromWhere, "dom\\jpb", "PWD");
DirectorySearcher dsLookForDomain = new DirectorySearcher(deBase);
dsLookForDomain.Filter = "(&(objectClass=crossRef)(nETBIOSName=*))";
dsLookForDomain.SearchScope = SearchScope.Subtree;
dsLookForDomain.PropertiesToLoad.Add("nCName");
dsLookForDomain.PropertiesToLoad.Add("dnsRoot");
SearchResultCollection srcDomains = dsLookForDomain.FindAll();
foreach (SearchResult aSRDomain in srcDomains)
{
/* For each root look for the groups containing my user
*/
string nCName = aSRDomain.Properties["nCName"][0].ToString();
string dnsRoot = aSRDomain.Properties["dnsRoot"][0].ToString();
/* To find all the groups that "user1" is a member of :
* Set the base to the groups container DN; for example root DN (dc=dom,dc=fr)
* Set the scope to subtree
* Use the following filter :
* (member:1.2.840.113556.1.4.1941:=cn=user1,cn=users,DC=x)
*/
/* Connection to Active Directory
*/
sFromWhere = "LDAP://" + dnsRoot + "/" + nCName;
deBase = new DirectoryEntry(sFromWhere, "dom\\jpb", "PWD");
DirectorySearcher dsLookFor = new DirectorySearcher(deBase);
// you cancomplete the filter here (&(member:1.2.840.113556.1.4.1941:=CN=user1 Users,OU=MonOu,DC=dom,DC=fr)(cn=*2)
dsLookFor.Filter = "(member:1.2.840.113556.1.4.1941:=CN=user1 Users,OU=MonOu,DC=dom,DC=fr)";
dsLookFor.SearchScope = SearchScope.Subtree;
dsLookFor.PropertiesToLoad.Add("cn");
SearchResultCollection srcGroups = dsLookFor.FindAll();
foreach (SearchResult srcGroup in srcGroups)
{
Console.WriteLine("{0}", srcGroup.Path);
}
}
This is just a proof of concept, you have to complete with :
using using(){} form for disposing DirectoryEntry objects
Exception management
Edited (2011-10-18 13:25)
Your comment about the way you solve the problem can be found in a method given in System.DirectoryServices.AccountManagement Namespace. It's a kind of recursive solution. This time, I test with a user belonging to group1 (in an other domain) which belongs to group2 (in a third domain) and it seems to work.
/* Retreiving a principal context
*/
Console.WriteLine("Retreiving a principal context");
PrincipalContext domainContext = new PrincipalContext(ContextType.Domain, "WM2008R2ENT:389", "dc=dom,dc=fr", "jpb", "PWD");
/* Look for all the groups a user belongs to
*/
UserPrincipal aUser = UserPrincipal.FindByIdentity(domainContext, "user1");
PrincipalSearchResult<Principal> a = aUser.GetAuthorizationGroups();
foreach (GroupPrincipal gTmp in a)
{
Console.WriteLine(gTmp.Name);
}
Related
I have created 2400 security groups and made a user member of all these security groups. Now I am trying to fetch all the security groups the user is member of using the DirectoryEntry.RefreshCache method. The method returns only 2050 groups.
$searcher = New-Object System.DirectoryServices.DirectorySearcher($null)
$searcher.SearchRoot = [ADSI]("LDAP://" + $gcName)
$searcher.Filter = "((msOnline-WindowsLiveNetId=XXXXXXXX))"
$searcher.PropertiesToLoad.AddRange(#("msOnline-UserPrincipalName"))
$sr = $searcher.FindOne()
$de= $sr.GetDirectoryEntry()
$de.RefreshCache(#("tokenGroups"))
$de.Properties["tokenGroups"].Count
I tried the range retrieval. Something like below. It still doesn't work.
$de.RefreshCache(#("tokenGroups"))
$count = $de.Properties["tokenGroups"].Count
$count
$de.RefreshCache(#("tokenGroups;range=$count-*"))
$de.Properties["tokenGroups"].Count
What's the best way to retrieve all the security groups of a user using the RefreshCache method?
I am using Okta as IDP and I have configured the user attribute statements and group attribute statements like this
And by providing a custom samluserdetails I am able to retrieve user attributes but not group attributes.
public class CustomSamlUserDetails implements SAMLUserDetailsService {
#Override
public Object loadUserBySAML(SAMLCredential cred) throws UsernameNotFoundException {
AppUser user = new AppUser();
user.setFirstName(cred.getAttributeAsString("firstName"));
user.setLastName(cred.getAttributeAsString("lastName"));
user.setLoginId(cred.getAttributeAsString("loginId"));
String groupname = cred.getAttributeAsString("role"); // comes null
return user;
}
}
Is there some config I missed or am I retrieving the group info in a wrong way?
EDIT:
If I use contains filter with some characters for example I have 3 groups test1, test2 and other1.
If I use contains filter *, I get null.
However if I use contains filter with test , I get test1 (and test2, if user is path of both groups).
Is wildchar not supported in case of groups?
What if in above case user was part of all 3 groups?
I am not an expert of OKTA but I statred working couple of weeks for one of my clients. I tested with * but it only worked for me for filter Regex. For other filters I never succeeded with *. For example the configuration without * worked perfectly for me.
OKTA CONFIG
I used the code String str = credential.getAttributeAsString("Groups");
But I have one issue that when I have more then one group I still get one group. I want to have list of groups instead.
EDIT - O6 Feb
Finally I restested and I am able to implement wildcard entry with regex I used the regex filter :
In java I got the groups as you suggested :
String[] str = credential.getAttributeAsStringArray("groups");
for(int i = 0; i< str.length; i++){
System.out.println(str[i]);
}
The result is :
Have a great day
I did some programming for reading the data from Active Directory such as user account or Orgnization info and so on. The code below is like something what I did.
DirectoryEntry entry = new DirectoryEntry(
"LDAP://CN=Users,DC=domain,DC=com",
null,
null,
AuthenticationTypes.Secure
);
DirectorySearcher search = new DirectorySearcher(entry);
using (SearchResultCollection src = search.FindAll())
{
foreach (SearchResult result in src)
{
Console.WriteLine(result.Properties["name"][0] + " : " +
result.Properties["department"][0]);
}
}
The problem is how can I know what properties that target objects have then I can use them to filter the data before get it all.
Any ideas?
If you have a DirectoryEntry, you can inspect its .SchemaEntry:
DirectoryEntry entry = new DirectoryEntry("LDAP://......");
DirectoryEntry schema = entry.SchemaEntry;
This should - if you have the necessary permissions - give you access to the properties defined in the schema - things like MandatoryProperties or OptionalProperties:
foreach (var prop in schema.Properties.PropertyNames)
{
string propName = prop.ToString();
var propValue = schema.Properties[propName].Value;
}
Does that help you get started??
You might also want to have a look at BeaverTail - my C# open-source LDAP browser.
(source: mvps.org)
It will allow you to inspect any LDAP node and see all its properties.
Is there a simple way to resolve the Active Directory path of a Domain name in Active Directory?
E.g. your user account might be SITE\Username or DEPARTMENT\Username but the actual path of the domain SITE might be site.company.com or DEPARTMENT might be dep.company.com etc
I'm trying to find a way of turning DEPARTMENT into DC=dep,DC=company,DC=com etc for searching correctly in Active Directory...
What you probably have is a company with one forrest (company.com) and a couple of child domains (SITE and DEPARTMENT in your case). The SITE- and DEPARTMENT-bits are the NetBIOS representations of the domains. Is not very common that they differ from the corresponding DNS names but it is possible. Just make sure we're not talking about OU:s and "physical" Active Directory sites.
Assuming the above, here are a couple of options:
The following assumes that your application is running under an account with "forrest-wide read access" (the ability to access your different domains):
using System.DirectoryServices.ActiveDirectory;
// ...
DirectoryContext directoryContext
= new DirectoryContext(DirectoryContextType.Domain, "DEPARTMENT");
Domain domain = Domain.GetDomain(directoryContext);
String domainName = domain.Name;
String domainComponent = "DC=" + domainName.Replace(".", ",DC=");
Console.WriteLine(domainComponent);
(I haven't found a "System.DirectoryServices-built-in" way to transform domain.company.com to DC=domain,DC=company,DC=com but the simple string manipulation above should do the trick.)
If you're using a non-forrest-wide account (or if DEPARTMENT and SITE are not domains in the same forrest but in themselves separate forrests), then you'll have to maintain a list of usernames and passwords to use depending on the "DEPARTMENT" or "SITE"-strings:
// if(domainNetBios == "DEPARMENT")...
DirectoryContext directoryContext
= new DirectoryContext(DirectoryContextType.Domain,
"DEPARTMENT",
"UserInDEPARTMENT",
"PassForUserInDEPARTMENT");
If you're not willing to bind to the different forrests/domains to get the domain name/component you could try something along the lines of:
IPAddress[] addresses = Dns.GetHostAddresses("DEPARTMENT");
IPHostEntry host = Dns.GetHostEntry(addresses[0]);
Int32 dotIndex = host.HostName.IndexOf('.');
String domain =
host.HostName.Substring(dotIndex + 1, host.HostName.Length - dotIndex - 1);
Console.WriteLine(domain);
But the above assumes that the NETBios-name is the same as the first part of the DNS-name and that DNS resolution is working correctly. What we're doing above is to query for a list of domain controllers then removing the hostnames from the DNS-names. Not a particularly clean option...
I've got an asp application running but i want to search the Active Directory.
i am using vb (visual web developer 2008)
how do i search the active directory for a given user?
ie: user enters login name in text box, clicks submit. active directory is searched on-click for this user. when found user information is displayed .
Thanks
What version of the .NET framework can you use? Searching and looking up stuff in AD has become extremely easy in .NET 3.5 - see this great MSDN article by Ethan Wilanski and Joe Kaplan on using the security principals API for that.
If you're not on .NET 3.5 yet, you'll have to use the DirectorySearcher class and set up the search filters as you need. Getting the LDAP filter right is probably the biggest obstacle.
Robbie Allen also has two great intro article on System.DirectoryServices programming:
- Part 1
- Part 2
There are some really good resources at http://www.directoryprogramming.net (Joe Kaplan's site - he's a Microsoft Active Directory MVP), and Richard Mueller has some great reference excel sheets on what properties are available for each of the ADSI providers, and what they mean, and how their LDAP name is - see http://www.rlmueller.net.
Marc
EDIT: Ok- here's the .NET 2.0 / 3.0 approach:
// set the search root - the AD container to search from
DirectoryEntry searchRoot = new DirectoryEntry("LDAP://dc=yourdomain,dc=com");
// create directory searcher
DirectorySearcher ds = new DirectorySearcher(searchRoot);
ds.SearchScope = SearchScope.Subtree;
// set the properties to load in the search results
// the fewer you load, the better your performance
ds.PropertiesToLoad.Add("cn");
ds.PropertiesToLoad.Add("sn");
ds.PropertiesToLoad.Add("givenName");
ds.PropertiesToLoad.Add("mail");
// set the filter - here I'm using objectCategory since this attribute is
// single-valued and indexed --> much better than objectClass in performance
// the "anr" is the "ambiguous name resolution" property which basically
// searches for all normally interesting name properties
ds.Filter = "(&(objectCategory=person)(anr=user-name-here))";
// get the result collection
SearchResultCollection src = ds.FindAll();
// iterate over the results
foreach (SearchResult sr in src)
{
// do whatever you need to do with the search result
// I'm extracting the properties I specified in the PropertiesToLoad
// mind you - a property might not be set in AD and thus would
// be NULL here (e.g. not included in the Properties collection)
// also, all result properties are really multi-valued, so you need
// to do this trickery to get the first of the values returned
string surname = string.Empty;
if (sr.Properties.Contains("sn"))
{
surname = sr.Properties["sn"][0].ToString();
}
string givenName = string.Empty;
if (sr.Properties.Contains("givenName"))
{
givenName = sr.Properties["givenName"][0].ToString();
}
string email = string.Empty;
if (sr.Properties.Contains("mail"))
{
email = sr.Properties["mail"][0].ToString();
}
Console.WriteLine("Name: {0} {1} / Mail: {2}", givenName, surname, email);
}
Hope this helps!
Marc