26

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

Similar Issue with workaround, but not actual solution to existing problem

Similar issue pointing to Microsoft End Point update as culprit

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

фона:

Я использую UserPrincipal.GetAuthorizationGroups разрешений для конкретного хода доступа к странице IIS 7.5 на сервере 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 часов работы. Рабочий сервер обычно перестает нормально функционировать до перезагрузки без разрешения.

На данный момент я пытаюсь уточнить привязку к конкретным контроллерам домена в сети, а также новый 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'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? Также были бы оценены идеи по устранению возможных причин.

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

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

UPDATE 13-JUN-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; 

    } 
+0

** Обновление: ** Там может быть проблема с утилизацией не PrincipalSearchResult. Хотя я не уверен, почему это было бы сейчас проблемой через два года. В настоящее время я включаю элементы iEnumerable в [using] заявлениях, пытаясь отследить проблемы через perfmon. [Ресурс на утечку] (http://stackoverflow.com/questions/10715159/does-principalsearchresultt-automatically-dispose-all-elements-in-its-collecti) – Pynt

+0

** Обновление: ** Все еще было несколько бит которые не были правильно утилизированы. Обновлено сегодня и будет продолжать следить за проблемой. Сбой перечисления группы SID происходил каждые 12 часов в выходные дни и разрешался каждые 12 часов. Мы добавили больше переменных в проблему, которые я ошибочно разместил как «14 мая», но на самом деле начали «16 мая», на 15-м обновлении Windows было установлено множество обновлений .NET. – Pynt

+0

** Обновление: ** Рефакторинг большого кода, поскольку я больше узнаю о правильной утилизации. Я по-прежнему новичок в C# и унаследовал этот проект, поэтому хороший бит для обновления. Проблема все еще происходит, и я все еще оптимизирую код, надеясь на лучшее. В какой-то момент я откажусь от обновлений .NET, которые были установлены 15 мая, и посмотрите, не исчезли ли вещи, но пока я продолжу исправлять плохо реализованный код. – Pynt

ответ

18

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

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

MS blog translated from German

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

SID S-1-18-1 and SID S-1-18-2 can't be mapped

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

Гэри

+0

Спасибо за информацию. Это была наша последняя линия мышления около 27 июня, когда мы создали среду Dev-Server 2012. К сожалению, ресурсы были перенесены в другое место, прежде чем мы смогли выполнить этот тест. Тем не менее, я считаю, что недавние события могут направить меня обратно, чтобы перенести все в среду 2012 года. Между вашим комментарием и выше, я считаю, что ситуация будет решена. Я буду отмечать ответ, когда мне разрешат полностью пройти тест. – Pynt

+0

Я проверил это обновление для наших машин с Windows 7 и обнаружил, что он решил проблему. – Jeff

+0

Спасибо, что поделились исправлением! Это решает нашу проблему! – dnx

1

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

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

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

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

Удачи вам!

+0

Я прочитал немного о нерешенном вопросе SID перед тем, как поставить эту задачу на задний план, но в какой-то момент я снова рассмотрю эту возможную причину. К счастью для меня, мы не в федеративной настройке в настоящий момент, так что только 1 домен беспокоиться. Я не верю, что нашему списку AD потребуется много времени для проверки сиротских SID. К сожалению, сейчас у меня есть 6-месячная задача, поэтому я не знаю, когда мое свободное время позволит провести этот тест. Спасибо за предложение. – Pynt

4

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

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

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

Мы решили, что после установки этого исправления мы были свободны дома. Однако после того, как он был установлен, мы получили другую прерывистую ошибку. Некоторые группы, которые мы проводили допросом, имели свойство 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); 
    } 
} 

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

0

У нас была аналогичная проблема после обновления контроллера домена до 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);           
       } 
2

я испытал код ошибки 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"); 
    } 
} 
4

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

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

Благодарим вас за отличное решение. Хотя я не люблю пустые уловы, он пропустит группу AD, которую он не сможет прочитать и продолжить. –

0

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

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

или загрузить исправление здесь: http://support.microsoft.com/kb/2830145

1

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

1) Вы должны ссылаться на сборочные System.DirectoryServices
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 
0

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

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

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