Have a scenario to verify logged in User is belong to AD Group mentioned in web.config. The code used below is working fine, but taking lot of time when looping through. (Some case User has relation of 100+ group in AD)string.
Is there a way to achieve this with direct method for better performance.
Note: I have tried Contains() method of Group obj, but could not pass string as input.
allowedRoles = "Role1, Role2"; //Read from web.config
string[] allowedRolesList = allowedRoles.Split(',');
PrincipalContext ctx = new PrincipalContext(ContextType.Domain, UserDomain);
UserPrincipal user = UserPrincipal.FindByIdentity(ctx,IdentityType.SamAccountName,SLID);
if (user != null)
{
var groups = user.GetAuthorizationGroups();
foreach (string allowedrole in allowedRolesList)
{
foreach (GroupPrincipal group in groups)
{
if (group.Name.ToLower() == allowedrole.ToLower())
{
return true;
}
}
}
}
Related
I have been searching around for a solution on getting both users and contacts from a group in Active Directory, but cant find one.
I understand that I cant get contacts the same way as users because they are not security principals?
I use this code to get all users in my group, is it possible to extend this to retrieve name, and mobile number from contacts? Or do I need to write something new?
var context = new PrincipalContext(ContextType.Domain, "MY_DOMAIN");
using (var searcher = new PrincipalSearcher())
{
var groupName = "MY_GROUP";
var sp = new GroupPrincipal(context, groupName);
searcher.QueryFilter = sp;
var group = searcher.FindOne() as GroupPrincipal;
if (group == null)
Console.WriteLine("Invalid Group Name: {0}", groupName);
foreach (var f in group.GetMembers())
{
var principal = f as UserPrincipal;
if (principal == null || string.IsNullOrEmpty(principal.Name))
continue;
DirectoryEntry entry = (principal.GetUnderlyingObject() as DirectoryEntry);
DirectorySearcher entrySearch = new DirectorySearcher(entry);
entrySearch.PropertiesToLoad.Add("mobile");
entrySearch.PropertiesToLoad.Add("sAMAccountName");
entrySearch.PropertiesToLoad.Add("name");
SearchResultCollection results = entrySearch.FindAll();
ResultPropertyCollection rpc = results[0].Properties;
foreach (string rp in rpc.PropertyNames)
{
if (rp == "mobile")
Console.WriteLine(rpc["mobile"][0].ToString());
if(rp == "sAMAccountName")
Console.WriteLine(rpc["sAMAccountName"][0].ToString());
}
You cannot use the System.DirectoryServices.AccountManagement namespace to query contact information from Active Directory because as you point out, they are not security principles. You'll need to read and parse the member property of the group directly from the group's DirectoryEntry. This will be a list of distinguished names of all the objects which are a member of the group. There's no way to know from this what kind of object they are so you'll need to query AD for each to find out.
You have all the code needed to accomplish this already in what you posted, just add the member property to the load list and then loop though it loading new DirectoryEntry objects. The objectClass property will tell you if it's a user, group or contact.
Is there a way to get all active users in multiple groups?
For example:
Get all active users in "AdGroupA" OR "AdGroupB" OR "AdGroupC"
I have see post about single group but not multiple groups.
Thanks.
If I understand you correctly, you just want to return the entire list of users in multiple groups? That should be as easy as getting the users from the single group multiple times.
public IEnumerable<UserPrincipal> GetUsersFromGroups(string[] groupNames)
{
using (var ctx = new PrincipalContext(ContextType.Domain))
{
foreach (var groupName in groupNames)
{
foreach (var userPrincipal in GroupPrincipal.FindByIdentity(ctx, groupName)
.GetMembers())
{
yield return userPrincipal;
}
}
}
}
Here is a method not using AccountManagement:
using System.DirectoryServices;
public static IEnumerable<DirectoryEntry> GetUsersFromGroups(string[] groupNames)
{
if (groupNames.Length > 0)
{
var searcher = new DirectorySearcher();
string searchFilter = "(&(objectClass=Group)"; //filter for groups
searchFilter += "(|"; //start a group of or parameters
foreach (var group in groupNames) // loop through the group names
{
searchFilter += string.Format("(SAMAccountName={0})",group); //add a parameter for each group in the list
}
searchFilter += "))"; //close off the filter string
searcher.Filter = searchFilter; //add the filter to the searcher
searcher.PropertiesToLoad.Add("member"); // load the members property for the group
var searchResults = searcher.FindAll(); // perform the search
foreach (SearchResult result in searchResults)
{
var directoryEntry = (DirectoryEntry)result.GetDirectoryEntry(); // get the directory entry for the group
PropertyValueCollection members = directoryEntry.Properties["member"]; // get the members collection
foreach (string name in members) // iterate over the members. This string will be the distinguished name
{
yield return new DirectoryEntry(string.Format("LDAP://{0}",name)); //return the directory entry. You may get the entry and return the display name or just return distinguished name.
}
}
}
}
In my environment I found this to be about 25% faster on average than using DirectoryServices.AccountManagement for 1 group, but as the count of groups and users increased the AccountManagement method actually became faster. This only queries AD once while the first method queries once per group.
I'm trying to export members of specific AD groups. I have a working solution to get ALL and filter, but that seems excessive if the group I want has 5 out of 1000 possible users..
I'm working in this direction:
public void PrintMembers(string groupname, string domain)
{
GroupPrincipal group = GroupPrincipal.FindByIdentity(new PrincipalContext(ContextType.Domain, domain), groupname);
foreach (Principal princ in group.Members)
{
if (princ.StructuralObjectClass == "user")
{
Response.Write(UserPrincipal.FindByIdentity(new PrincipalContext(ContextType.Domain, domain), princ.Name));
}
}
}
This sort of works, but fails to give members that have inherited the membership through an underlying group.
So:
"Specific group 1" = I get all 5 members alright
"Specific group 2" = I get all 7 members alright
"Mother group", that holds the two groups above = I get no members...
I could iterate that groups subgroups, but feel there must be another way....
Any suggestions?
Anything wrong with GetMembers(true)?
static void Main(string[] args)
{
foreach (string user in GetMemberNames("My Group", "domain.local"))
{
Console.WriteLine(user);
}
Console.ReadKey();
}
public static string[] GetMemberNames(string groupname, string domain)
{
using (PrincipalContext context = new PrincipalContext(ContextType.Domain, domain))
using (GroupPrincipal group = GroupPrincipal.FindByIdentity(context, groupname))
using (PrincipalSearchResult<Principal> results = group.GetMembers(true))
{
return results.OfType<UserPrincipal>().Select(u => u.SamAccountName).ToArray();
}
}
First of all : #shriop point the exact answer to your question.
As far as the bounty is concerned that is to say : "A solution which uses the LDAP protocol to enumerate the users in a group and it's subgroups without recursion". Here is something working withe Active-Directory begining Windows Server 2003 SP2 and called LDAP_MATCHING_RULE_IN_CHAIN. It Search recursively (but in one query) all the users from a group (be careful it return users from security and distribution groups). Here is the ADSI usage in C# :
static void Main(string[] args)
{
/* Connection to Active Directory
*/
string sFromWhere = "LDAP://SRVENTR2:389/dc=societe,dc=fr";
DirectoryEntry deBase = new DirectoryEntry(sFromWhere, "societe\\administrateur", "test.2011");
/* To find all the users member of groups "Grp1" :
* Set the base to the groups container DN; for example root DN (dc=societe,dc=fr)
* Set the scope to subtree
* Use the following filter :
* (member:1.2.840.113556.1.4.1941:=CN=Grp1,OU=MonOu,DC=X)
*/
DirectorySearcher dsLookFor = new DirectorySearcher(deBase);
dsLookFor.Filter = "(&(memberof:1.2.840.113556.1.4.1941:=CN=Grp1,OU=MonOu,DC=societe,DC=fr)(objectCategory=user))";
dsLookFor.SearchScope = SearchScope.Subtree;
dsLookFor.PropertiesToLoad.Add("cn");
dsLookFor.PropertiesToLoad.Add("samAccountName");
SearchResultCollection srcUsers = dsLookFor.FindAll();
/* Just show each user
*/
foreach (SearchResult srcUser in srcUsers)
{
Console.WriteLine("{0}", srcUser.Path);
Console.WriteLine("{0}", srcUser.Properties["samAccountName"][0]);
}
Console.ReadLine();
}
I have a question about determining the type (User or Group) of a account name.
For example, I have two strings, say "Adventure-works\david" and "Adventure-works\admins",
the first represents a user named david, and the second represents an AD group.
My question is how can I determin the type(User or AD group) of these account? Are there convenient method I can use?
Any comments are appreciated.
Thanks.
What version of .NET are you on??
If you're on .NET 3.5, see this excellent MSDN article on how the Active Directory interface has changed quite a bit.
If you're on .NET 3.5, you could write:
PrincipalContext ctx = new PrincipalContext(ContextType.Domain, "YOURDOMAIN");
Principal myObject = Principal.FindByIdentity(ctx, "your name value");
Typically, you'd have to pass in just the user name - the part after the backslash - not the whole DOMAIN\USERNAME string.
This "Principal" now either is a UserPrincipal or a GroupPrincipal (or it could some other type of principal, e.g. ComputerPrincipal):
if(myObject is UserPrincipal)
{
// you have a user
}
else if(myObject is GroupPrincipal)
{
// you have a group
}
and you can go on from there.
If you're on .NET 1.x/2.0/3.0, you'd have to use the slightly more involved procedure of creating a DirectorySearcher and searching for your object:
// create root DirectoryEntry for your search
DirectoryEntry deRoot = new DirectoryEntry("LDAP://dc=YourCompany,dc=com");
// create searcher
DirectorySearcher ds = new DirectorySearcher(deRoot);
ds.SearchScope = SearchScope.Subtree;
// define LDAP filter - all you can specify is the "anr" (ambiguous name
// resolution) attribute of the object you're looking for
ds.Filter = string.Format("(anr={0})", "YourNameValue");
// define properties you want in search result(s)
ds.PropertiesToLoad.Add("objectCategory");
ds.PropertiesToLoad.Add("displayName");
// search
SearchResult sr = ds.FindOne();
// check if we get anything back, and if we can check the "objectCategory"
// property in the search result
if (sr != null)
{
if(sr.Properties["objectCategory"] != null)
{
// objectType will be "Person" or "Group" (or something else entirely)
string objectType = sr.Properties["objectCategory"][0].ToString();
}
}
Marc
Warning: In case of using DirectorySearcher the accepted answer might fail, since objectCategory it doesn't return consistent results.
Consider using objectClass instead:
SearchResult sr = ds.FindOne();
bool isUser = sr.Properties["objectClass"]?.Contains("user") == true;
// OR
bool isGroup = sr.Properties["objectClass"]?.Contains("group") == true;
using System.DirectoryServices.AccountManagement;
...
..
Principal myPrincipal = Principal.FindByIdentity(ctx, "your name value");
if (myPrincipal.GetType() == typeof(GroupPrincipal)) {
GroupPrincipal myGroup = (GroupPrincipal)myPrincipal;
} else {
UserPrincipal myUser = (UserPrincipal)myPrincipal;
}
I have code that searches for all users in a department:
string Department = "Billing";
DirectorySearcher LdapSearcher = new DirectorySearcher();
LdapSearcher.PropertiesToLoad.Add("displayName");
LdapSearcher.PropertiesToLoad.Add("cn");
LdapSearcher.PropertiesToLoad.Add("department");
LdapSearcher.PropertiesToLoad.Add("title");
LdapSearcher.PropertiesToLoad.Add("memberOf");
LdapSearcher.Filter = string.Format("(&(objectClass=user)(department={0}))", Department);
SearchResultCollection src = LdapSearcher.FindAll();
What would the filter need to look like if I only wanted everyone in the "Manager Read Only" AD Group?
Am I going about this all wrong?
Looking at your search I have a couple of points for you. First, the search uses objectClass (non-indexed) instead of objectCategory (indexed). Huge performance issue with that query. You would most always want to combine the two together depending on what you are trying to retrieve:
(&(objectCategory=person)(objectClass=user)) = All users (no contacts)
(&(objectCategory=person)(objectClass=contact)) = All contacts (no users)
(&(objectCategory=person)) = All users and contacts
As for looking up the users in a group you can enumerate the list of member objects of the specific group. In the member attribute of the group object is the distinguishedName of each user.
This article describes enumerating members of a group...
Don't forget that you may have to handle nested groups of the parent group, as there isn't a default way to handle this with LDAP queries. For that you may need to evaluate if the member object is a group and then get the member attribute for that child group.
Lastly, you should get in the habit of specifying a dns prefix to your query.
Without DNS prefix:
LDAP://ou=ouname,dc=domain,dc=com
With DNS prefix (all three work):
LDAP://servername/ou=ouname,dc=domain,dc=com
LDAP://servername.domain.com/ou=ouname,dc=domain,dc=com
LDAP://domain.com/ou=ouname,dc=domain,dc=com
A single domain won't cause you much issue but when you try and run a search in a multiple domain environment you will get bitten without this addition. Hope this helps move you closer to your goal.
I've always found Howto: (Almost) Everything In Active Directory via C# helps for most AD questions.
If you know the AD path to the group already it would probably be easier to open a DirectoryEntry on that, then do a DirectorySearcher from there.
using (DirectoryEntry de = new DirectoryEntry("LDAP://somedomain/CN=FooBar"))
{
DirectorySearcher search = new DirectorySearcher(de, ("(objectClass=user)"));
}
There is also a flag on the Searcher for whether to drill down to sub containers, I forget the name off hand.
I use following code (from http://blogs.technet.com/b/brad_rutkowski/archive/2008/04/15/c-getting-members-of-a-group-the-easy-way-with-net-3-5-discussion-groups-nested-recursive-security-groups-etc.aspx) it works fine.
IList<string> getMembers(string domainName, string groupName)
{
PrincipalContext ctx = new PrincipalContext(ContextType.Domain, domainName);
GroupPrincipal grp = GroupPrincipal.FindByIdentity(ctx, IdentityType.Name, groupName);
if (grp == null) {
throw new ApplicationException("We did not find that group in that domain, perhaps the group resides in a different domain?");
}
IList<string> members = new List<String>();
foreach (Principal p in grp.GetMembers(true))
{
members.Add(p.Name); //You can add more attributes, samaccountname, UPN, DN, object type, etc...
}
grp.Dispose();
ctx.Dispose();
return members;
}
//Search for Group and list group members
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.DirectoryServices.AccountManagement;
namespace ExportActiveDirectoryGroupsUsers
{
class Program
{
static void Main(string[] args)
{
if (args == null)
{
Console.WriteLine("args is null, useage: ExportActiveDirectoryGroupsUsers OutputPath"); // Check for null array
}
else
{
Console.Write("args length is ");
Console.WriteLine(args.Length); // Write array length
for (int i = 0; i < args.Length; i++) // Loop through array
{
string argument = args[i];
Console.Write("args index ");
Console.Write(i); // Write index
Console.Write(" is [");
Console.Write(argument); // Write string
Console.WriteLine("]");
}
try
{
using (var ServerContext = new PrincipalContext(ContextType.Domain, ServerAddress, Username, Password))
{
/// define a "query-by-example" principal - here, we search for a GroupPrincipal
GroupPrincipal qbeGroup = new GroupPrincipal(ServerContext, args[0]);
// create your principal searcher passing in the QBE principal
PrincipalSearcher srch = new PrincipalSearcher(qbeGroup);
// find all matches
foreach (var found in srch.FindAll())
{
GroupPrincipal foundGroup = found as GroupPrincipal;
if (foundGroup != null)
{
// iterate over members
foreach (Principal p in foundGroup.GetMembers())
{
Console.WriteLine("{0}|{1}", foundGroup.Name, p.DisplayName);
// do whatever you need to do to those members
}
}
}
}
//Console.WriteLine("end");
}
catch (Exception ex)
{
Console.WriteLine("Something wrong happened in the AD Query module: " + ex.ToString());
}
Console.ReadLine();
}
}
}
}