Using Active Directory to determine what is displayed on a page - c#

I am rather new to C#. I want to display one set of fields if the user who is logged in is a member of the Administration Board and another set of fields if the user is not a member of the Administration Board.
The manager has instructed me to use Active Directory to do this.
Is there an example someone can point me to that I can review?
Is there a way for me to write a page (just for my own review) that will display all the user groups this organization has in Active Directory?

Here is one way of doing an Active Directory query that gets all domain users, a specific user's groups, and whether a user belongs to a group:
public static List<string> DomainUsers
{
get
{
List<string> users = new List<string>();
using (PrincipalContext ctx = new PrincipalContext(ContextType.Domain, "domain"))
{
// find user by display name
UserPrincipal user = new UserPrincipal(ctx);
PrincipalSearcher search = new PrincipalSearcher(user);
search.FindAll().Cast<UserPrincipal>().ToList().ForEach(u => users.Add(u.SamAccountName));
}
return users;
}
}
/// <summary>
/// Gets all associated group names for current user on the current domain
/// </summary>
/// <returns></returns>
public static List<string> GetGroupNames(string username)
{
var pc = new PrincipalContext(ContextType.Domain, "domain");
var src = UserPrincipal.FindByIdentity(pc, username).GetGroups(pc);
var result = new List<string>();
src.ToList().ForEach(sr => result.Add(sr.SamAccountName));
return result;
}
public static bool UserBelongsToGroup(string group)
{
PrincipalContext pc = new PrincipalContext((Environment.UserDomainName == Environment.MachineName ? ContextType.Machine : ContextType.Domain), Environment.UserDomainName);
GroupPrincipal gp = GroupPrincipal.FindByIdentity(pc, group);
UserPrincipal up = UserPrincipal.FindByIdentity(pc, Environment.UserName);
return up.IsMemberOf(gp);
}
Please note that you will need to use the System.DirectoryServices.AccountManagement namespace which can be found in the .NET 4.0 framework assemblies when adding a reference. You will need to be targetting .NET 4.0 to use this namespace.

Use IsInRole() method. It is in System.Principle namespace - as far as I remember.

Related

Search Users in Specific OU Active Directory

