UserPrincipals.GetAuthorizationGroups при перечислении групп произошла ошибка (1301). После обновления до контроллера домена Server 2012

исследования:

аналогичная проблема с обходным путем, но не фактическое решение существующей проблемы

аналогичная проблема, указывающая на обновление конечной точки Microsoft как виновник

приведенные выше ссылки наиболее подходят для моей проблемы, я также просмотрел каждый подобный вопрос, указанный Stack Overflow при создании этого сообщения, и только приведенные выше вопросы соответствуют моим вопрос.

Справочная информация:

Я использую UserPrincipal.GetAuthorizationGroups для разрешения конкретных доступ к странице с IIS 7.5 на Server 2008 R2 в C#.NET веб-сайт 4.0 форм ЗА 2 с половиной года. 15 мая 2013 года мы удалили основной контроллер домена под управлением Server 2008 (не r2) и заменили его контроллером домена Server 2012. На следующий день мы начали получать исключения, перечисленные ниже.

Я использую основной контекст для проверки подлинности форм. Этот имя пользователя / передать рукопожатие успешно и cookie auth правильно установлен, но последующий вызов основного контекста, который также вызывает UserPrincipal.GetAuthorizationGroups нарушается. Мы решили несколько проблем BPA, которые появились в контроллере домена Server 2012, но это еще не решило проблему. Я также установил cron, который работает на двух отдельных серверах. Два сервера будут терпеть неудачу при разрешении Sid группы в разное время, хотя они работают с одной и той же базой кода. (Среда разработки и производства окружающая среда.)

проблема временно решается при перезагрузке веб-сервера, а также на dev-сервере она будет решена после 12 часов не работает. Производственный сервер обычно перестает функционировать должным образом до перезагрузки без разрешения.

на данный момент я пытаюсь уточнить cron, нацеленный на определенные контроллеры домена в сети, а также новый DC и используя стандартный запрос LDAP, который в настоящее время не дает больше целевые времена исключений. До сих пор мы обнаружили на одном веб-сервере, что нет шаблона дней, в которые он терпит неудачу, но он восстановится в течение примерно 12 часов. Последние результаты показывают сбой разрешения Sid группы между 8AM-8PM, затем он восстанавливается, через несколько дней он потерпит неудачу в 8pm и восстановится в 8am, затем будет работать нормально еще 12 часов и снова потерпит неудачу. Мы надеемся увидеть, является ли это просто конкретной проблемой связи с сервером или увидеть, является ли это весь набор домена Контроллеры.

исключения:

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()

вопрос:

учитывая приведенную выше информацию, кто-нибудь знает, почему вывод из эксплуатации Windows Server 2008 (не r2) и внедрение нового сервера 2012 DC вызовет UserPrincipal.GetAuthorizationGroups для сбоя с ошибкой разрешения 1301 SID? Были бы также признательны за идеи по устранению возможных причин.

отказ от ответственности:

это мой первый пост на стек Переполнение, я часто исследую здесь, но до сих пор не участвовал в дискуссиях. Простите, если я должен был опубликовать в другом месте и не стесняйтесь указывать лучшие шаги перед публикацией.

ОБНОВЛЕНИЕ 13-ИЮНЬ-2013:

12 июня я рассмотрел возможность того, что предметы, не утилизированные, вызывают проблему. Временные рамки были слишком короткими, чтобы определить, исправил ли скорректированный код проблему, но я буду продолжать обновлять, поскольку мы работаем над таким разрешением что, если повезет, кто-нибудь здесь сможет помочь.

Исходный Код

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

Обновленный Код

        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;

    }

9 ответов


Я только что столкнулся с этой же проблемой, и информация, которую мне удалось отследить, может быть полезна; как указано выше, мы видели эту проблему, когда контроллер домена работает на сервере 2012 - сначала с развертыванием клиента, а затем реплицируется в нашей собственной сети.

после некоторых экспериментов мы обнаружили, что наш код будет работать нормально на сервере 2012, но попал в код ошибки 1301, когда клиентская система запускала сервер 2008. Найдена ключевая информация о происходящем здесь:

MS blog в переводе с немецкого

исправление, указанное в приведенной ниже ссылке, исправило проблему в нашей тестовой системе

SID S-1-18-1 и SID S-1-18-2 невозможно отобразить

надеюсь, что это полезно для кого-то! Как отмечали многие, этот вызов метода представляется довольно хрупким, и мы, вероятно, рассмотрим реализацию некоторого альтернативного подхода до того, как мы коснемся других вопросов.

Гари


мы столкнулись с этой проблемой, когда наша команда инфраструктуры принесла контроллер домена 2012 онлайн. Мы также имели ДКС до 2012 на месте и поэтому мы испытали вопрос периодически. Мы придумали исправление, которым я хотел поделиться - у него есть 2 части.

