Типовая задача: сотруднику нужна сетевая папка на Windows сервере. Чтобы её создать нужен Админ, чтобы раздать к ней права нужен Админ - убираем Админа из этой задачи при помощи IDM Midpoint.
Упрощённая схема, чтобы понять что происходит
1. Сотрудник в Midpoint запросил роль Create Shared Folder и получил её сразу, или через согласование с кем-нибудь (там же, в Midpoint).
2. Роль создаёт в промежуточном ресурсе запись для этого пользователя через обычный Scheme Handler в CSV файле. Факт создания этой записи запускает Additional Connector который позволяет запустить Powershell скрипт на сервере. Additional Connector реагирует на события создание, удаление, изменение в конкретном Scheme Handler.
3. Скрипт Powershell создаёт сетевую папку, AD группы с правами чтения, редактирования. Добавляет пользователя в группу редактирования (уже может пользоваться).
4. При очередной синхронизации AD ресурса в Midpoint создаются роли для AD group, созданных скриптом. На их выдачу назначается Approver сотрудник запросившей сетевую папку, это делает роль-политика Policy: Add Approver.
5. User B в своём кабинете Midpoint видит новые роли по сетевой папке User A и может их запросить. User A так же в своём кабинете одобряет или отвергает выдачу доступов к своей сетевой папке!
Приступаем к настройке исходные данные:
- Midpoint 4.8.4 полностью настроена интеграция с Windows Server
- Windows Server 2019, LDAPS, Open-SSH (из компонентов)
1. Создаём ресурс CSV Shared Folder
Нам нужен ресурс для запуска скрипта. Им может быть CSV или DB. Для простоты и наглядности берём CSV. Midpoint в нём будет вести состояние - должна быть у пользователя сетевая папка или нет. При выдаче роли создаётся учётка в CSV-ресурсе, что запускает скрипт на создание сетевой папки, при отъёме роли удаляется учётка в CSV-ресурсе, что запускается скрипт на удаление сетевой папки.
На сервере Midpoint в папке
/opt/midpoint
создаем пустой файл
shared_folder.csv
в нем одна строка
hr_id;date_create;info_1;info_2;info_3;info_4
В админке Midpoint создаём ресурс CSV в Resources\New Resource\From Scratch выбираем CsvConnector
Далее все как на картинках, название CSV Shared Folder
Дальше по-умолчанию всё ок
Заходим в ресурс CSV Shared Folder, нажимаем Edit Raw
и добавляем перед
<capabilities>
следующий code
<schemaHandling>
<objectType id="6">
<kind>account</kind>
<intent>Account SCV Shared Folder</intent>
<displayName>Account SCV Shared Folder</displayName>
<default>true</default>
<delineation>
<objectClass>ri:AccountObjectClass</objectClass>
</delineation>
<focus>
<type>c:UserType</type>
</focus>
<attribute id="8">
<ref>ri:hr_id</ref>
<outbound>
<name>01 HR ID to hr_id</name>
<strength>strong</strength>
<source>
<path>$focus/personalNumber</path>
</source>
</outbound>
<inbound id="9">
<name>just for correlation</name>
<strength>strong</strength>
<target>
<path>personalNumber</path>
</target>
<use>synchronization</use>
</inbound>
</attribute>
<attribute id="11">
<ref>ri:info_1</ref>
<outbound>
<name>02 FullName to info_1</name>
<strength>strong</strength>
<source>
<path>$focus/fullName</path>
</source>
</outbound>
</attribute>
<attribute id="12">
<ref>ri:date_create</ref>
<outbound>
<name>03 Date to date_create</name>
<strength>weak</strength>
<source>
<path>$focus/personalNumber</path>
</source>
<expression>
<script>
<code>import java.text.SimpleDateFormat
def date = new Date()
def sdf = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss")
return sdf.format(date)</code>
</script>
</expression>
</outbound>
</attribute>
<correlation>
<correlators>
<items id="686">
<name>Corr HR ID personalNumber</name>
<item id="687">
<ref>c:personalNumber</ref>
</item>
</items>
</correlators>
</correlation>
<synchronization>
<reaction id="17">
<situation>unlinked</situation>
<actions>
<link id="19"/>
</actions>
</reaction>
<reaction id="18">
<situation>deleted</situation>
<actions>
<deleteResourceObject id="20"/>
</actions>
</reaction>
</synchronization>
</objectType>
</schemaHandling>
Этот Scheme Handler берёт из Midpoint у сотрудника personalNumber и вставляет в CSV файл в hr_id. Дату создания в date_create и fullName в info_1. Еще остаются две колонки пустые, на всякий случай.
Теперь настраиваем Additional Connector
Скачиваем со страницы SSH Connector jar файлhttps://docs.evolveum.com/connectors/connectors/com.evolveum.polygon.connector.ssh.SshConnector/
Кладём его в папку
/opt/midpoint/var/connid-connectors
И перезагружаем Midpoint
Смотрим в Repository Objects появился ли ConnId com.evolveum.polygon.connector.ssh.SshConnector v1.0
тут же берем его OID
388af43f-fae8-4734-8f17-c549352d5939
Заходим в ресурс CSV Shared Folder, нажимаем Edit Raw
и добавляем после
</connectorConfiguration>
следующий code
<additionalConnector id="649">
<name>ssh</name>
<connectorRef oid="388af43f-fae8-4734-8f17-c549352d5939" relation="org:default" type="c:ConnectorType">
<!-- ConnId com.evolveum.polygon.connector.ssh.SshConnector v1.0 -->
</connectorRef>
<connectorConfiguration xmlns:icfc="http://midpoint.evolveum.com/xml/ns/public/connector/icf-1/connector-schema-3">
<icfc:configurationProperties xmlns:gen807="http://midpoint.evolveum.com/xml/ns/public/connector/icf-1/bundle/com.evolveum.polygon.connector-ssh/com.evolveum.polygon.connector.ssh.SshConnector">
<gen807:host>192.168.1.168</gen807:host>
<gen807:username>midpoint</gen807:username>
<gen807:password>qwerty123</gen807:password>
</icfc:configurationProperties>
<icfc:resultsHandlerConfiguration>
<icfc:enableNormalizingResultsHandler>false</icfc:enableNormalizingResultsHandler>
<icfc:enableFilteredResultsHandler>false</icfc:enableFilteredResultsHandler>
<icfc:enableAttributesToGetSearchResultsHandler>false</icfc:enableAttributesToGetSearchResultsHandler>
</icfc:resultsHandlerConfiguration>
</connectorConfiguration>
<capabilities>
<native xmlns:cap="http://midpoint.evolveum.com/xml/ns/public/resource/capabilities-3">
<cap:testConnection/>
<cap:script>
<cap:host id="682">
<cap:type>resource</cap:type>
</cap:host>
<cap:host id="683">
<cap:type>connector</cap:type>
</cap:host>
</cap:script>
</native>
<configured xmlns:cap="http://midpoint.evolveum.com/xml/ns/public/resource/capabilities-3">
<cap:script>
<cap:enabled>true</cap:enabled>
</cap:script>
</configured>
</capabilities>
</additionalConnector>
<connectorRef> - указывает ваш OID для SSH Connector
<host> - host
<username> - Логин учётки для управления AD со всеми нужными правами (например из AD Ресурса)
<password> - пароль, его Midpoint сразу зашифрует и в XML не будет показывать в явном виде.
Так же добавляем перед
</resource>
Следующий код
<scripts>
<script>
<host>resource</host>
<language>powershell</language>
<argument>
<name>user_hr_id</name>
<path>$focus/personalNumber</path>
</argument>
<code>powershell.exe -File "C:\Users\midpoint\Documents\shared_folder_create.ps1" %*</code>
<operation>add</operation>
<kind>account</kind>
<intent>Account SCV Shared Folder</intent>
<order>after</order>
</script>
<script>
<host>resource</host>
<language>powershell</language>
<argument>
<name>user_hr_id</name>
<path>$focus/personalNumber</path>
</argument>
<code>powershell.exe -File "C:\Users\midpoint\Documents\shared_folder_delete.ps1" %*</code>
<operation>delete</operation>
<kind>account</kind>
<intent>Account SCV Shared Folder</intent>
<order>after</order>
</script>
</scripts>
Тут запускаются два скрипта
<operation>add</operation> - операция добавления создания
<kind>account</kind>- учетки
<intent>Account SCV Shared Folder</intent> - в Schem Handlere с таким то intent
<order>after</order> - скрипт запускается после создания записи в CSV ресурсе
Описываем передаваемый в скрипт аргумент:
<argument>
<name>user_hr_id</name>
<path>$focus/personalNumber</path>
</argument>
И строка запуска на Windows сервере
<code>powershell.exe -File "C:\Users\midpoint\Documents\shared_folder_create.ps1" %*</code>
Остаётся положить на Windows Server скрипты
Папка
C:\Users\midpoint\Documents\
Имя
shared_folder_create.ps1
Код
param([string]$user_hr_id)
$SF_Name_Read = 'Shared_Folder_READ_' + $user_hr_id
$SF_Name_Edit = 'Shared_Folder_EDIT_' + $user_hr_id
$SF_Name_Folder = 'SF_' + $user_hr_id
$SF_Name_Path = 'C:\Shared Folder\' + $SF_Name_Folder
#Create AD Groups
New-ADGroup -Name $SF_Name_Read -SamAccountName $SF_Name_Read -GroupCategory Security -GroupScope Global -DisplayName $SF_Name_Read -Path "OU=Groups,OU=OOO_ODIN,DC=168testserverhome,DC=com" -Description $user_hr_id
New-ADGroup -Name $SF_Name_Edit -SamAccountName $SF_Name_Edit -GroupCategory Security -GroupScope Global -DisplayName $SF_Name_Read -Path "OU=Groups,OU=OOO_ODIN,DC=168testserverhome,DC=com" -Description $user_hr_id
#add owner user to edit group
Get-ADUser -Properties employeeNumber -Filter 'employeeNumber -eq $user_hr_id' | ForEach-Object {Add-ADGroupMember -Identity $SF_Name_Edit -Members $_.SamAccountName}
#Create Shared Folder
New-Item -Path 'C:\Shared Folder\' -Name $SF_Name_Folder -ItemType 'directory'
$Parameters = @{
Name = $SF_Name_Folder
Path = $SF_Name_Path
ReadAccess = $SF_Name_Read
ChangeAccess = $SF_Name_Edit
}
New-SmbShare @Parameters
#Set access rights for AD groups
$ACL = Get-Acl -Path $SF_Name_Path
$AccessRule = New-Object System.Security.AccessControl.FileSystemAccessRule($SF_Name_Edit,"ListDirectory,Read,ReadAndExecute,Write,Modify,FullControl","3","0","Allow")
$AccessRule2 = New-Object System.Security.AccessControl.FileSystemAccessRule($SF_Name_Read,"ListDirectory,Read,ReadAndExecute","3","0","Allow")
$ACL.SetAccessRule($AccessRule)
$ACL.SetAccessRule($AccessRule2)
$ACL | Set-Acl -Path $SF_Name_Path
Папка
C:\Users\midpoint\Documents\
Имя
shared_folder_delete.ps1
Код
param([string]$user_hr_id)
$SF_Name_Read = 'Shared_Folder_READ_' + $user_hr_id
$SF_Name_Edit = 'Shared_Folder_EDIT_' + $user_hr_id
$SF_Name_Folder = 'SF_' + $user_hr_id
$SF_Name_Path = 'C:\Shared Folder\' + $SF_Name_Folder
$date = Get-Date -Format "yyyyMMddHHmm"
$SF_NEW_Name_Folder = 'ARC_' + $date + '_SF_' + $user_hr_id
#delete groups from shared folder ACL
$acl = Get-Acl $SF_Name_Path
$groupid = New-Object System.Security.Principal.Ntaccount($SF_Name_Read)
$acl.PurgeAccessRules($groupid)
$acl | Set-Acl $SF_Name_Path
$acl = Get-Acl $SF_Name_Path
$groupid = New-Object System.Security.Principal.Ntaccount($SF_Name_Edit)
$acl.PurgeAccessRules($groupid)
$acl | Set-Acl $SF_Name_Path
#delete share folder capability not data
Remove-FileShare -Name $SF_Name_Folder -confirm:$false
#rename ex shared folder
Rename-Item -Path $SF_Name_Path -NewName $SF_NEW_Name_Folder
#delete groups
Remove-ADGroup -Identity $SF_Name_Read -confirm:$false
Remove-ADGroup -Identity $SF_Name_Edit -confirm:$false
Сетевая папка создается в C:\Shared Folder\
Как формируется имя тоже задано в начале скрипта .
У пользователей в AD должен быть заполнен employeeNumber соответсовать personalNumber в Midpoint по ниму в скрипте идет поиск и добавление в группу для редактирования
Get-ADUser -Properties employeeNumber -Filter 'employeeNumber -eq $user_hr_id' | ForEach-Object {Add-ADGroupMember -Identity $SF_Name_Edit -Members $_.SamAccountName}
В ADMINISTRATION\Role\All Roles создаём просто роль (чёрную) под названием Create Shared Folder
Заходим в неё, нажимаем Edit Raw
Вставляем перед
</role>
следующий code
<inducement id="2">
<construction>
<resourceRef oid="8a047f87-2761-45d1-a32a-a64c32f36a84" relation="org:default" type="c:ResourceType">
<!-- CSV Shared Folder -->
</resourceRef>
<kind>account</kind>
<intent>Account SCV Shared Folder</intent>
</construction>
</inducement>
<resourceRef> - OID ресурса CSV Shared Folder, у вас будет свой
Проверяем как работает. Берём пользователя User A, у него уже есть учётка в ресурсе AD. Выдаём ему роль Create Shared Folder.
Смотрим сразу в файле
/opt/midpoint/shared_folder.csv
появилась запись
87328348;10/10/2024 09:35:39;Егеров Егеров Ким;;;
Смотрим в AD
Все создалось!
2. Создаём в Midpoint группы по сетевой папке из ресурса AD
AD группы редактирование и чтение под сетевую папку пользователя создались в AD. Но в Midpoint их пока нет, хотя бы потому что мы не запускали синхронизацию групп AD в роли Midpoint и обычная синхронизация нас не устроит - этим группам ролям при создании в Midpoint нужно назначать одобрителя на выдачу.
Поэтому в Schem Handler’e для прочих групп AD настраиваем фильтр. В Resources\Your AD resource\Scheme Handler\Yout AD group ALL\Basic Attributes на второй странице в Filter пишем
attributes/cn not startsWith 'Shared_Folder_'
Этот Scheme Handler будет обрабатывать только AD группы сn которых не начинается на Shared_Folder_...
Создаём Scheme Handler под сетевые папки, я назову его Groups Shared Folder POC
В Resources\Your AD resource\Scheme Handler\Add object type запоняем как на картинках
Заходим в созданный Schema Handler и редактируем дальше в квадратиках
Groups Shared Folder POC\Mappings
02 - Здесь script
return true
05 - Здесь script („Win-ikpur723q06“ hostname Windows сервера)
"Rights for user HRID:" + input + " shared folder path \\\\Win-ikpur723q06\\sf_" + input
06 - Пишем в атрибут Documentation табельный номер который в AD группе писали в Description
03-04 - здесь конструкция для назначения роли по OID, её можно только в код вставить, делайте сначала пустой, а потом заполняете.
<inbound id="11472">
<name>03 add POLICY add approver from role atribute</name>
<lifecycleState>active</lifecycleState>
<strength>strong</strength>
<expression>
<assignmentTargetSearch>
<targetType>c:RoleType</targetType>
<oid>a0f4de80-c941-43bd-9216-966bc3a62d3e</oid>
</assignmentTargetSearch>
</expression>
<target>
<path>$focus/assignment</path>
</target>
</inbound>
Роль в маппинге 03, которая будет назначать одобрителя ещё не создана. А роль в мапинге 04 копирует роль для обычных групп AD, только со своими именами.
Groups Shared Folder POC\Synchronization
Groups Shared Folder POC\Correlation
По настройкам Scheme Handler почти всё, нам надо создать роль, которая будет добавлять одобрителя в роли сетевых папок и её OID написать в маппинг 03
Полностью кодом Scheme Handler Groups Shared Folder POC
<objectType id="747">
<kind>entitlement</kind>
<intent>intent Groups Shared Folder POC</intent>
<displayName>Groups Shared Folder POC</displayName>
<delineation>
<objectClass>ri:group</objectClass>
<filter>
<q:text>attributes/cn startsWith 'Shared_Folder_'</q:text>
</filter>
</delineation>
<focus>
<type>c:RoleType</type>
</focus>
<attribute id="749">
<ref>ri:cn</ref>
<inbound id="750">
<name>01 name is name</name>
<strength>strong</strength>
<target>
<path>name</path>
</target>
</inbound>
<inbound id="774">
<name>02 set requestable mark</name>
<strength>strong</strength>
<expression>
<script>
<code>return true</code>
</script>
</expression>
<target>
<path>requestable</path>
</target>
</inbound>
<inbound id="11472">
<name>03 add POLICY add approver from role atribute</name>
<lifecycleState>active</lifecycleState>
<strength>strong</strength>
<expression>
<assignmentTargetSearch>
<targetType>c:RoleType</targetType>
<oid>a0f4de80-c941-43bd-9216-966bc3a62d3e</oid>
</assignmentTargetSearch>
</expression>
<target>
<path>$focus/assignment</path>
</target>
</inbound>
<inbound id="11473">
<name>04 add ADD AD group SHARED FOLDER</name>
<lifecycleState>active</lifecycleState>
<strength>strong</strength>
<expression>
<assignmentTargetSearch>
<targetType>c:RoleType</targetType>
<oid>99cefb8d-2aa8-4015-b427-b9fde2fe50a9</oid>
</assignmentTargetSearch>
</expression>
<target>
<path>$focus/assignment</path>
</target>
</inbound>
</attribute>
<attribute id="751">
<ref>ri:objectGUID</ref>
</attribute>
<attribute id="753">
<ref>ri:description</ref>
<inbound id="754">
<name>05 some human readable text</name>
<strength>strong</strength>
<expression>
<script>
<code>"Rights for user HRID:" + input + " shared folder path \\\\Win-ikpur723q06\\sf_" + input</code>
</script>
</expression>
<target>
<path>description</path>
</target>
</inbound>
<inbound id="11488">
<name>06 user HR id for approver set</name>
<strength>strong</strength>
<target>
<path>documentation</path>
</target>
</inbound>
</attribute>
<attribute id="11475">
<ref>ri:dn</ref>
<inbound id="11479">
<name>07 dn for identifier</name>
<strength>strong</strength>
<target>
<path>identifier</path>
</target>
</inbound>
</attribute>
<correlation>
<correlators>
<items id="767">
<item id="768">
<ref>c:identifier</ref>
</item>
</items>
</correlators>
</correlation>
<synchronization>
<reaction id="756">
<situation>unlinked</situation>
<actions>
<link id="761"/>
</actions>
</reaction>
<reaction id="757">
<situation>linked</situation>
<actions>
<synchronize id="762"/>
</actions>
</reaction>
<reaction id="758">
<situation>unmatched</situation>
<actions>
<addFocus id="763"/>
</actions>
</reaction>
</synchronization>
</objectType>
Добавить одобрителя через маппинг не удаётся известными способами. Поэтому создаём роль с политикой, запускающей скрипт, который всё может. Эту роль политику и назначаем каждой роли-группе AD, созданной в Scheme Handler Groups Shared Folder POC.
В ADMINISTRATION\Role\All Roles создаём просто роль (чёрную) под названием POLICY add approver from role atribute
Заходим в неё, нажимаем Edit Raw
Вставляем перед
</role>
Следующий код
<inducement id="2">
<policyRule>
<name>add approver from role atribute</name>
<policyConstraints>
<modification id="30">
<item>c:documentation</item>
</modification>
</policyConstraints>
<policyActions>
<scriptExecution id="5">
<name>some script</name>
<executeScript xmlns:s="http://midpoint.evolveum.com/xml/ns/public/model/scripting-3">
<s:pipeline list="true">
<s:action>
<s:type>execute-script</s:type>
<s:parameter>
<s:name>script</s:name>
<s:value>
<code>
import com.evolveum.midpoint.xml.ns._public.common.common_3.*
import com.evolveum.midpoint.prism.delta.builder.*
import com.evolveum.midpoint.model.api.*
import com.evolveum.midpoint.schema.constants.SchemaConstants
import javax.xml.namespace.QName
role = midpoint.getObject(RoleType.class, input.oid)
userId = basic.stringify(role.documentation)
query_user = midpoint.queryFor(UserType.class, "personalNumber = '$userId'")
result_USER = midpoint.searchObjects(query_user)
userApprover = new ObjectReferenceType()
userApprover.setOid(input.oid)
userApprover.setType(RoleType.COMPLEX_TYPE)
userApprover.setRelation(SchemaConstants.ORG_APPROVER)
addAssignment = new AssignmentType()
addAssignment.setTargetRef(userApprover)
def delta = []
delta = prismContext.deltaFor(UserType.class).item(FocusType.F_ASSIGNMENT).add(addAssignment.asPrismContainerValue()).asObjectDelta(result_USER.oid)
midpoint.modifyObject(delta, ModelExecuteOptions.createRaw())
midpoint.recompute(UserType.class, basic.stringify(result_USER.oid))
</code>
</s:value>
</s:parameter>
</s:action>
</s:pipeline>
</executeScript>
</scriptExecution>
</policyActions>
</policyRule>
<focusType>c:RoleType</focusType>
</inducement>
При изменении в роли атрибута documentation (это включает и его первое заполнение) будет запускаться скрипт, который назначает одобрителем этой роли того, чей табельный номер(HRID) указан в роли в атрибуте documentation.
Берём OID роли POLICY add approver from role atribute и вставляем его в Groups Shared Folder POC в маппинге 03
Теперь в AD ресурсе можно запускать синхронизацию Scheme Handler’а Groups Shared Folder POC
Начинают появляться роли-AD группы
Name как cn в AD (надо быть уверенным, что он будет уникальным для Midpoint также как и для AD). Description заполнен по скрипту. И Requestable проставлен True.
Пользователь с табельным номером 87328348 одобритель роли Shared_Folder_EDIT_87328348.
3. Запрашиваем права к сетевой папке
У нас в Midpoint два пользователя с ролями
User A
- End User
- Approver
- AD account
- Create Shared Folder
- Shared_Folder_EDIT_87328348
- Shared_Folder_EDIT_87328348
- Shared_Folder_READ_87328348
User B
- End User
- AD account
End User - стандартная роль Midpoint, нам от неё нужно то, что она позволяет заходить на GUI Midpoint и запрашивать роли, помеченные Requestable как True
Approver - стандартная роль Midpoint, позволяет одобрять запросы на добавление в роль
Shared_Folder_EDIT_87328348 - в пользователе будет видна как две отдельные роли, в списке видимые как одинаковые. Одна придёт как выданная с синхронизацией AD, а вторая будет помечена как функционал approver.
Заходим под User B в Midpoint, идём в Request Access и запрашиваем роль Shared_Folder_READ_87328348.
Теперь заходим в Midpoint под User A видим пришёл запрос, одобряем его
Заходим ещё раз в Midpoint пользователем User B, на главной странице видим в My accesses что появилась запрошенная роль, и сам запрос в My request тоже видно, что одобрен.
Теперь заходим под пользователем User B на Windows ПК, если зашли раньше то выходим и заходим по новой, и видим что читать можем, а редактировать нет!
Accep
Здравствуйте, у вас очень полезные статьи, не могли бы вы рассказать про настройку соединения с AD через LDAP, буду благодарен.