Recherche:
Problème similaire avec solution de contournement, mais pas de solution réelle au problème existant
Problème similaire désignant la mise à jour de Microsoft End Point comme coupable
Les liens ci-dessus conviennent le mieux à mon problème. J'ai également consulté toutes les questions similaires répertoriées par Stack Overflow lors de la création de ce message. Seules les questions mentionnées ci-dessus correspondent à mon problème.
Contexte:
J'utilise UserPrincipal.GetAuthorizationGroups
pour les autorisations d'accès aux pages spécifiques exécutant IIS 7.5 sur Server 2008 R2 dans un site de formulaires Web C # .NET 4.0 depuis 2 ans et demi. Le 15 mai 2013, nous avons retiré un contrôleur de domaine principal exécutant Server 2008 (et non la R2) et l'avons remplacé par un contrôleur de domaine Server 2012. Le lendemain, nous avons commencé à recevoir l'exception indiquée ci-dessous.
J'utilise le contexte principal pour l'authentification par formulaire. La prise de contact nom d'utilisateur/passe réussit et le cookie d'authentification est correctement défini, mais l'appel de contexte principal suivant qui appelle également UserPrincipal.GetAuthorizationGroups
échoue par intermittence. Nous avons résolu quelques problèmes BPA apparus dans le contrôleur de domaine Server 2012, mais cela n'a pas encore résolu le problème. J'ai également institué un cron qui fonctionne sur deux serveurs distincts. Les deux serveurs échoueront à la résolution du groupe SID à des moments différents bien qu'ils exécutent la même base de code. (Un environnement de développement et un environnement de production).
Le problème se résout lui-même temporairement au redémarrage du serveur Web et sur le serveur dev qu'il se résoudra après 12 heures d'inactivité. Le serveur de production cesse généralement de fonctionner correctement jusqu'au redémarrage sans se résoudre lui-même.
À ce stade, j'essaie d'affiner le système cron en ciblant des contrôleurs de domaine spécifiques du réseau, ainsi que le nouveau DC, et en utilisant la requête LDAP standard qui ne permet pas de générer des délais d'exception plus ciblés. Jusqu'ici, nous avons constaté sur un serveur Web qu'il n'y avait pas de modèle pour les jours où il échoue, mais il va récupérer dans environ 12 heures. Les derniers résultats montrent un échec de la résolution du groupe SID entre 8h00 et 20h00, puis il récupère. Plusieurs jours plus tard, il échouera à 20h00 et récupérera à 8h00, puis s'exécutera correctement pendant 12 heures supplémentaires et échouera à nouveau. Nous espérons savoir s’il s’agit uniquement d’un problème de communication entre serveurs ou s’il s’agit de l’ensemble des contrôleurs de domaine.
Exception:
Exception information:
Exception type: PrincipalOperationException
Exception message: An error (1301) occurred while enumerating the groups.
The group's SID could not be resolved.
at System.DirectoryServices.AccountManagement.SidList.TranslateSids(String target, IntPtr[] pSids)
at System.DirectoryServices.AccountManagement.SidList..ctor(SID_AND_ATTR[] sidAndAttr)
at System.DirectoryServices.AccountManagement.AuthZSet..ctor(Byte[] userSid, NetCred credentials, ContextOptions contextOptions, String flatUserAuthority, StoreCtx userStoreCtx, Object userCtxBase)
at System.DirectoryServices.AccountManagement.ADStoreCtx.GetGroupsMemberOfAZ(Principal p)
at System.DirectoryServices.AccountManagement.UserPrincipal.GetAuthorizationGroups()
Question:
Compte tenu des informations ci-dessus, quelqu'un a-t-il une idée du pourquoi du déclassement de Windows Server 2008 (et non de la R2) et de la mise en œuvre d'un nouveau serveur 2012 DC entraînerait l'échec de UserPrincipal.GetAuthorizationGroups
avec l'erreur de résolution 1301 SID? les causes seraient également appréciées.
Avertissement:
Ceci est mon premier article sur Stack Overflow. Je fais souvent des recherches ici, mais je n’ai pas encore participé aux discussions. Pardonnez-moi si j'aurais dû poster ailleurs et n'hésitez pas à indiquer de meilleures étapes avant de poster.
UPDATE 13-JUN-2013:
Le 12 juin, j'ai évoqué la possibilité que des éléments ne soient pas éliminés à l'origine du problème ... Le délai a été trop court pour déterminer si le code ajusté a résolu le problème, mais je continuerai à mettre à jour le logiciel pour le résoudre. de sorte que peut-être avec un peu de chance, quelqu'un ici peut donner un coup de main.
Code d'origine
public bool isGroupMember(string userName, ArrayList groupList)
{
bool valid = false;
PrincipalContext ctx = new PrincipalContext(ContextType.Domain, domain_server + ".domain.org:636", null, ContextOptions.Negotiate | ContextOptions.SecureSocketLayer);
// find the user in the identity store
UserPrincipal user =
UserPrincipal.FindByIdentity(
ctx,
userName);
// get the groups for the user principal and
// store the results in a PrincipalSearchResult object
PrincipalSearchResult<Principal> groups =
user.GetAuthorizationGroups();
// display the names of the groups to which the
// user belongs
foreach (Principal group in groups)
{
foreach (string groupName in groupList)
{
if (group.ToString() == groupName)
{
valid = true;
}
}
}
return valid;
}
Code mis à jour
public bool isGroupMember(string userName, ArrayList groupList, string domain_server)
{
bool valid = false;
try
{
using (PrincipalContext ctx = new PrincipalContext(ContextType.Domain, domain_server + ".domain.org:636", null, ContextOptions.Negotiate | ContextOptions.SecureSocketLayer))
{
// find the user in the identity store
UserPrincipal user =
UserPrincipal.FindByIdentity(
ctx,
userName);
try
{
// get the groups for the user principal and
// store the results in a PrincipalSearchResult object
using (PrincipalSearchResult<Principal> groups = user.GetAuthorizationGroups())
{
// display the names of the groups to which the
// user belongs
foreach (Principal group in groups)
{
foreach (string groupName in groupList)
{
if (group.ToString() == groupName)
{
valid = true;
}
}
group.Dispose();
}
}//end using-2
}
catch
{
log_gen("arbitrary info");
return false;
}
}//end using-1
}
catch
{
log_gen("arbitrary info");
return false;
}
return valid;
}
Je viens de rencontrer le même problème et les informations que j'ai réussi à retrouver peuvent être utiles; Comme ci-dessus, nous avons constaté le problème suivant: le contrôleur de domaine exécute Server 2012, d'abord avec un déploiement client, puis répliqué sur notre propre réseau.
Après quelques expériences, nous avons constaté que notre code fonctionnerait correctement sur Server 2012, mais nous avons frappé le code d'erreur 1301 lorsque le système client exécutait Server 2008. Les informations clés sur ce qui se passait se trouvaient ici:
Le correctif mentionné dans le lien ci-dessous a résolu le problème de notre système de test.
Le SID S-1-18-1 et le SID S-1-18-2 ne peuvent pas être mappés
J'espère que c'est utile pour quelqu'un! Comme beaucoup l'ont noté, cet appel à la méthode semble plutôt fragile et nous envisagerons probablement de mettre en œuvre une approche alternative avant de nous attaquer à d'autres problèmes.
Gary
Voici ma solution. Cela semble toujours bien fonctionner. Parce que le problème survient lors d'une itération sur la collection, j'utilise une approche différente lors de l'itération afin de gérer l'exception sans bloquer l'itération réelle:
private string[] GetUserRoles(string Username)
{
List<string> roles = new List<string>();
try
{
string domain = Username.Contains("\\") ? Username.Substring(0, Username.IndexOf("\\")) : string.Empty;
string username = Username.Contains("\\") ? Username.Substring(Username.LastIndexOf("\\") + 1) : Username;
if (!string.IsNullOrEmpty(domain) && !string.IsNullOrEmpty(username))
{
PrincipalContext principalContext = new PrincipalContext(ContextType.Domain, domain);
UserPrincipal user = UserPrincipal.FindByIdentity(principalContext, username);
if (user != null)
{
PrincipalSearchResult<Principal> groups = user.GetAuthorizationGroups();
int count = groups.Count();
for (int i = 0; i < count; i++)
{
IEnumerable<Principal> principalCollection = groups.Skip(i).Take(1);
Principal principal = null;
try
{
principal = principalCollection.FirstOrDefault();
}
catch (Exception e)
{
//Error handling...
//Known exception - sometimes AD can't query a particular group, requires server hotfix?
//http://support.Microsoft.com/kb/2830145
}
if (principal!=null && principal is GroupPrincipal)
{
GroupPrincipal groupPrincipal = (GroupPrincipal)principal;
if (groupPrincipal != null && !string.IsNullOrEmpty(groupPrincipal.Name))
{
roles.Add(groupPrincipal.Name.Trim());
}
}
}
}
}
}
catch (Exception e)
{
//Error handling...
}
return roles.ToArray();
}
Nous avons rencontré ce problème lorsque notre équipe d'infrastructure a mis en ligne un contrôleur de domaine 2012. Nous avions également des CD pré-2012 en place et nous avons donc connu le problème de façon intermittente. Nous avons proposé un correctif que je voulais partager: il comporte deux parties.
Tout d’abord, installez le hotfix mentionné par Gary Hill. Cela résoudra le problème suivant:
Une erreur (1301) s'est produite lors de l'énumération des groupes. Le SID du groupe n'a pas pu être résolu.
Nous pensions être à la maison après l'installation de ce correctif. Cependant, après son installation, nous avons eu une erreur intermittente différente. Certains groupes que nous interrogions avaient une propriété null sAMAccountName
. La propriété réelle était remplie dans Active Directory, mais elle était renvoyée à tort avec une valeur null par l'API. Je présume que c'est un bogue quelque part dans l'API Active Directory, mais je ne le sais pas davantage.
Heureusement, nous avons pu contourner le problème en utilisant la propriété group Name
au lieu de la propriété sAMAccountName
. Cela a fonctionné pour nous. Je pense que sAMAccountName
est effectivement obsolète et n’existe que pour des raisons de compatibilité ascendante. Cela étant, cela semblait un changement raisonnable.
Je joins une version simplifiée de notre code GetRolesForUser
pour illustrer le changement de lieu.
using (var context = new PrincipalContext(ContextType.Domain, _domainName))
{
try
{
var p = UserPrincipal.FindByIdentity(context, IdentityType.SamAccountName, username);
if (p == null) throw new NullReferenceException(string.Format("UserPrincipal.FindByIdentity returned null for user: {0}, this can indicate a problem with one or more of the AD controllers", username));
var groups = p.GetAuthorizationGroups();
var domain = username.Substring(0, username.IndexOf(@"\", StringComparison.InvariantCultureIgnoreCase)).ToLower();
foreach (GroupPrincipal group in groups)
{
if (!string.IsNullOrEmpty(group.Name))
{
var domainGroup = domain + @"\" + group.Name.ToLower();
if (_groupsToUse.Any(x => x.Equals(domainGroup, StringComparison.InvariantCultureIgnoreCase)))
{
// Go through each application role defined and check if the AD domain group is part of it
foreach (string role in roleKeys)
{
string[] roleMembers = new [] { "role1", "role2" };
foreach (string member in roleMembers)
{
// Check if the domain group is part of the role
if (member.ToLower().Contains(domainGroup))
{
// Cache the Application Role (NOT the AD role)
results.Add(role);
}
}
}
}
}
group.Dispose();
}
}
catch (Exception ex)
{
throw new ProviderException("Unable to query Active Directory.", ex);
}
}
J'espère que cela pourra aider.
J'ai rencontré le code d'erreur 1301 avec UserPrincipal.GetAuthorizationGroups
lors de l'utilisation d'un tout nouveau domaine de développement virtuel contenant 2 postes de travail et 50 utilisateurs/groupes (dont beaucoup sont des postes intégrés). Nous utilisions Windows Server 2012 R2 Essentials avec deux stations de travail Windows 8.1 Enterprise jointes au domaine.
J'ai pu obtenir de manière récursive une liste des membres d'un groupe d'utilisateurs à l'aide du code suivant:
class ADGroupSearch
{
List<String> groupNames;
public ADGroupSearch()
{
this.groupNames = new List<String>();
}
public List<String> GetGroups()
{
return this.groupNames;
}
public void AddGroupName(String groupName)
{
this.groupNames.Add(groupName);
}
public List<String> GetListOfGroupsRecursively(String samAcctName)
{
PrincipalContext ctx = new PrincipalContext(ContextType.Domain, System.Environment.UserDomainName);
Principal principal = Principal.FindByIdentity(ctx, IdentityType.SamAccountName, samAcctName);
if (principal == null)
{
return GetGroups();
}
else
{
PrincipalSearchResult<Principal> searchResults = principal.GetGroups();
if (searchResults != null)
{
foreach (GroupPrincipal sr in searchResults)
{
if (!this.groupNames.Contains(sr.Name))
{
AddGroupName(sr.Name);
}
Principal p = Principal.FindByIdentity(ctx, IdentityType.SamAccountName, sr.SamAccountName);
try
{
GetMembersForGroup(p);
}
catch (Exception ex)
{
//ignore errors and continue
}
}
}
return GetGroups();
}
}
private void GetMembersForGroup(Principal group)
{
if (group != null && typeof(GroupPrincipal) == group.GetType())
{
GetListOfGroupsRecursively(group.SamAccountName);
}
}
private bool IsGroup(Principal principal)
{
return principal.StructuralObjectClass.ToLower().Equals("group");
}
}
Je suis dans un environnement avec plusieurs forêts de domaines et approbations. J'ai à peu près ce code exactement identique qui s'exécute sur un formulaire de site Web utilisé pour effectuer des recherches de groupe de sécurité utilisateur dans différents domaines.
Je reçois cette erreur exacte dans l'un des très grands domaines où l'appartenance à un groupe peut inclure plus de 50 groupes différents. Cela fonctionne très bien dans les forêts d'autres domaines.
Dans mes recherches, j'ai trouvé un thread qui n'a pas l'air d'être lié, mais qui a en fait la même trace de pile. C'est pour une application distante s'exécutant sur SBS. Le fil mentionne que l'erreur est provoquée par un SSID non résolu dans un groupe. Je crois que ceux-ci seraient ce qu'on appelle les "PEID" dans Active Directory. Voir le fil ici .
Le fil de discussion suggère que le fait de retrouver les entreprises entêtées et de les supprimer des groupes résolve le problème. Est-il possible que l'erreur que vous recevez est due au fait que les PEID sont désactivés toutes les 12 heures par un processus distinct non lié? En fin de compte, j'estime qu'il s'agit d'un bogue dans le cadre et que la méthode ne devrait pas échouer à cause d'un PEID désemparé/insoluble.
Bonne chance!
Si quelqu'un est intéressé, il s'agit d'une version VB.NET du même code. Peu de choses que vous devez faire avant que ce code puisse fonctionner
1) Vous devez référencer l'Assembly System.DirectoryServices
2) Assurez-vous de passer la variable "nomutilisateur" sans le domaine. Ainsi, si votre domaine est "SIG" et votre nom d'utilisateur est "Hussein", Windows vous authentifie généralement en tant que SIG\Hussein. Donc, vous devez simplement entrer le nom d'utilisateur "Hussein". J'ai travaillé sur les choses sensibles à la casse .
3) La méthode GetGroupsNew prend un nom d'utilisateur et retourne une liste de groupes
4) La méthode isMemberofnew prend un nom d'utilisateur et un groupe et vérifie que cet utilisateur appartient ou non à ce groupe. C'est celui qui m'intéressait.
Private Function getGroupsNew(theusername As String) As List(Of String)
Dim lstGroups As New List(Of String)
Try
Dim allDomains = Forest.GetCurrentForest().Domains.Cast(Of Domain)()
Dim allSearcher = allDomains.[Select](Function(domain)
Dim searcher As New DirectorySearcher(New DirectoryEntry("LDAP://" + domain.Name))
searcher.Filter = [String].Format("(&(&(objectCategory=person)(objectClass=user)(userPrincipalName=*{0}*)))", theusername)
Return searcher
End Function)
Dim directoryEntriesFound = allSearcher.SelectMany(Function(searcher) searcher.FindAll().Cast(Of SearchResult)().[Select](Function(result) result.GetDirectoryEntry()))
Dim memberOf = directoryEntriesFound.[Select](Function(entry)
Using entry
Return New With { _
Key .Name = entry.Name, _
Key .GroupName = DirectCast(entry.Properties("MemberOf").Value, Object()).[Select](Function(obj) obj.ToString()) _
}
End Using
End Function)
For Each user As Object In memberOf
For Each groupName As Object In user.GroupName
lstGroups.Add(groupName)
Next
Next
Return lstGroups
Catch ex As Exception
Throw
End Try
End Function
Private Function isMemberofGroupNew(theusername As String, thegroupname As String) As Boolean
Try
Dim lstGroups As List(Of String) = getGroupsNew(theusername)
For Each sGroup In lstGroups
If sGroup.ToLower.Contains(thegroupname.ToLower) Then Return True
Next
Return False
Catch ex As Exception
Throw
End Try
End Function
J'ai eu le problème que si je suis connecté via VPN et que j'utilise groups=UserPrincipal.GetGroups()
, l'exception se produit lors d'une itération sur les groupes.
Si quelqu'un veut lire tous les groupes d'un utilisateur, la possibilité est la suivante (ce qui est plus rapide que d'utiliser GetGroups()
)
private IList<string> GetUserGroupsLDAP(string samAccountName)
{
var groupList = new List<string>();
var domainConnection = new DirectoryEntry("LDAP://" + serverName, serverUser, serverUserPassword); // probably you don't need username and password
var samSearcher = new DirectorySearcher();
samSearcher.SearchRoot = domainConnection;
samSearcher.Filter = "(samAccountName=" + samAccountName + ")";
var samResult = samSearcher.FindOne();
if (samResult != null)
{
var theUser = samResult.GetDirectoryEntry();
theUser.RefreshCache(new string[] { "tokenGroups" });
var sidSearcher = new DirectorySearcher();
sidSearcher.SearchRoot = domainConnection;
sidSearcher.PropertiesToLoad.Add("name");
sidSearcher.Filter = CreateFilter(theUser);
foreach (SearchResult result in sidSearcher.FindAll())
{
groupList.Add((string)result.Properties["name"][0]);
}
}
return groupList;
}
private string CreateFilter(DirectoryEntry theUser)
{
string filter = "(|";
foreach (byte[] resultBytes in theUser.Properties["tokenGroups"])
{
var SID = new SecurityIdentifier(resultBytes, 0);
filter += "(objectSid=" + SID.Value + ")";
}
filter += ")";
return filter;
}
nous avions un problème similaire après la mise à niveau du contrôleur de domaine en 2012. Soudain, mon appel à user.GetAuthorizationGroups () a commencé à échouer; Je recevais la même exception que vous étiez (erreur 1301). Donc, je l'ai changé en user.GetGroups (). Cela a fonctionné pendant un petit moment, puis a échoué par intermittence sur "Nom d'utilisateur ou mot de passe incorrect". Ma dernière solution de contournement semble résoudre ce problème, du moins pour le moment. Au lieu d'appeler l'un de ceux-ci, après avoir construit l'objet utilisateur, je construis également un objet groupe, un pour chaque groupe que je veux voir si l'utilisateur est membre de. c'est-à-dire "user.IsMemberOf (groupe)". Cela semble fonctionner.
try
{
using (HostingEnvironment.Impersonate())
{
using (var principalContext = new PrincipalContext(ContextType.Domain, "MYDOMAIN"))
{
using (var user = UserPrincipal.FindByIdentity(principalContext, userName))
{
if (user == null)
{
Log.Debug("UserPrincipal.FindByIdentity failed for userName = " + userName + ", thus not authorized!");
isAuthorized = false;
}
if (isAuthorized)
{
firstName = user.GivenName;
lastName = user.Surname;
// so this code started failing:
// var groups = user.GetGroups();
// adGroups.AddRange(from @group in groups where
// @group.Name.ToUpper().Contains("MYSEARCHSTRING") select @group.Name);
// so the following workaround, which calls, instead,
// "user.IsMemberOf(group)",
// appears to work (for now at least). Will monitor for issues.
// test membership in SuperUsers
const string superUsersGroupName = "MyApp-SuperUsers";
using (var superUsers = GroupPrincipal.FindByIdentity(principalContext, superUsersGroupName))
{
if (superUsers != null && user.IsMemberOf(superUsers))
// add to the list of groups this user is a member of
// then do something with it later
adGroups.Add(superUsersGroupName);
}
J'ai eu la même exception. Si quelqu'un ne veut pas utiliser "LDAP", utilisez ce code. Parce que j'avais des groupes imbriqués, j'ai utilisé GetMembers (true) et c'est un peu plus long que GetMembers ().
https://stackoverflow.com/a/27548271/1857271
ou téléchargez le correctif à partir d’ici: http://support.Microsoft.com/kb/2830145
Le même problème lié à l'énumération des groupes d'autorisation et des correctifs mentionnés dans la réponse ne s'appliquait pas à notre serveur Web.
Énumérer et ignorer manuellement les problèmes qui causent des groupes fonctionne bien, cependant:
private static bool UserIsMember(string usr, string grp)
{
usr = usr.ToLower();
grp = grp.ToLower();
using (var pc = new PrincipalContext(ContextType.Domain, "DOMAIN_NAME"))
{
using (var user = UserPrincipal.FindByIdentity(pc, usr))
{
var isMember = false;
var authGroups = user?.GetAuthorizationGroups().GetEnumerator();
while (authGroups?.MoveNext() ?? false)
{
try
{
isMember = authGroups.Current.Name.ToLower().Contains(grp);
if (isMember) break;
}
catch
{
// ignored
}
}
authGroups?.Dispose();
return isMember;
}
}
}