[Из песочницы] Управление списком баз 1С 8.2 с помощью Active Directory

Приветствую тебя, уважаемый читатель! По традиции, прошу слишком сильно не пинать, т.к. это мой первый пост.Итак, приблизительно с полгода назад, встала задача автоматизировать управление списком баз 1С (коих развелось уже более 20 штук) у пользователей домена.Делалось это не только удобства ради, но и в рамках проекта по внедрению «ролевой модели доступа». Вкратце, смысл этой модели в том, что каждый пользователь в домене является членом определенной группы (именуемой согласно должности), которая имеет заранее определенный набор привилегий, в том числе и список информационных баз.

Т.к. у нас имеется домен Active Directory, логично использовать групповые политики для выполнения нашей задачи.Гугление выдавало достаточно много реализаций (и даже платных), но все они, чаще всего, сводились к заранее сформированным файлам со списками баз (ibases.v8i). Нам же хотелось: a) Централизованно управлять настройками подключения к информационным базам (у нас клиент-серверный вариант с SQL базами).б) Централизованно управлять списком доступных пользователю информационных баз, согласно его «роли».В итоге я наткнулся на этот блог, в котором было описано решение, отвечавшее всем нашим требованиям.С любезного согласия автора, я хочу поделится этим решением с сообществом, т.к. в свое время мне далеко не сразу повезло натолкнуться на его заметку (может плохо гуглил).По ходу использования нижеприведенного решения в своей корпоративной среде, «вылезло» несколько досадных багов, которые были успешно исправлены, и все прекрасно работает уже больше полугода в нашей компании (а у автора решения, уже больше года).

Итак, приступим.

Шаг 1.

1С 8.2 хранит список информационных баз в файле ibases.v8i, такой файл присутствует в профиле у каждого пользователя. Формат и принцип работы этого файла отлично описаны тут и тут, поэтому я не вижу смысла здесь это повторять.Также, в одном каталоге с файлом ibases.v8i, находится файл 1CEStart.cfg, особенностью этого файла является то, что в нем можно прописать пути к отдельным файлам *.v8i, содержащим параметры подключения к конкретным информационным базам.При запуске, 1С берет параметры подключений к информационным базам из файлов, прописанных в 1CEStart.cfg и помещает их в ibases.v8i. Эту-то особенность мы и будем использовать.Сначала, сформируем файл v8i для каждой информационной базы.Самый простой способ сформировать такой файлик — это кликнуть правой кнопкой на нужной базе в списке, и выбрать пункт «Сохранить ссылку в файл»: imageОднако, следует иметь ввиду, что сформированный таким образом файл v8i содержит некоторые «лишние» строки, которые нам не нужны. Для нормальной работы достаточно оставить только следующее:

[%NAME% ] Connect=Srvr=»%server%»; Ref=»%base%»; ClientConnectionSpeed=Normal App=Auto WA=1 Version=8.2 Далее, необходимо разместить эти файлы в общедоступном, для пользователей локальной сети, месте, и дать права на «чтение». Я не стал заморачиваться, и просто разместил их в папке NETLOGON контроллера домена. Тому есть несколько причин — это и репликация каталога между контроллерами домена, и отказоустойчивость (в силу того, что контроллеров три, и в каждый момент времени хотя-бы один из них доступен).Шаг 2.

Раз мы собираемся управлять списком информационных баз на основе принадлежности пользователя к той или иной группе AD, создадим в ней необходимое количество групп безопасности согласно имеющимся у нас базам 1С: image

Префикс »1C_82» является обязательным, и далее будет понятно для чего.

Теперь, в каждой вновь созданной группе безопасности, в поле «заметки», укажем путь к соответствующему ей файлу v8i: image

На этом с группами закончили.

Шаг 3.

Создаем групповую политику, которая будет запускать следующий vbs скрипт каждый раз при логоне пользователя:

Код на vbs On Error Resume Next Const PROPERTY_NOT_FOUND = &h8000500D Dim sGroupNames Dim sGroupDNs Dim aGroupNames Dim aGroupDNs Dim aMemof Dim oUser Dim tgdn Dim fso Dim V8iConfigFile Dim dir Const ForReading = 1, ForWriting = 2, ForAppending = 8 'Настраиваем лог файл Set fso = CreateObject («Scripting.FileSystemObject») Set WshShell = WScript.CreateObject («Wscript.Shell») strSysVarTEMP = WshShell.ExpandEnvironmentStrings (»%TEMP%») Set oScriptLog = fso.OpenTextFile (strSysVarTEMP + »\_dbconn.log», ForWriting, True) oScriptLog.Write » strToLog = CStr (Date ())+» »+CStr (Time ()) + » — » + «Start…» oScriptLog.WriteLine (strToLog)

'Проверяем, что 1С установлена Set objFSO = CreateObject («Scripting.FileSystemObject») If Not (objFSO.FolderExists («C:\Program Files\1cv82») Or objFSO.FolderExists («C:\Program Files (x86)\1cv82»)) Then strToLog = CStr (Date ())+» »+CStr (Time ()) + » — » + »1C 8.2 not installed… Quit…» oScriptLog.WriteLine (strToLog) WScript.quit End If

