Управление списком баз 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 = "<LDAP://" & ADDomain & ">" ' ' 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С, которые им необходимы. После перезагрузки у пользователя будет индивидуальный именно ему список информационных баз.
Ну вот вроде-бы и все.
Кстати, для применения изменений, пользователю не обязательно перелогиниваться, нужно просто заставить пользователя выполнить этот скрипт любым удобным способом, к примеру, отправив скрипт по электронной почте.

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

ссылка на оригинал статьи http://habrahabr.ru/post/231119/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *