25 сентября 2014

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

Oracle.NETC#
Мне часто приходится сталкиваться с обработкой информации из 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.
Теги:Active Directoryoraclec#.netdbms_ldap
Хабы: Oracle .NET C#
+6
52,9k 91
Комментарии 2
Похожие публикации
Разработчик C#
от 170 000 ₽TennisiМоскваМожно удаленно
.NET C#/Blazor Developer
от 3 000 до 4 000 $Hand2NoteМожно удаленно
DBA Oracle
до 250 000 ₽ВСКМожно удаленно
Ведущий программист .net
от 70 000 до 120 000 ₽Мечел-СервисЧелябинскМожно удаленно
Middle Backend developer (C#)
от 60 000 ₽Email SoldiersМожно удаленно
▇▅▄▅▅▄ ▇▄▅