Подтвердить что ты не робот

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

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

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

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

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

Фон:

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

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

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

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

Исключение:

Exception information: 
Exception type: PrincipalOperationException 
Exception message: An error (1301) occurred while enumerating the groups.  
The group 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 с ошибкой разрешения SID 1301? Также были бы оценены идеи по устранению возможных причин.

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

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

ОБНОВЛЕНИЕ 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;

    }
4b9b3361

Ответ 1

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

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

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

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

SID S-1-18-1 и SID S-1-18-2 не могут быть отображены

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

Гэри

Ответ 2

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

Прежде всего, установите исправление, указанное Gary Hill. Это устранит следующую проблему:

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

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

К счастью, мы смогли обойти проблему, переключившись на использование свойства group 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);
    }
}

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

Ответ 3

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

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

Ответ 4

Я столкнулся с кодом ошибки 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");
    }
}

Ответ 5

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

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

В моих исследованиях я нашел нить, которая выглядит несвязанной, но на самом деле имеет одну и ту же трассировку стека. Это для удаленного приложения, запущенного на SBS. В потоке упоминается, что ошибка вызвана неразрешимыми SIDS в группе. Я считаю, что это будет то, что известно в виде "надгробных" СИДС в активном каталоге. См. тему здесь.

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

Удачи!

Ответ 6

Если кому-то интересно, это версия VB.NET того же кода. Мало что вам нужно сделать, прежде чем этот код сможет работать

1) Вам необходимо обратиться к сборке System.DirectoryServices
2) Обязательно передайте переменную "theusername" без домена, поэтому, если ваш домен "ГИС", а ваше имя пользователя - "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

Ответ 7

у нас была аналогичная проблема после обновления контроллера домена до 2012 года. Неожиданно мой вызов user.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);                                        
                }

Ответ 8

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

fooobar.com/questions/201595/...

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

Ответ 9

Опираясь на ту же проблему, перечисляющую группы авторизации, и исправления, отмеченные в ответе, не распространяются на наш веб-сервер.

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

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