web-dev-qa-db-fra.com

UserPrincipals.GetAuthorizationGroups Une erreur (1301) s'est produite lors de l'énumération des groupes. Après la mise à niveau vers le contrôleur de domaine Server 2012

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;

    }
27
Pynt

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:

Blog MS traduit de l'allemand

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

18
Gary Hill

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();
}
5
AlishahNovin

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.

4
John Reilly

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");
    }
}
2
TS12

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!

1
Bri

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
1
Hussein Nasser

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;
}
0
Apfelkuacha

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);                                        
                }
0
David Barrows

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

0
Nazar Iaremii

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;
        }
    }
}
0
davidmdem