Работа с данными Active Directory c помощью сценариев: Часть 3. Проблемы, связанные с обработкой данных, загруженных из LDAP-сервера

Источник: ibm
Рашид Ачилов

Введение

После изучения основного алгоритма обращения к LDAP-серверу через сценарии оболочки с помощью утилиты ldapsearch осталось рассмотреть две процедуры ldap_data_deconvert() и write_when_not_dn().

Проблемы возникающие при обработке данных

Утилита ldapsearch в обязательном порядке выводит строку dn для объекта, поля которого будут распечатаны. В сценарии эту строку необходимо пропустить, но при этом возникает еще одна проблема: если в значении dn есть символы национальных алфавитов, то оно будет представлено base64 кодом. Поскольку значение dn может быть очень длинным, то длина закодированного значения зачастую превышает 80 символов, а у ldapsearch есть еще одна особенность - после 80 символов обязательно ставится символ перевода строки. Это мешает выполнить простой построчный анализ, но и отбрасывать символы перевода строки нельзя, так как тогда исчезнет разделение между атрибутами. Поэтому придется сделать несколько нетривиальных ходов. Для описания ldap_data_deconvert()достаточно сказать, что процедура write_when_not_dn() выводит данные, требующие преобразования в один файл, а не требующие - в другой и пропускает поле dn при чтении. У ldapsearch есть и еще одна особенность - если атрибут содержит данные в base64, то его значение отделяется от имени двумя символами двоеточия вместо одного. Таким образом, если есть второе поле - это данные, которые не требуют перекодировки, а если его нет, но есть третье - это данные, которые необходимо перекодировать.

Листинг 1. Процедура ldap_data_deconvert()
# обратное преобразование данных, прочитанных из AD
# вход:	$1 source (строка) 	- имя файла с исходными данными
#		$2 dest (строка)	- имя файла для записи выходных данных
# выход:	none
ldap_data_deconvert()
{
  local _tempsfx _outfile _tempstr _enconved _skip
  # из строки типа /path/to/filename.ext берется только имя файла
  # первая команда отбрасывает путь (шаблон - наибольший префикс)
  # вторая команда отбрасывает расширение (шаблон - наименьший суффикс)
  _tempsfx=${1##*/}
  _tempsfx=${_tempsfx%.*}
  _outfile=$2
  # удалить из файла все пробелы и комментарии
  _unspace $1 stripped.$_tempsfx
  # разбить по строкам, используя в качестве разделителя символ перевода строки
  saveifs=$IFS
  IFS=$newline
  _skip=0
  _tempstr=""
  _enconved=0
  conved=`cat stripped.$_tempsfx`
  for one in $conved
   do
      # еще разбить построчно используя в качестве разделителя двоеточие
      IFS=:
      set $one
      # если $2 установлен, значит, это значение в коде ASCII
      # которое может быть записано напрямую в выходной файл
      # но только если это не атрибут dn!!!
      write_when_not_dn "$2" $1 1
         # если $3 установлен, значит что значение в коде base64 
         # которое должно быть преобразовано перед записью
         # но только если это не атрибут dn!!!
      write_when_not_dn "$3" $1 0
         # если же одна переменная - это промежуточная строка
         # и она должна быть присоединена к значению буфера 
      if [ ${#2} -eq 0 ] && [ ${#3} -eq 0 ] && [ $_skip -eq 0 ]; then
        _tempstr=$_tempstr$1
      fi
      IFS=$newline
   done
   # если данные не требуют преобразования, то выводим в файл unconved.data,
   # если данные требуют преобразования, то выводим в файл conved.data
   # потому что mmencode пытается преобразовывать все переданные значения
   if [ $_enconved -eq 1 ]; then
     echo $_tempstr >> unconved.data
    else
      echo $_tempstr >> conved.data
   fi
   # сортируем данные в обеих файлах и удаляем дублирующие строки
   cat unconved.data / sort -df / uniq > $_outfile
   cat conved.data / sort -df / uniq > deconved.data
   # построчно перекодируем файл
   deconved=`cat deconved.data`
   for one in $deconved
    do
      echo $one / $util_mmencode -u / $util_iconv -f utf-8 -t koi8-r >> $_outfile
      printf "\n" >> $_outfile
    done
   IFS=$saveifs
}

Первые три строки после объявления переменных - пример того, как можно отбросить путь к файлу (еще для этого используется $(basename $1)) и его расширение, оставив только имя. Затем из файла удаляются все комментарии и пустые строки, и начинается его построчная разборка. Поскольку в выводе ldapsearch имя поля отделяется от данных двоеточием, каждая строка разбивается по этому символу.

Если после разбора есть второе поле, то данные не нужно преобразовывать, достаточно проверить, не dn ли это (вызов процедуры write_when_not_dn()). Если же после разбора остается третье поле (второе при этом не проверяется), то значит, это данные в кодировке base64, и они должны быть преобразованы, опять же, если это не dn. Если же нет ни второго, ни третьего полей - это промежуточная строка закодированных данных, и она должна быть присоединена к буферу, в который помещается недописанная строка. Данные действия выполняются до тех пор, пока не закончится входной файл.

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

После этого остается отсортировать файлы, отобрать уникальные записи и декодировать файл с кодированным текстом. Декодирование идет по одной строке с последующим преобразованием в KOI8 и выводом символа перевода строки после каждой строки.

Последней процедурой является write_when_not_dn(). Она не очень большая, но весьма запутанная, так как в ней используются внешние переменные.

Листинг 2. Процедура write_when_not_dn()
# сформировать файл с данными для преобразования, если это требуется, 
# или файл с данными, не требующими преобразования,
# но только если текущий атрибут не dn!!!
# вход:	$1 line (строка) 		- данные из исходного файла
#		$2 attribute (строка)	- имя атрибута
#		$3 enconved (digit)	- 1 значит данные не требуют преобразования
# выход:	none
write_when_not_dn()
{
  # если не установлен первый параметр $1 - сразу выходим
  [ ${#1} -eq 0 ] && return
  # если промежуточный буфер содержит накопленные данные, их нужно записать
  if [ ${#_tempstr} -ne 0 ] && [ $_skip -ne 1 ]; then
    # если данные не требуют преобразования, записать в один файл,
    # а если требуют, то в другой
    if [ $_enconved -eq 1 ]; then
      echo $_tempstr >> unconved.data
      _enconved=0
     else
       echo $_tempstr >> conved.data
    fi
  fi
  # если текущий атрибут - dn, 
  # пропустить эту и следующую строки до тех пор, пока не начнется новый атрибут
  if [ $2 = "dn" ]; then
    # пропускать следующие строки
    _skip=1
    return
   else
     # использовать следующие строки и накапливать данные во временном буфере
     _skip=0
     _tempstr=$1
     # отметим, если данные не требуют преобразования
     if [ $3 -eq 1 ]; then
       _enconved=1
     fi
  fi
}

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

Если обнаруженный атрибут - dn, то можно пропустить данные, переданные при вызове и все следующие строки, пока снова не будет обнаружено два поля в строке. Иначе поместить данные во временный буфер и, если они не требуют преобразования, отметить это.

Заключение

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

Загрузка

Имя Размер Метод загрузки
ldapshtst.tar.bz2 5KB HTTP 

Страница сайта http://test.interface.ru
Оригинал находится по адресу http://test.interface.ru/home.asp?artId=29553