С чего начинается дружба с Active Directory

    Мне часто приходится сталкиваться с обработкой информации из Active Directory. Так как данная система много где распространена, я решил поделиться своим опытом работы с ней.

    Началось всё с того, что я раздавал доступ в MS Sharepoint. В заявках от сотрудников, в лучшем случае, приходили учетные записи, в худшем могли написать что-то типа “Мне и моему начальнику”. Это поначалу напрягало, и я, чувствуя себя заправским админом, посылал такие заявки обратно нерадивым юзерам, чтобы они всё переделали. Это превращалось в ворох пропитанных гневом писем и звонков. Я даже стал задумываться, не случайно ли совпадение сокращения от Active Directory – AD с русским словом “Ад”. В итоге, осознав, что надо менять мир к лучшему, я решил детально разобраться, что вообще хранится в Active Directory и как это можно использовать, для автоматической раздачи доступа и других полезных вещей.

    В компании информация о сотрудниках хранится в 2-х местах:
    • Active Directory
    • БД Oracle

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

    Решение этой задачи было найдено и растиражировано во множество утилит и программных продуктов, активно используемых внутри компании. Одной из наиболее используемых утилит стала программа поиска информации по сотруднику. Она ищет сотрудника или сотрудников по различным критериям и выдаёт наиболее полную информацию о них, совмещая данные из Active Directory и Oracle. Особой популярностью пользуются фотографии сотрудников, которые хранятся в базе данных.

    Так же стоит отметить интеграцию Active Directory не только с Oracle но и с MS Sharepoint. Благодаря найденному решению появилось приложение способное быстро дать доступ группе пользователей из Active Directory на страницы портала Sharepoint, а так же удалить из него пользователей, которые по различным причинам, ушли из неё.

    Задача была решена 2-мя способами:
    1. Для получения информации по одному сотруднику — DBMS_LDAP в Oracle
    2. Для того чтобы выкачать все доступные записи и посмотреть, что вообще есть в Active Directory использовали код на C#

    При использовании C# следует учесть следующее (библиотека System.DirectoryServices.dll):
    • Свойство DirectorySearcher.PageSize должно быть отлично от 0, если хотите чтобы функция FindAll() вернула все записи.
    • Обязательно надо вызвать Dispose() для экземпляра DirectorySearcher

    Так же есть свойство DirectorySearcher.SizeLimit, которое устанавливает максимальное количество записей в возвращаемом результате. По умолчанию оно равно 0, что означает – взять это значение с сервера. На сервере обычно стоит значение 1000. Можно поиграть со свойством SizeLimit и посмотреть, как меняется результат работы функции (ссылка).

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

    Примеры кода:


    • DBMS_LDAP Oracle
      function GetDataByUser(userAccount VARCHAR2)
      return VARCHAR2 is
      
      l_ldap_host VARCHAR2(256) := 'host';
      l_ldap_port VARCHAR2(256) := 'port';
      l_ldap_user VARCHAR2(256) := 'user';
      l_ldap_passwd VARCHAR2(256) := 'password';
      l_ldap_base VARCHAR2(256) := 'ldap_path';
      
      l_retval PLS_INTEGER;
      l_session DBMS_LDAP.session;
      l_attrs DBMS_LDAP.string_collection;
      l_message DBMS_LDAP.message;
      l_entry DBMS_LDAP.message;
      l_attr_name VARCHAR2(256);
      l_ber_element DBMS_LDAP.ber_element;
      l_vals DBMS_LDAP.string_collection;
      
      ret VARCHAR2(256);
      
      begin
      
         DBMS_LDAP.USE_EXCEPTION := TRUE;
         l_session := DBMS_LDAP.init(hostname => l_ldap_host,portnum => l_ldap_port);
         l_retval := DBMS_LDAP.simple_bind_s(ld => l_session,dn => l_ldap_user,passwd => l_ldap_passwd);
         l_attrs(1) := 'postalcode';
         l_retval := DBMS_LDAP.search_s(ld => l_session,base => l_ldap_base,scope => DBMS_LDAP.SCOPE_SUBTREE,filter => '(&(objectClass=user)(cn=' ||userAccount || ')(mail=*))',attrs => l_attrs,attronly => 0,res => l_message);
      
         IF DBMS_LDAP.count_entries(ld => l_session, msg => l_message) > 0 THEN
            l_entry := DBMS_LDAP.first_entry(ld => l_session,msg => l_message);
            l_attr_name := DBMS_LDAP.first_attribute(ld => l_session, ldapentry => l_entry, ber_elem => l_ber_element);
            l_vals := DBMS_LDAP.get_values (ld => l_session, ldapentry => l_entry, attr => l_attr_name);
            ret := l_vals(0);
         END IF;
      
         l_retval := DBMS_LDAP.unbind_s(ld => l_session);
         return ret;
      end GetDataByUser; 
      

    • С#

              static void Main(string[] args) 
              { 
                  var listDict = ActiveDirectoryTraversal(); 
                  var d = listDict[0]; 
              } 
              private static List<Dictionary<string, object>> ActiveDirectoryTraversal() 
              { 
                  List<Dictionary<string, object>> ret = null; 
                  var dep = new DirectoryEntry(); 
                  dep.AuthenticationType = AuthenticationTypes.FastBind; 
                  dep.Path = "LDAP://yourpath"; 
      
                  using(DirectorySearcher ds = new DirectorySearcher(dep)) 
                  { 
                      ds.Filter = "(&(objectClass=user))"; 
                     //ds.SizeLimit = 5; 
                      ds.PageSize = 100; 
                      using (SearchResultCollection results = ds.FindAll()) 
                      { 
                          var e = results.Count; 
                          if (results != null && results.Count > 0) 
                          { 
                            ret = SaveData(results); 
                          } 
                      } 
                  } 
                  dep.Close(); 
                  return ret; 
              } 
              private static List<Dictionary<string, object>> SaveData(SearchResultCollection results) 
              { 
                  var ret = new List<Dictionary<string, object>>(); 
                  for (int i = 0; i < results.Count; i++) 
                  { 
                      var res = results[i]; 
                      var dict = new Dictionary<string, object>(); 
                      foreach (var e in res.Properties.PropertyNames) 
                      { 
                          if (!dict.ContainsKey(e.ToString())) 
                              dict.Add(e.ToString(), res.Properties[e.ToString()][0]); 
                      } 
                      ret.Add(dict); 
                  } 
                  return ret; 
              }
      


    Заключение:


    Умение работать с Active Directory и обращаться к нему напрямую из БД Oracle существенно упростило работу программистам отдела, где я работаю. Изначально я, ради эксперимента, выкачал содержимое Active Directory в таблицу базы данных. Потом, к своему удивлению, я обнаружил ряд хранимых процедур, которые работали с этой таблицей. Выяснилось, что разработчики обращались к ней, поленившись разобраться в пакете DBMS_LDAP. Этот прецедент и подтолкнул меня к желанию рассказать и привести примеры того как можно работать с Active Directory.

    Комментарии 2

      0
      Запустил скрипт на C#, вывел в консоль имена компьютеров, закрыл. Можно ли обычному пользователю извлечь из этой информации пользу, если ты не админ и задач по интеграции и распределению прав нет?
        0
        Я намеренно вывел всю информацию в словарь, но у меня есть приложение которое выводит на экран всю информацию для пользователя. Просто я не знаю, насколько правомерно это делать, не набрав кармы для режима я пиарюсь. Напишите мне в диалог, обсудим проблему более детально.

      Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

      Самое читаемое