'Проверяем есть ли старый файл и удаляем в случае наличия' APPDATA = WshShell.ExpandEnvironmentStrings (»%APPDATA%») v8i = APPDATA + »\1C\1CEStart\ibases.v8i» If fso.FileExists (v8i) Then fso.DeleteFile (v8i) Set V8iConfigFile = fso.CreateTextFile (v8i, True) strToLog = CStr (Date ())+» »+CStr (Time ()) + » — » + «Удален файл v8i и создан новый» oScriptLog.WriteLine (strToLog) ' Если файла нет (1С только установлена), то создаем файла по указанному пути Else Set dir = fso.CreateFolder (APPDATA + »\1C») Set dir = fso.CreateFolder (dir + »\1CEStart») Set V8iConfigFile = fso.CreateTextFile (v8i, True) strToLog = CStr (Date ())+» »+CStr (Time ()) + » — » + «Создан файл v8i» oScriptLog.WriteLine (strToLog) End if

' ' Initialise strings. We make the assumption that every account is a member of two system groups ' sGroupNames = «Authenticated Users (S), Everyone (S)» ' ' Enter the DN for the user account here Set objSysInfo = CreateObject («ADSystemInfo») strUserName = objSysInfo.UserName strToLog = CStr (Date ())+» »+CStr (Time ()) + » — » + «Logged user DN:»+strUserName oScriptLog.WriteLine (strToLog)

' Получаем имя залогиненного пользователя Set oUser = GetObject («LDAP://» + strUserName) If Err.Number <> 0 Then strToLog = CStr (Date ())+» »+CStr (Time ()) + » — » + «There is an error retrieving the account. Please check your distinguished name syntax assigned to the oUser object.» oScriptLog.WriteLine (strToLog) WScript.quit End If ' ' Determine the DN of the primary group ' We make an assumption that every user account is a member of a primary group ' iPgid = oUser.Get («primaryGroupID») sGroupDNs = primgroup (iPgid) tgdn = sGroupDNs ' ' Call a subroutine to extract the group name and scope ' Add the result to the accumulated group name String ' Call Getmemof (tgdn) ' ' Check the direct group membership for the User account ' aMemOf = oUser.GetEx («memberOf») If Err.Number <> PROPERTY_NOT_FOUND Then ' ' Call a recursive subroutine to retrieve all indirect group memberships ' Err.clear For Each GroupDN in aMemof Call AddGroups (GroupDN) Call Getmemof (GroupDN) Next End If

aGroupNames = Split (sGroupNames,»,») aGroupDNs = Split (sGroupDNs,»:»)

'Откидываем все группы, кроме начинающихся с 1C_82 For Each strGroupDN in aGroupDNs if StrComp (Mid (strGroupDN,1,8), «CN=1C_82», vbTextCompare) = 0 Then strToLog = CStr (Date ())+» »+CStr (Time ()) + » — » + «User is member of:» + strGroupDN oScriptLog.WriteLine (strToLog) Set objGroup = GetObject («LDAP://» & strGroupDN) If Err.Number <> 0 Then strToLog = CStr (Date ())+» »+CStr (Time ()) + » — » + «There is an error retrieving the group. Please check your distinguished name syntax assigned to the objGroup object:» + strGroupDN oScriptLog.WriteLine (strToLog) WScript.quit End If strInfo = objGroup.Get («info») strToLog = CStr (Date ())+» »+CStr (Time ()) + » — » + «Group » + strGroupDN +» info field:» + strInfo oScriptLog.WriteLine (strToLog) strAllInfo = strAllInfo & »:» & strInfo End If Next

aInfoStrings = Split (strAllInfo,»:»)

Call WriteDBSettings ()

Sub WriteDBSettings () 'Прописываем ссылки на v8i файлы в 1CEStart.cfg strSysVarAPPDATA = WshShell.ExpandEnvironmentStrings (»%APPDATA%») strDBConfigFilePath = strSysVarAPPDATA + »\1C\1CEStart\1CEStart.cfg» strToLog = CStr (Date ())+» »+CStr (Time ()) + » — » + »1C Config file is:» + strDBConfigFilePath oScriptLog.WriteLine (strToLog)