I have different OU in my Active Directory for different users, I want to get all users of a specific OU using C#.
Currently I have this filter, but it returns all users from all OU
(&(objectClass=User)(objectCategory=Person))
Kindly help me in finding users of specific user using ldap
You can use a PrincipalSearcher and a "query-by-example" principal to do your searching:
// LDAP string to define your OU
string ou = "OU=Sales,DC=YourCompany,DC=com";
// set up a "PrincipalContext" for that OU
using (PrincipalContext ctx = new PrincipalContext(ContextType.Domain, "Yourcompany.com", ou))
{
// define the "query-by-example" user (or group, or computer) for your search
UserPrincipal qbeUser = new UserPrincipal(ctx);
// set whatever attributes you want to limit your search for, e.g. Name, etc.
qbeUser.Surname = "Smith";
// define a searcher for that context and that query-by-example
using (PrincipalSearcher searcher = new PrincipalSearcher(qbeUser))
{
foreach (Principal p in searcher.FindAll())
{
// Convert the "generic" Principal to a UserPrincipal
UserPrincipal user = p as UserPrincipal;
if (user != null)
{
// do something with your found user....
}
}
}
If you haven't already - absolutely read the MSDN article Managing Directory Security Principals in the .NET Framework 3.5 which shows nicely how to make the best use of the new features in System.DirectoryServices.AccountManagement. Or see the MSDN documentation on the System.DirectoryServices.AccountManagement namespace.
Of course, depending on your need, you might want to specify other properties on that "query-by-example" user principal you create:
DisplayName (typically: first name + space + last name)
SAM Account Name - your Windows/AD account name
User Principal Name - your "username#yourcompany.com" style name
You can specify any of the properties on the UserPrincipal and use those as "query-by-example" for your PrincipalSearcher.
One option is to just set the organization unit (OU) when you create your DirectoryEntry object:
using (var entry = new DirectoryEntry($"LDAP://OU={unit},OU=Accounts,DC={domain},DC=local"))
{
// Setup your search within the directory
var search = new DirectorySearcher(entry)
{
Filter = "(&(objectCategory=person)(objectClass=user)(memberOf=*))"
};
// Set the properties to be returned
search.PropertiesToLoad.Add("SamAccountName");
// Get the results
var results = search.FindAll();
// TODO Process the results as needed...
}

List users in active directory domain AND subdomain

We have an AD with users in "mydomain.com" and users in "child.mydomain.com". When We try to list them, we can only find the "mydomain.com"'s users and groups, but we also need those from the child domain. How can I achieve this using C# ? Please take a look to my sample code :
context = new PrincipalContext(ContextType.Domain);
//...
var filter = new GroupPrincipal(context);
filter.IsSecurityGroup = true;
using(var searcher = new PrincipalSearcher(filter)
using(var results = searcher.FindAll())
{
foreach(GroupPrincipal group in results)
{
string path = "LDAP://rootDSE";
DirectoryEntry searchRoot = new DirectoryEntry(path);
string configNC = searchRoot.Properties["configurationNamingContext"].Value.ToString();
DirectoryEntry configSearchRoot = new DirectoryEntry("LDAP://" + configNC);
DirectorySearcher configSearch = new DirectorySearcher(configSearchRoot);
configSearch.Filter("(NETBIOSName=*)");
configSearch.PropertiesToLoad.Add("dnsroot");
configSearch.PropertiesToLoad.Add("ncname");
configSearch.PropertiesToLoad.Add("NETBIOSName");
SearchResultCollection forestPartitionList = configSearch.FindAll();
List<Tuple<string,string>> netbiosNameList = new List<Tuple<string,string>>(forestPartitionList.Count);
foreach(SearchResult domainPartition in forestPartitionList)
{
string ncname = domainPartition.Properties["ncname"][0].ToString();
string netBIOSName = domainPartition.Properties["NETBIOSName"][0].ToString();
netbiosNameList.Add(Tuple.Create(ncname, netBIOSName));
}
//...
//Find group members
using (var principal = GroupPrincipal.FindByIdentity(context, IdentityType.DistinguishedName, group.DistinguishedName))
using (var members = principal.GetMembers(true))
using (var enumerator = members.GetEnumerator())
{
//...
}
}
}
The code is not exactly written this way, I just want to show you the main calls that are made to query the AD. We can list the parent domain groups and users but not the child domain ones. If I change the initialization of my "context" variable passing the child domain IP and user/password, I can list the groups and users in it. But we want to be able to do so while being in the parent domain.
I hope you can help me. Thanks a lot!
You can query the global catalog.
It contains a read-only, searchable, partial representation of every object in every domain in a multidomain Active Directory forest.
The GC operates on port 3268 ( standard ldap ) and 3269 ( SSL ldap ). Simply connect to any of your domain controllers on one of the above two ports and your search will be automatically directed to the GC server.
To perform any modifications, though, you will have to send such request to a domain controller for that particular domain the object belongs to.

Active Directory RoleProvider - Principal.IsMemberOf throws PrincipalOperationException

I have created the following method in a custom Active Directory RoleProvider:
public override string[] GetRolesForUser(string username)
{
ArrayList results = new ArrayList();
using (var principalContext = new PrincipalContext(
ContextType.Domain, null, domainContainer))
{
var user = UserPrincipal.FindByIdentity(
principalContext, IdentityType.SamAccountName, username);
foreach (string acceptibleGroup in GroupsToInclude)
{
GroupPrincipal adGroup = GroupPrincipal.FindByIdentity(
principalContext, acceptibleGroup);
if (user.IsMemberOf(adGroup))
results.Add(acceptibleGroup);
}
}
return results.ToArray(typeof(string)) as string[];
}
It only checks against a white list of roles which are used in my application. The problem is that if the user is not a member of one of the roles, I get a PrincipalOperationException when the
if (user.IsMemberOf(adGroup))
line is executed. I would expect this to simply return `false if the user is not in the group. What is going wrong here?
EDIT:
As and aside, if I call user.GetAuthorizationGroups() and attempt to loop through the results, I get a COMException - The specified directory service attribute or value does not exist.
Both Principal.IsMemberOf() and user.GetAuthorizationGroups() are using tokenGroups attribute to determine the group membership.
You need to make sure the account you used to run the program is added to Builtin\Windows Authorization Access Group in order to access tokenGroups attribute.
See this MSDN KB for more details.
I have managed to work around this problem with the following:
public override string[] GetRolesForUser(string username)
{
ArrayList results = new ArrayList();
using (PrincipalContext principalContext = new PrincipalContext(ContextType.Domain, null, domainContainer))
{
UserPrincipal user = UserPrincipal.FindByIdentity(principalContext, IdentityType.SamAccountName, username);
foreach (string acceptibleGroup in GroupsToInclude)
{
GroupPrincipal p = GroupPrincipal.FindByIdentity(principalContext, IdentityType.SamAccountName, acceptibleGroup);
if (p.GetMembers().Contains(user))
results.Add(acceptibleGroup);
}
}
return results.ToArray(typeof(string)) as string[];
}
However it is not exactly efficient as it pulls all the members of a group back. I am sure there is a better solution to my problem and hopefully someone will post it here!

How to determine all the groups a user belongs to (including nested groups) in ActiveDirectory and .NET 3.5

I have an application that uses ActiveDirecotry authorisation and it has been decided that it needs to support nested AD groups, e.g.:
MAIN_AD_GROUP
|
|-> SUB_GROUP
|
|-> User
So, the user in not directly a member of MAIN_AD_GROUP. I'd like to be able to look for the user recursively, searching the groups nested in MAIN_AD_GROUP.
The main problem is that I'm using .NET 3.5 and there is a bug in System.DirectoryServices.AccountManagement in .NET 3.5 whereby the method UserPrincipal.IsMemberOf() will not work for groups with more than 1500 users. So I can't use UserPrincipal.IsMemberOf() and no, I can't switch to .NET 4 either.
I've worked around this last problem with the following function:
private bool IsMember(Principal userPrincipal, Principal groupPrincipal)
{
using (var groups = userPrincipal.GetGroups())
{
var isMember = groups.Any(g =>
g.DistinguishedName == groupPrincipal.DistinguishedName);
return isMember;
}
}
But userPrincipal.GetGroups() only returns the groups of which the user is a direct member.
How can I get this to work with nested groups?
Workaround #1
This bug is reported here at Microsoft Connect along with the following code that works around this issue by manually iterating through the PrincipalSearchResult<Principal> returned objects, catching this exception, and continuing on:
PrincipalSearchResult<Principal> groups = user.GetAuthorizationGroups();
var iterGroup = groups.GetEnumerator();
using (iterGroup)
{
while (iterGroup.MoveNext())
{
try
{
Principal p = iterGroup.Current;
Console.WriteLine(p.Name);
}
catch (NoMatchingPrincipalException pex)
{
continue;
}
}
}
Workaround #2
Another workaround found here avoids the AccountManagement class, and uses the System.DirectoryServices API instead:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.DirectoryServices;
namespace GetGroupsForADUser
{
class Program
{
static void Main(string[] args)
{
String username = "Gabriel";
List<string> userNestedMembership = new List<string>();
DirectoryEntry domainConnection = new DirectoryEntry(); // Use this to query the default domain
//DirectoryEntry domainConnection = new DirectoryEntry("LDAP://example.com", "username", "password"); // Use this to query a remote domain
DirectorySearcher samSearcher = new DirectorySearcher();
samSearcher.SearchRoot = domainConnection;
samSearcher.Filter = "(samAccountName=" + username + ")";
samSearcher.PropertiesToLoad.Add("displayName");
SearchResult samResult = samSearcher.FindOne();
if (samResult != null)
{
DirectoryEntry theUser = samResult.GetDirectoryEntry();
theUser.RefreshCache(new string[] { "tokenGroups" });
foreach (byte[] resultBytes in theUser.Properties["tokenGroups"])
{
System.Security.Principal.SecurityIdentifier mySID = new System.Security.Principal.SecurityIdentifier(resultBytes, 0);
DirectorySearcher sidSearcher = new DirectorySearcher();
sidSearcher.SearchRoot = domainConnection;
sidSearcher.Filter = "(objectSid=" + mySID.Value + ")";
sidSearcher.PropertiesToLoad.Add("distinguishedName");
SearchResult sidResult = sidSearcher.FindOne();
if (sidResult != null)
{
userNestedMembership.Add((string)sidResult.Properties["distinguishedName"][0]);
}
}
foreach (string myEntry in userNestedMembership)
{
Console.WriteLine(myEntry);
}
}
else
{
Console.WriteLine("The user doesn't exist");
}
Console.ReadKey();
}
}
}
Use UserPrincipal.GetAuthorizationGroups() instead - from its MSDN docs:
This method searches all groups
recursively and returns the groups in
which the user is a member. The
returned set may also include
additional groups that system would
consider the user a member of for
authorization purposes.
The groups that are returned by this
method may include groups from a
different scope and store than the
principal. For example, if the
principal is an AD DS object that has
a DN of
"CN=SpecialGroups,DC=Fabrikam,DC=com,
the returned set can contain groups
that belong to the
"CN=NormalGroups,DC=Fabrikam,DC=com.
I know this is an old thread, but it's the top result on Google, so in case this helps anyone, here's what I came up with that uses the AccountManagement stuff, but makes this particular query much easier.
public static class AccountManagementExtensions
{
public static bool IsNestedMemberOf(this Principal principal, GroupPrincipal group)
{
// LDAP Query for memberOf Nested
var filter = String.Format("(&(sAMAccountName={0})(memberOf:1.2.840.113556.1.4.1941:={1}))",
principal.SamAccountName,
group.DistinguishedName
);
var searcher = new DirectorySearcher(filter);
var result = searcher.FindOne();
return result != null;
}
}
The efficient way is to do a single AD query by having the right DirectorySearcher filter for e.g.
public bool CheckMemberShip(string userName)
{
bool membership = false;
string connection = "LDAP://"+YOURDOMAIN;
DirectoryEntry entry = new DirectoryEntry(connection);
DirectorySearcher mySearcher = new DirectorySearcher(entry);
mySearcher.Filter = "(&(objectClass=user)(memberOf:1.2.840.113556.1.4.1941:=cn=GROUPNAME,OU=Groups,OU=ABC,OU=ABC,OU=IND,DC=ad,DC=COMPANY,DC=com)(|(sAMAccountName=" + userName + ")))";
SearchResult result = mySearcher.FindOne();
// No search result, hence no membership
if (result == null)
{
membership = false;
}
entry.Close();
entry.Dispose();
mySearcher.Dispose();
membership = true;
return membership;
}
You need to replace YOURDOMAIN and GROUPNAME with right values from your AD.
Source : How to Recursively Get the Group Membership of a User in Active Directory using .NET/C# and LDAP (without just 2 hits to Active Directory)
Need to include using System.DirectoryServices;

Query From LDAP for User Groups

How To Get User group of user from LDAP active directory in C# .NET for ASP. In my Scenario I want to Pass user name to method which query from LDAP Active directory and tell me my user is Member of This User Groups. Please help me in this
If you're on .NET 3.5 or newer, you can also use the new System.DirectoryServices.AccountManagement (S.DS.AM) namespaces.
With this, you can do something like:
// create context for domain
PrincipalContext ctx = new PrincipalContext(ContextType.Domain);
// find the user
UserPrincipal up = UserPrincipal.FindByIdentity(ctx, "YourUserName");
if(up != null)
{
// get groups for that user
var authGroups = up.GetAuthorizationGroups();
}
Read more about the new S.DS.AM namespace:
Managing Directory Security Principals in the .NET Framework 3.5
Look into using the System.DirectoryServices namespace. You can use a DirectorySearcher to find the user. Once you have the DirectoryEntry object for that user do this:
public List<string> GetMemberOf(DirectoryEntry de)
{
List<string> memberof = new List<string>();
foreach (object oMember in de.Properties["memberOf"])
{
memberof.Add(oMember.ToString());
}
return memberof;
}
This will return a list of strings which are the group names the user is a member of.
Of course you could further refine this to include the DirectorySearcher code so you can just pass the function the samAccountName.
try this...
public override string[] GetRolesForUser(string username)
{
var allRoles = new List<string>();
var root = new DirectoryEntry(WebConfigurationManager.ConnectionStrings[ConnectionStringName].ConnectionString,
ConnectionUsername,
ConnectionPassword);
var searcher = new DirectorySearcher(root,
string.Format(CultureInfo.InvariantCulture, "(&(objectClass=user)({0}={1}))",
AttributeMapUsername,
username));
searcher.PropertiesToLoad.Add("memberOf");
SearchResult result = searcher.FindOne();
if (result != null && !string.IsNullOrEmpty(result.Path))
{
DirectoryEntry user = result.GetDirectoryEntry();
PropertyValueCollection groups = user.Properties["memberOf"];
foreach (string path in groups)
{
string[] parts = path.Split(',');
if (parts.Length > 0)
{
foreach (string part in parts)
{
string[] p = part.Split('=');
if (p[0].Equals("cn", StringComparison.OrdinalIgnoreCase))
{
allRoles.Add(p[1]);
}
}
}
}
}
return allRoles.ToArray();
}
Use the DirectorySearcher class to preform an ldap query.
For reference:
http://www.codeproject.com/KB/system/QueryADwithDotNet.aspx
I needed a method of authenticating a user and a check to see if they were in a specific user group. I did it by pushing the username and password and loading the "memberOf" property into the 'search' instance. Example below will display all the groups for that specific user name. The 'catch' statement will trap a wrong user name or password.
DirectoryEntry entry = new DirectoryEntry("LDAP://xxxxxxxx/OU=xxxxxxx,DC=xxxxxx,DC=xxxxx,DC=xxxxxx", strLdapUserName, strLdapPassword);
try
{
//the object is needed to fire off the ldap connection
object obj = entry.NativeObject;
DirectorySearcher search = new DirectorySearcher(entry);
search.Filter = "(SAMAccountName=" + strLdapUserName + ")";
search.PropertiesToLoad.Add("memberOf");
SearchResult result = search.FindOne();
string filterAttribute = (String)result.Properties["cn"][0];
foreach(string groupMemberShipName in result.Properties["memberOf"])
{
Console.WriteLine("Member of - {0}", groupMemberShipName);
}
}
catch (Exception ex)
{
//failed to authenticate
throw new Exception(ex.ToString());
}
Hope this helps. (Remember to reference System.DirectoryServices)
I think most methods listed above should work, but i would suggest adding code to ensure that your code can "detect circular loops in nested group memberships", and if found, break any infinite loops that your script of choice could potentially get into.

Categories

Resources