this postを既にチェックしました。しかし、それは私の質問には答えません。特定のユーザーがメンバーになっているすべてのActive Directoryグループを取得したい。
次のコードを書きました。しかし、フィルターの指定方法やプロパティへのアクセス方法がわからないため、これ以上先に進むことはできません。
class Program
{
static void Main(string[] args)
{
DirectoryEntry de = new DirectoryEntry("LDAP://mydomain.com");
DirectorySearcher searcher = new DirectorySearcher(de);
searcher.Filter = "(&(ObjectClass=group))";
searcher.PropertiesToLoad.Add("distinguishedName");
searcher.PropertiesToLoad.Add("sAMAccountName");
searcher.PropertiesToLoad.Add("name");
searcher.PropertiesToLoad.Add("objectSid");
SearchResultCollection results = searcher.FindAll();
int i = 1;
foreach (SearchResult res in results)
{
Console.WriteLine("Result" + Convert.ToString(i++));
DisplayProperties("distinguishedName", res);
DisplayProperties("sAMAccouontName", res);
DisplayProperties("name", res);
DisplayProperties("objectSid", res);
Console.WriteLine();
}
Console.ReadKey();
}
private static void DisplayProperties(string property, SearchResult res)
{
Console.WriteLine("\t" + property);
ResultPropertyValueCollection col = res.Properties[property];
foreach (object o in col)
{
Console.WriteLine("\t\t" + o.ToString());
}
}
}
何か案は?
「memberOf」プロパティを照会し、戻り値を反復処理します。例:
search.PropertiesToLoad.Add("memberOf");
StringBuilder groupNames = new StringBuilder(); //stuff them in | delimited
SearchResult result = search.FindOne();
int propertyCount = result.Properties["memberOf"].Count;
String dn;
int equalsIndex, commaIndex;
for (int propertyCounter = 0; propertyCounter < propertyCount;
propertyCounter++)
{
dn = (String)result.Properties["memberOf"][propertyCounter];
equalsIndex = dn.IndexOf("=", 1);
commaIndex = dn.IndexOf(",", 1);
if (-1 == equalsIndex)
{
return null;
}
groupNames.Append(dn.Substring((equalsIndex + 1),
(commaIndex - equalsIndex) - 1));
groupNames.Append("|");
}
return groupNames.ToString();
これにより、グループ名がgroupNames文字列にパイプで区切られますが、スピンスルーするときは何でも好きなようにできます
System.DirectoryServices.AccountManagement を使用する必要があります。はるかに簡単です。こちらがニースコードプロジェクトです article このDLLのすべてのクラスの概要を説明します。
あなたが指摘したように、あなたの現在のアプローチはプライマリグループを見つけません。実際、あなたが思っていたよりもずっと悪いです。別のドメインのドメインローカルグループのように、機能しないケースがいくつかあります。詳細は here で確認できます。 System.DirectoryServices.AccountManagementを使用するように切り替えると、コードは次のようになります。次のコードは、このユーザーが割り当てた直接のグループ(プライマリグループを含む)を見つけることができます。
UserPrincipal user = UserPrincipal.FindByIdentity(new PrincipalContext (ContextType.Domain, "mydomain.com"), IdentityType.SamAccountName, "username");
foreach (GroupPrincipal group in user.GetGroups())
{
Console.Out.WriteLine(group);
}
tokenGroups を使用します。
DirectorySearcher ds = new DirectorySearcher();
ds.Filter = String.Format("(&(objectClass=user)(sAMAccountName={0}))", username);
SearchResult sr = ds.FindOne();
DirectoryEntry user = sr.GetDirectoryEntry();
user.RefreshCache(new string[] { "tokenGroups" });
for (int i = 0; i < user.Properties["tokenGroups"].Count; i++) {
SecurityIdentifier sid = new SecurityIdentifier((byte[]) user.Properties["tokenGroups"][i], 0);
NTAccount nt = (NTAccount)sid.Translate(typeof(NTAccount));
//do something with the SID or name (nt.Value)
}
注:これはセキュリティグループのみを取得します
このコードはさらに高速に動作します(以前のバージョンより2つ1.5高速)。
public List<String> GetUserGroups(WindowsIdentity identity)
{
List<String> groups = new List<String>();
String userName = identity.Name;
int pos = userName.IndexOf(@"\");
if (pos > 0) userName = userName.Substring(pos + 1);
PrincipalContext domain = new PrincipalContext(ContextType.Domain, "riomc.com");
UserPrincipal user = UserPrincipal.FindByIdentity(domain, IdentityType.SamAccountName, userName); // NGeodakov
DirectoryEntry de = new DirectoryEntry("LDAP://RIOMC.com");
DirectorySearcher search = new DirectorySearcher(de);
search.Filter = "(&(objectClass=group)(member=" + user.DistinguishedName + "))";
search.PropertiesToLoad.Add("cn");
search.PropertiesToLoad.Add("samaccountname");
search.PropertiesToLoad.Add("memberOf");
SearchResultCollection results = search.FindAll();
foreach (SearchResult sr in results)
{
GetUserGroupsRecursive(groups, sr, de);
}
return groups;
}
public void GetUserGroupsRecursive(List<String> groups, SearchResult sr, DirectoryEntry de)
{
if (sr == null) return;
String group = (String)sr.Properties["cn"][0];
if (String.IsNullOrEmpty(group))
{
group = (String)sr.Properties["samaccountname"][0];
}
if (!groups.Contains(group))
{
groups.Add(group);
}
DirectorySearcher search;
SearchResult sr1;
String name;
int equalsIndex, commaIndex;
foreach (String dn in sr.Properties["memberof"])
{
equalsIndex = dn.IndexOf("=", 1);
if (equalsIndex > 0)
{
commaIndex = dn.IndexOf(",", equalsIndex + 1);
name = dn.Substring(equalsIndex + 1, commaIndex - equalsIndex - 1);
search = new DirectorySearcher(de);
search.Filter = "(&(objectClass=group)(|(cn=" + name + ")(samaccountname=" + name + ")))";
search.PropertiesToLoad.Add("cn");
search.PropertiesToLoad.Add("samaccountname");
search.PropertiesToLoad.Add("memberOf");
sr1 = search.FindOne();
GetUserGroupsRecursive(groups, sr1, de);
}
}
}
ここに私のために働いたコードがあります:
public ArrayList GetBBGroups(WindowsIdentity identity)
{
ArrayList groups = new ArrayList();
try
{
String userName = identity.Name;
int pos = userName.IndexOf(@"\");
if (pos > 0) userName = userName.Substring(pos + 1);
PrincipalContext domain = new PrincipalContext(ContextType.Domain, "riomc.com");
UserPrincipal user = UserPrincipal.FindByIdentity(domain, IdentityType.SamAccountName, userName);
DirectoryEntry de = new DirectoryEntry("LDAP://RIOMC.com");
DirectorySearcher search = new DirectorySearcher(de);
search.Filter = "(&(objectClass=group)(member=" + user.DistinguishedName + "))";
search.PropertiesToLoad.Add("samaccountname");
search.PropertiesToLoad.Add("cn");
String name;
SearchResultCollection results = search.FindAll();
foreach (SearchResult result in results)
{
name = (String)result.Properties["samaccountname"][0];
if (String.IsNullOrEmpty(name))
{
name = (String)result.Properties["cn"][0];
}
GetGroupsRecursive(groups, de, name);
}
}
catch
{
// return an empty list...
}
return groups;
}
public void GetGroupsRecursive(ArrayList groups, DirectoryEntry de, String dn)
{
DirectorySearcher search = new DirectorySearcher(de);
search.Filter = "(&(objectClass=group)(|(samaccountname=" + dn + ")(cn=" + dn + ")))";
search.PropertiesToLoad.Add("memberof");
String group, name;
SearchResult result = search.FindOne();
if (result == null) return;
group = @"RIOMC\" + dn;
if (!groups.Contains(group))
{
groups.Add(group);
}
if (result.Properties["memberof"].Count == 0) return;
int equalsIndex, commaIndex;
foreach (String dn1 in result.Properties["memberof"])
{
equalsIndex = dn1.IndexOf("=", 1);
if (equalsIndex > 0)
{
commaIndex = dn1.IndexOf(",", equalsIndex + 1);
name = dn1.Substring(equalsIndex + 1, commaIndex - equalsIndex - 1);
GetGroupsRecursive(groups, de, name);
}
}
}
AttributeValuesMultiString
再帰メソッドを使用するコードに対して200回実行するループでパフォーマンスを測定しました。そして、それは1.3倍速く動作しました。 AD設定のせいかもしれません。ただし、両方のスニペットは同じ結果をもたらしました。
次の例は、コードプロジェクトの記事 (Almost)C#経由のActive Directoryのすべて からのものです。
// userDn is a Distinguished Name such as:
// "LDAP://CN=Joe Smith,OU=Sales,OU=domain,OU=com"
public ArrayList Groups(string userDn, bool recursive)
{
ArrayList groupMemberships = new ArrayList();
return AttributeValuesMultiString("memberOf", userDn,
groupMemberships, recursive);
}
public ArrayList AttributeValuesMultiString(string attributeName,
string objectDn, ArrayList valuesCollection, bool recursive)
{
DirectoryEntry ent = new DirectoryEntry(objectDn);
PropertyValueCollection ValueCollection = ent.Properties[attributeName];
IEnumerator en = ValueCollection.GetEnumerator();
while (en.MoveNext())
{
if (en.Current != null)
{
if (!valuesCollection.Contains(en.Current.ToString()))
{
valuesCollection.Add(en.Current.ToString());
if (recursive)
{
AttributeValuesMultiString(attributeName, "LDAP://" +
en.Current.ToString(), valuesCollection, true);
}
}
}
}
ent.Close();
ent.Dispose();
return valuesCollection;
}
ユーザーに対して 識別名 でGroupsメソッドを呼び出し、boolフラグを渡すだけでよいかどうかを示します結果のArrayListにネスト/子グループのメンバーシップを含めます:
ArrayList groups = Groups("LDAP://CN=Joe Smith,OU=Sales,OU=domain,OU=com", true);
foreach (string groupName in groups)
{
Console.WriteLine(groupName);
}
.NETで深刻なレベルのActive Directoryプログラミングを行う必要がある場合は、上記のコードプロジェクトの記事をブックマークして確認することを強くお勧めします。
これは、特定の識別名のすべてのグループ(直接および間接)をリストする方法です。
文字列 1.2.840.113556.1.4.1941 は、LDAP_MATCHING_RULE_IN_CHAINを指定します。
このルールは、DNに適用されるフィルターに制限されています。これは特別な「拡張」一致演算子であり、一致が見つかるまでオブジェクトの祖先のチェーンをルートまでたどります。
このメソッドは、私のテストでは serPrincipal.GetGroups() メソッドよりも25倍高速です。
注:プライマリグループ(通常はドメインユーザー)は、このメソッドまたはGetGroups()メソッドによって返されません。プライマリグループ名も取得するために、 この方法 が機能することを確認しました。
さらに、私はこれを見つけました LDAPのリスト フィルターは非常に便利です。
private IEnumerable<string> GetGroupsForDistinguishedName(DirectoryEntry domainDirectoryEntry, string distinguishedName)
{
var groups = new List<string>();
if (!string.IsNullOrEmpty(distinguishedName))
{
var getGroupsFilterForDn = $"(&(objectCategory=group)(member:1.2.840.113556.1.4.1941:={distinguishedName})))";
using (var dirSearch = CreateDirectorySearcher(domainDirectoryEntry, getGroupsFilterForDn))
{
dirSearch.PropertiesToLoad.Add("name");
using (var results = dirSearch.FindAll())
{
foreach (SearchResult result in results)
{
if (result.Properties.Contains("name"))
groups.Add((string)result.Properties["name"][0]);
}
}
}
}
return groups;
}
Microsoft LDAPには、ユーザーのすべてのメンバーシップを再帰的に検索する特別な方法がいくつかあります。
「member」属性に指定できる一致ルール。特に、Microsoft Exclusive LDAP_MATCHING_RULE_IN_CHAIN "member"属性のルールを使用すると、再帰的/ネストされたメンバーシップ検索が可能になります。ルールは、メンバー属性の後に追加するときに使用されます。例(メンバー:1.2.840.113556.1.4.1941:= XXXXX)
アカウントと同じドメインの場合、フィルターは、必要に応じてクロスドメインを使用するのに非常に便利なアカウントのDistinguishedName属性の代わりに、<SID = S-1-5-21-XXXXXXXXXXXXXXXXXXXXXXX>を使用できます。ただし、<SID =>タグはForeignSecurityPrincipalオブジェクトタイプを考慮しないため、SIDを解決しないため、ForeignSecurityPrincipal <GUID = YYYY>を使用する必要があるようです。 ForeignSecurityPrincipal DistinguishedNameも使用できます。
この知識を使用して、アカウントがメンバーである「ドメインローカル」グループなど、メンバーシップを取得するのが難しいLDAPクエリを実行できますが、グループのメンバーを確認しない限り、ユーザーがメンバーかどうかはわかりません。
//Get Direct+Indirect Memberships of User (where SID is XXXXXX)
string str = "(& (objectCategory=group)(member:1.2.840.113556.1.4.1941:=<SID=XXXXXX>) )";
//Get Direct+Indirect **Domain Local** Memberships of User (where SID is XXXXXX)
string str2 = "(& (objectCategory=group)(|(groupType=-2147483644)(groupType=4))(member:1.2.840.113556.1.4.1941:=<SID=XXXXXX>) )";
//TAA DAA
すべてのグループメンバーシップを取得するユーザーのSIDを置き換えた後、これらのLDAPクエリを試してみてください。 PowerShellコマンドGet-ADPrincipalGroupMembership が舞台裏で使用するものと同じクエリでない場合、これは似ていると思います。コマンドには、「別のドメインでローカルグループを検索する場合は、ResourceContextServerパラメーターを使用して、他のドメインの代替サーバーを指定します。」
C#とActive Directoryについて十分に理解している場合は、提供されているLDAPクエリを使用してLDAP検索を実行する方法を知っている必要があります。
追加文書:
Active Directoryに接続するためのユーザー名とパスワードを使用したLDAP接続がある場合、適切に接続するために使用したコードは次のとおりです。
using System.DirectoryServices.AccountManagement;
// ...
// Connection information
var connectionString = "LDAP://domain.com/DC=domain,DC=com";
var connectionUsername = "your_ad_username";
var connectionPassword = "your_ad_password";
// Get groups for this user
var username = "myusername";
// Split the LDAP Uri
var uri = new Uri(connectionString);
var Host = uri.Host;
var container = uri.Segments.Count() >=1 ? uri.Segments[1] : "";
// Create context to connect to AD
var princContext = new PrincipalContext(ContextType.Domain, Host, container, connectionUsername, connectionPassword);
// Get User
UserPrincipal user = UserPrincipal.FindByIdentity(princContext, IdentityType.SamAccountName, username);
// Browse user's groups
foreach (GroupPrincipal group in user.GetGroups())
{
Console.Out.WriteLine(group.Name);
}