прежде всего, установить исправления упомянуто Гэри Хиллом. Это позволит решить следующую проблему:

при перечислении групп произошла ошибка (1301). Сид группы не смог разрешиться.

мы думали, что мы были дома бесплатно после установки этого исправления. Однако после его установки мы получили другую прерывистую ошибку. Некоторые группы, которые мы допрашивали, имели null sAMAccountName собственность. Фактическое свойство был заполняется в Active Directory, но он неправильно возвращается с нулевым значением API. Я предполагаю, что это ошибка где-то в API Active Directory, но я не знаю больше, чем что.

К счастью, мы смогли обойти эту проблему, переключившись на использование группы Name свойство вместо sAMAccountName собственность. Это сработало для нас. Я верю, что sAMAccountName фактически устарел и существует только по причинам обратной совместимости. В таком случае это казалось разумным изменением.

прилагаю сокращенную версию нашего GetRolesForUser код для демонстрации изменения на месте.

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);
    }
}

надеюсь, это поможет.


вот мое решение. Кажется, он работает стабильно хорошо. Поскольку проблема возникает при итерации по коллекции, я использую другой подход при итерации, чтобы обработать исключение, не блокируя фактическую итерацию:

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

Я испытал код ошибки 1301 с UserPrincipal.GetAuthorizationGroups при использовании нового виртуального домена разработки, который содержал 2 рабочие станции и 50 пользователей / групп (многие из которых являются встроенными). Мы работали под управлением Windows Server 2012 R2 Essentials с двумя рабочими станциями Windows 8.1 Enterprise, присоединенными к домену.

я смог рекурсивно получить список членства пользователя в группе, используя следующий код:

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

Я нахожусь в среде с несколькими лесами доменов и довериями. У меня есть почти такой же код, работающий на форме веб-сайта, используемой для выполнения поиска групп безопасности пользователей в разных доменах.

Я получаю эту точную ошибку в одном из очень больших доменов, где членство в группе может включать 50+ разных групп. Он отлично работает в лесах других доменов.

в моем исследовании я нашел поток, который выглядит несвязанным, но на самом деле имеет ту же трассировку стека. Он для удаленного приложения, работающего на SBS. Поток упоминает, что ошибка вызвана неразрешимыми SIDS в группе. Я считаю, что это будут так называемые "надгробные" Сиды в active directory. смотрите нить здесь.

поток предполагает, что поиск захороненных enteries и удаление их из групп решает проблему. Возможно ли, что ошибка, которую вы получаете, заключается в том, что SIDS получают tombstoned каждые 12 часов отдельным несвязанным процесс? В конечном счете, я считаю, что это ошибка в рамках, и что метод не должен сбой из-за надгробных/неразрешимых SIDS.

удачи!


Если кто-то заинтересован, это VB.NET версия того же кода. Несколько вещей, которые вы должны сделать, прежде чем этот код может работать

1) Вы должны ссылаться на систему сборки.Каталог
2) Не забудьте передать переменную "theusername" без домена, поэтому, если ваш домен "GIS", а ваше имя пользователя "Hussein", Windows обычно аутентифицирует вас как GIS\Hussein. Поэтому вам нужно отправить только имя пользователя "Hussein". Я разработал чувствительный случай материал.
3) Метод GetGroupsNew принимает имя пользователя и возвращает список групп
4) метод isMemberofnew берет имя пользователя и группу и проверяет, является ли этот пользователь частью этой группы или нет, это тот, который меня интересовал.

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

у нас была аналогичная проблема после обновления контроллера домена до 2012 года. Внезапно мой звонок пользователю.GetAuthorizationGroups () начал сбой; я получал то же исключение, что и вы (ошибка 1301). Итак, я изменил его на user.Методы getgroups(). Это работало некоторое время, а затем началось сбой периодически на "плохое имя пользователя или пароль". Мой последний обходной путь, похоже, исправит это, по крайней мере на данный момент. Вместо вызова любого из них после построения объекта user я также строю объект группы, по одному для каждой группы, в которую я хочу видеть, является ли пользователь членом. т. е. " пользователь.IsMemberOf (group)". Что, кажется, работает.

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);                                        
                }

У меня было такое же исключение. Если кто-то не хочет использовать "LDAP", используйте этот код. Потому что у меня были вложенные группы, я использовал GetMembers (true), и это немного дольше, чем GetMembers ().

https://stackoverflow.com/a/27548271/1857271

или скачать исправление отсюда:http://support.microsoft.com/kb/2830145


проблема с перечислением групп авторизации и исправлений, указанных в ответе, не относится к нашему веб-серверу.

ручное перечисление и игнорирование проблем, вызывающих группы, работает хорошо, однако:

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