If (fso.FileExists (strDBConfigFilePath)) Then Set objDBConfigFile = fso.OpenTextFile (strDBConfigFilePath, ForWriting, True) objDBConfigFile.Write » For each strInfo in aInfoStrings objDBConfigFile.WriteLine («CommonInfoBases=» + strInfo) strToLog = CStr (Date ())+» »+CStr (Time ()) + » — » + «Add Line:» + «CommonInfoBases=» + strInfo oScriptLog.WriteLine (strToLog) next 'Изменить на 0, если аппаратные лицензии не используются objDBConfigFile.WriteLine («UseHWLicenses=1») strToLog = CStr (Date ())+» »+CStr (Time ()) + » — » + «Add Line:» + «UseHWLicenses=1» oScriptLog.WriteLine (strToLog) strToLog = CStr (Date ())+» »+CStr (Time ()) + » — » + «Ready» oScriptLog.WriteLine (strToLog) objDBConfigFile.Close Else Set fso = CreateObject («Scripting.FileSystemObject») Set WshShell = WScript.CreateObject («Wscript.Shell») Set objDBConfigFile = fso.OpenTextFile (strDBConfigFilePath, ForWriting, True) objDBConfigFile.Write » For each strInfo in aInfoStrings objDBConfigFile.WriteLine («CommonInfoBases=» + strInfo) strToLog = CStr (Date ())+» »+CStr (Time ()) + » — » + «Add Line:» + «CommonInfoBases=» + strInfo oScriptLog.WriteLine (strToLog) next strToLog = CStr (Date ())+» »+CStr (Time ()) + » — » + »1C Config file» + strDBConfigFilePath + » Not Exist! Create!» oScriptLog.WriteLine (strToLog) WScript.Quit End If

End Sub

'************************************************************************************************* ' End of mainline code '*************************************************************************************************

Function primgroup (groupid) ' This function accepts a primary group id ' It binds to the local domain and returns the DN of the primary group ' David Zemdegs 6 May 2008 ' Dim oRootDSE, oConn, oCmd, oRset Dim ADDomain, srchdmn ' Bind to loca domain Set oRootDSE = GetObject («LDAP://RootDSE») ADDomain = oRootDSE.Get («defaultNamingContext») srchdmn = »» ' ' Initialise AD search and obtain the recordset of groups ' Set oConn = CreateObject («ADODB.Connection») oConn.Open «Provider=ADsDSOObject;» Set oCmd = CreateObject («ADODB.Command») oCmd.ActiveConnection = oConn oCmd.CommandText = srchdmn & »;(objectCategory=Group);» & _ «distinguishedName, primaryGroupToken; subtree» Set oRset = oCmd.Execute ' ' Loop through the recordset and find the matching primary group token ' When found retrieve the DN and exit the loop ' Do Until oRset.EOF If oRset.Fields («primaryGroupToken») = groupid Then primgroup = oRset.Fields («distinguishedName») Exit Do End If oRset.MoveNext Loop ' ' Close and tidy up objects ' oConn.Close Set oRootDSE = Nothing Set oConn = Nothing Set oCmd = Nothing Set oRset = Nothing End Function Sub Getmemof (sDN) ' ' This is recursive subroutine that calls itself for memberof Property ' David Zemdegs 6 May 2008 ' On Error Resume Next Dim oGrp Dim aGrpMemOf Dim sGrpDN Set oGrp = GetObject («LDAP://» & sDN) aGrpMemOf = oGrp.GetEx («memberOf») If Err.Number <> PROPERTY_NOT_FOUND Then ' ' Call a recursive subroutine to retrieve all indirect group memberships ' Err.clear For Each sGrpDN in aGrpMemOf Call AddGroups (sGrpDN) Call Getmemof (sGrpDN) Next End If Err.clear Set oGrp = Nothing End Sub Sub AddGroups (sGdn) ' ' This subroutine accepts a disguished name ' It extracts the RDN as the group name and determines the group scope ' This is then appended to the group name String ' It also appends the DN to the DN String ' Const SCOPE_GLOBAL = &h2 Const SCOPE_LOCAL = &h4 Const SCOPE_UNIVERSAL = &h8 Dim SNewgrp ' ' Retrieve the group name ' iComma = InStr (1, sGdn,»,») sGrpName = Mid (sGdn,4, iComma-4)

' ' Add the results to the group name String ' Check that the group doesnt already exist in the list ' sNewgrp = sGrpName If InStr (1, sGroupNames, SNewgrp,1) = 0 Then sGroupNames = sGroupNames & »,» & SNewgrp End If ' ' Add the Groups DN to the string if not duplicate ' If InStr (1, sGroupDNs, sGdn,1) = 0 Then sGroupDNs = sGroupDNs & »:» & sGdn End If End Sub

Логика работы скрипта следующая:1. Проверяет установлена ли 1С, если нет — скрипт завершается.2. Проверяет существует ли файл ibases.v8i, и перезаписывает его пустым (или создает в случае отсутствия).3. Извлекает все группы из AD, членом которых является пользователь.4. Отбрасывает все, кроме тех, которые начинаются с 1C_82.5. Получает значение атрибута «Notes».6. Прописывает значение этого атрибута в файл 1CEStart.cfgПопутно пишется лог: Для Windows 7 — C:\Users\username\appdata\Local\Temp\_dbconn.logДля Windows XP — C:\Documents and Settings\username\Local Settings\Temp\_dbconn.log

Шаг 4.

«Вешаем» групповую политику на необходимую OU или весь домен. Стоит отметить, чтоб для того, чтоб скрипт не применялся всем подряд без разбора (не все пользователи работают с 1С), я добавил в фильтр безопасности групповой политики только те группы, которые мы создавали на шаге 2, таким образом скрипт будет отрабатывать только у пользователей включенных в хотя-бы одну из этих групп: image

Шаг 5.

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

Спасибо за внимание, буду очень рад, если статья кому-либо поможет.

© Habrahabr.ru