Продолжаем реализацию концепции Сотрудник-Трудоустройство-Назначение на должность в IDM Midpoint. В первой части мы настроили носители информации и пути её перетекания из профиля сотрудника в трудоустройства и назначения из кадрового источника. Теперь будем делать тоже самое с ролями. Нам потребуется еще две концепции Forward роли и nickName как роль.

Что будет работать в конце: Выдача ролей на трудоустройство и назначение. Выдача учеток AD на трудоустройство и назначение. Отбор AD групп пришедших в трудоустройство от назначения при выдаче назначению AD учетки. Роль генерирующая логин и раздающая его всем учеткам.
Реализация Forward роли
В Midpoint вшиты и мало применимы, потому что привязаны к одному архетипу, но ценны как логические элементы следующие типы ролей:
Бизнес роль - состоит из Системных ролей
Системные роли - выдают что-то в ресурсе
В Midpoint в GUI они прописаны и раскрашены - это можно игнорировать но как тип-подход-формулировку учитывать! Нам нужен еще один тип Forward роли, они по аналоги с Forward Contract из финансовой сферы, будут нам формировать список тех ролей которы должны быть на трудоустройстве или назначении. Такой заявительный формат прав позволяет нам их привязать к трудоустройству или назначению и отобрать их в случае исчезновения оных.
И тогда по типам у нас будут такие роли:
Бизнес роль - состоит из Системных ролей или Forward ролей
Forward роли - пишут в носителя что должно быть из Системных ролей
Системные роли - выдают что-то в ресурсе
Например для MS AD group мы будем делать две роли и Системные и Forward. Forward будет запрашивать сотрудник, а Системные получает его Аккаунт как User.
Для начала добавим атрибутов в Midpoint. Как обычно в папку /opt/midpoint/var/schema файл employment_part2.xsd
<xsd:schema elementFormDefault="qualified" targetNamespace="http://example.com/xml/ns/mySchema"
xmlns:tns="http://example.com/xml/ns/mySchema"
xmlns:a="http://prism.evolveum.com/xml/ns/public/annotation-3"
xmlns:c="http://midpoint.evolveum.com/xml/ns/public/common/common-3"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:complexType name="RoleExtensionType">
<xsd:annotation>
<xsd:appinfo>
<a:extension ref="c:RoleType"/>
</xsd:appinfo>
</xsd:annotation>
<xsd:sequence>
<xsd:element name="role_purpose_type" type="xsd:string" minOccurs="0" maxOccurs="1">
<xsd:annotation>
<xsd:appinfo>
<a:indexed>true</a:indexed>
<a:displayName>Purpose type</a:displayName>
<a:displayOrder>156</a:displayOrder>
<a:help>ToDo</a:help>
</xsd:appinfo>
</xsd:annotation>
</xsd:element>
<xsd:element name="role_root_system" type="xsd:string" minOccurs="0" maxOccurs="1">
<xsd:annotation>
<xsd:appinfo>
<a:indexed>true</a:indexed>
<a:displayName>Root system</a:displayName>
<a:displayOrder>156</a:displayOrder>
<a:help>ToDo</a:help>
</xsd:appinfo>
</xsd:annotation>
</xsd:element>
<xsd:element name="user_forward_roles" type="xsd:string" minOccurs="0" maxOccurs="unbounded">
<xsd:annotation>
<xsd:appinfo>
<a:indexed>true</a:indexed>
<a:displayName>Users Forward Roles</a:displayName>
<a:displayOrder>138</a:displayOrder>
<a:help>ToDo</a:help>
</xsd:appinfo>
</xsd:annotation>
</xsd:element>
<xsd:element name="user_forward_roles_inherited" type="xsd:string" minOccurs="0" maxOccurs="unbounded">
<xsd:annotation>
<xsd:appinfo>
<a:indexed>true</a:indexed>
<a:displayName>Users Inherited Forward Roles</a:displayName>
<a:displayOrder>139</a:displayOrder>
<a:help>ToDo</a:help>
</xsd:appinfo>
</xsd:annotation>
</xsd:element>
<xsd:element name="user_account_number" type="xsd:string" minOccurs="0" maxOccurs="1">
<xsd:annotation>
<xsd:appinfo>
<a:indexed>true</a:indexed>
<a:displayName>Account Number</a:displayName>
<a:displayOrder>148</a:displayOrder>
<a:help>ToDo</a:help>
</xsd:appinfo>
</xsd:annotation>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="UserExtensionType">
<xsd:annotation>
<xsd:appinfo>
<a:extension ref="c:UserType"/>
</xsd:appinfo>
</xsd:annotation>
<xsd:sequence>
<xsd:element name="person_forward_roles" type="xsd:string" minOccurs="0" maxOccurs="unbounded">
<xsd:annotation>
<xsd:appinfo>
<a:indexed>true</a:indexed>
<a:displayName>Forward Roles</a:displayName>
<a:displayOrder>138</a:displayOrder>
<a:help>ToDo</a:help>
</xsd:appinfo>
</xsd:annotation>
</xsd:element>
<xsd:element name="person_forward_roles_inherited" type="xsd:string" minOccurs="0" maxOccurs="unbounded">
<xsd:annotation>
<xsd:appinfo>
<a:indexed>true</a:indexed>
<a:displayName>Inherited Forward Roles</a:displayName>
<a:displayOrder>139</a:displayOrder>
<a:help>ToDo</a:help>
</xsd:appinfo>
</xsd:annotation>
</xsd:element>
<xsd:element name="person_account_number" type="xsd:string" minOccurs="0" maxOccurs="1">
<xsd:annotation>
<xsd:appinfo>
<a:indexed>true</a:indexed>
<a:displayName>Account Number</a:displayName>
<a:displayOrder>148</a:displayOrder>
<a:help>ToDo</a:help>
</xsd:appinfo>
</xsd:annotation>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
</xsd:schema>
Перезапускаем Midpoint
И данные у нас в /opt/midpoint/var/info/POC_EMPLOYMENT_DATA.csv
такие
number_poce;type_poce;main_id;parent_id;members_poce;member_of_poce;name_poce;grade_poce;title_poce;department_poce;subordinate_to_poce;status_poce;info_01;info_02;info_03
1;user;600667;;;EMP002001,EMP002002;;;;;;active;;;
2;employment;EMP002001;EMP001001;600667;POS100995,POS100996,POS000101,POS100108,POS100171,POS100345;;;Основное;;;active;;;
3;employment;EMP002002;EMP001002;600667;POS000125,POS000124;;;Совместительство;;;disabled;;;
4;position;POS000101;EMP002001;;;;;Системный Администратор;;600110;active;;;
5;position;POS000125;EMP002002;;;;;Уборщик;;;disabled;;;
6;position;POS000124;EMP002002;;;;;Грузчик;;;disabled;;;
7;position;POS100108;EMP002001;;;;;Принеси воды;;;disabled;;;
8;position;POS100171;EMP002001;;;;;Курьер;;;active;;;
9;position;POS100345;EMP002001;;;;;Сборщик мебели;;;active;;;
10;position;POS100995;EMP002001;;;;;Бухгалтер;;;active;;;
11;position;POS100996;EMP002001;;;;;Кассир;;;disabled;;;
12;employment;EMP002003;EMP001001;600110;POS100885,POS100886;;;Основное;;;active;;;
13;user;600110;;;EMP002003;;;;;;active;;;
14;position;POS100885;EMP002003;;;;;Биг Босс;;;active;;;
15;position;POS100886;EMP002003;;;;;Кассир;;;active;;;
На каждую Forward роль будут навешиваться роли политики описывающие как она себя будет вести - кому отдаваться, что писать.
Поэтому сначала создаем эти роли
В Administration\Roles создаем обычную роль черную под названием POCE Policy: Forward Role for Employment in EMP01001 Company
в неё добавляем код
<inducement id="2">
<policyRule>
<name>Member must have same Company</name>
<policyConstraints>
<requirement id="3">
<targetRef oid="e23bf649-5d84-440f-9993-818b4960bfcf" relation="org:default" type="c:RoleType">
<!-- EMP001001 -->
</targetRef>
</requirement>
</policyConstraints>
<policyActions>
<enforcement>
<name>Send ERROR</name>
</enforcement>
</policyActions>
<evaluationTarget>assignment</evaluationTarget>
</policyRule>
</inducement>
Политика показывает ошибку и предлагает изменить выбор если запрашивается или выдаётся Forward роль трудоутсройство или назначению у которой нет роли карточки организации.
Чтобы эта политика сработала пришлось всем трудоустройствам-назначениям выдать роль из организации.
В Configuration\Object Template\ добавялем в POCE Position User Object Template и в POCE Employment User Object Template
код
<item id="3">
<ref>assignment</ref>
<displayName>Assignment to Organization Role</displayName>
<mapping id="9">
<source>
<path>organization</path>
</source>
<expression>
<assignmentTargetSearch>
<targetType>RoleType</targetType>
<filter>
<q:text>identifier = $organization and archetypeRef matches (oid = "f44dc355-31d3-499b-9854-e0ae277a60dc")</q:text>
</filter>
</assignmentTargetSearch>
</expression>
</mapping>
</item>
Здесь обратите внимание что у нас в assignmentTargetSearch конкретный поиск с указанием архетипа ролей карточек Организации. Это правильно, ранее я просто искал по identifier в надежде что он уникален, хотя он не name и не обязан.
Для каждой компании нужна такая политика в Administration\Roles создаем обычную роль черную под названием POCE Policy: Forward Role for Employment in EMP01002 Company
в неё добавляем код
<inducement id="2">
<policyRule>
<name>Member must have same Company</name>
<policyConstraints>
<requirement id="3">
<targetRef oid="9fad33eb-d7cb-4aea-a828-835665d6ce9b" relation="org:default" type="c:RoleType">
<!-- EMP001002 -->
</targetRef>
</requirement>
</policyConstraints>
<policyActions>
<enforcement>
<name>Enf</name>
</enforcement>
</policyActions>
<evaluationTarget>assignment</evaluationTarget>
</policyRule>
</inducement>
Так же у нас будут роли политики запрещающие назначать роль предназначенные для назначения на трудоустройства и набоорот.
В Administration\Roles создаем обычную роль черную под названием POCE Policy: Forward Role only for Position User
в неё добавляем код
<inducement id="30">
<policyRule>
<name>Member must have archetype POCE Position User ArcgeType</name>
<policyConstraints>
<requirement id="31">
<targetRef oid="0d1b1269-0011-49e6-b9f1-e62e7827dfed" relation="org:default" type="c:ArchetypeType">
<!-- POCE Position User ArcheType -->
</targetRef>
</requirement>
</policyConstraints>
<policyActions>
<enforcement>
<name>Enf</name>
</enforcement>
</policyActions>
<evaluationTarget>assignment</evaluationTarget>
</policyRule>
</inducement>
oid у вас будет другой, для каждой установки Midpoint oid`ы свои
В Administration\Roles создаем обычную роль черную под названием POCE Policy: Forward Role only for Employment User
в неё добавляем код
<inducement id="2">
<policyRule>
<name>Member must have archetype POCE Employment User ArcgeType</name>
<policyConstraints>
<requirement id="3">
<targetRef oid="441f8e23-33cd-41f2-bbb6-beea20feeaee" relation="org:default" type="c:ArchetypeType">
<!-- POCE Employment User ArcheType -->
</targetRef>
</requirement>
</policyConstraints>
<policyActions>
<enforcement>
<name>Enf</name>
</enforcement>
</policyActions>
<evaluationTarget>assignment</evaluationTarget>
</policyRule>
</inducement>
Так же у нас будет функциональная роль политики которая будет писать в User какая роль у него должна быть
В Administration\Roles создаем обычную роль черную под названием POCE POLICY: identifier from Forward Role to User Forward Roles List
в неё добавляем код
<inducement id="3">
<identifier>write to User from Role</identifier>
<focusMappings>
<mapping id="4">
<name>01 some</name>
<strength>strong</strength>
<source>
<path>name</path>
</source>
<source>
<path>extension/person_account_number</path>
</source>
<source>
<path>extension/person_employment_parent</path>
</source>
<expression>
<script>
<code>import com.evolveum.midpoint.xml.ns._public.common.common_3.*
forward_role = midpoint.getObject(RoleType.class, assignmentPath[0].target.oid)
return ( basic.stringify(basic.getExtensionPropertyValue(forward_role, "http://example.com/xml/ns/mySchema", "role_root_system")) + "|" + basic.stringify(basic.getExtensionPropertyValue(forward_role, "http://example.com/xml/ns/mySchema", "user_employment_parent")) + "|" + basic.stringify(assignmentPath[0].target.identifier + "|" + name + "|" + person_employment_parent + "|" + person_account_number))</code>
</script>
</expression>
<target>
<path>$user/extension/person_forward_roles</path>
</target>
</mapping>
</focusMappings>
<focusType>c:UserType</focusType>
<condition>
<source>
<path>$user/activation/administrativeStatus</path>
</source>
<expression>
<script>
<code>
if (basic.stringify(administrativeStatus) == "DISABLED")
{return false}
else
{return true}
</code>
</script>
</expression>
</condition>
</inducement>
Тут прописано условие что пишем если User трудоустройство-назначение не заблокирован, это сразу(при реконсиляции) отбирает роль если в кадровом источники назначение вдруг заблокировано.
Создаем Архетип под Forward роли
В Configuration\ArchyTypes создаем архетип под названием POCE Forward Role ArcheType
в нем код
<assignment id="1">
<identifier>holderType</identifier>
<activation>
<effectiveStatus>enabled</effectiveStatus>
</activation>
<assignmentRelation id="2">
<holderType>RoleType</holderType>
</assignmentRelation>
</assignment>
...
<archetypePolicy>
<display>
<icon>
<cssClass>fa fa-clipboard-check</cssClass>
<color>#d88222</color>
</icon>
</display>
...
</archetypePolicy>
</archetype>
Создаем Object Template для архетипа POCE Forward Role ArcheType
В Configuration\Object Template\ добавляем под именем POCE Forward Role Object Template
добавляем код
<item id="13">
<ref>inducement</ref>
<displayName>Auto inducement to POCE: identifier from Forward Role to User Forward Roles List</displayName>
<mapping id="9">
<authoritative>true</authoritative>
<strength>strong</strength>
<source>
<path xmlns:gen569="http://example.com/xml/ns/mySchema">c:extension/gen569:role_root_system</path>
</source>
<expression>
<assignmentTargetSearch>
<targetType>RoleType</targetType>
<oid>d7763c65-c038-432f-9a12-01d99d0f38ce</oid>
</assignmentTargetSearch>
</expression>
<condition>
<script>
<code>
if (role_root_system)
{return true}
else{return false}
</code>
</script>
</condition>
</mapping>
</item>
<item id="14">
<ref>assignment</ref>
<displayName>Auto assignment to POCE Policy: Forward Role only for Position User</displayName>
<mapping id="9">
<authoritative>true</authoritative>
<strength>strong</strength>
<source>
<path xmlns:gen569="http://example.com/xml/ns/mySchema">c:extension/gen569:role_purpose_type</path>
</source>
<expression>
<assignmentTargetSearch>
<targetType>RoleType</targetType>
<oid>1a4b2799-1357-4e23-bbbf-ab8031de889f</oid>
</assignmentTargetSearch>
</expression>
<condition>
<script>
<code>if (role_purpose_type == "Position")
{return true}
else{return false}</code>
</script>
</condition>
</mapping>
</item>
<item id="15">
<ref>assignment</ref>
<displayName>Auto assignment to POCE Policy: Forward Role only for Employment User</displayName>
<mapping id="9">
<authoritative>true</authoritative>
<strength>strong</strength>
<source>
<path xmlns:gen569="http://example.com/xml/ns/mySchema">c:extension/gen569:role_purpose_type</path>
</source>
<expression>
<assignmentTargetSearch>
<targetType>RoleType</targetType>
<oid>971c7f3e-4c00-4ba4-ad66-27aca05f742f</oid>
</assignmentTargetSearch>
</expression>
<condition>
<script>
<code>if (role_purpose_type == "Employment")
{return true}
else{return false}</code>
</script>
</condition>
</mapping>
</item>
<item id="16">
<ref>assignment</ref>
<displayName>Auto assignment to POCE Policy: Forward Role for Employment in EMP01001 Company</displayName>
<mapping id="9">
<authoritative>true</authoritative>
<strength>strong</strength>
<source>
<path xmlns:gen569="http://example.com/xml/ns/mySchema">c:extension/gen569:user_employment_parent</path>
</source>
<expression>
<assignmentTargetSearch>
<targetType>RoleType</targetType>
<oid>8e4b4ea4-22b7-4b09-98ce-aa8fd5ace849</oid>
</assignmentTargetSearch>
</expression>
<condition>
<script>
<code>if (user_employment_parent == "EMP001001")
{return true}
else{return false}</code>
</script>
</condition>
</mapping>
</item>
<item id="17">
<ref>assignment</ref>
<displayName>Auto assignment to POCE Policy: Forward Role for Employment in EMP01002 Company</displayName>
<mapping id="9">
<authoritative>true</authoritative>
<strength>strong</strength>
<source>
<path xmlns:gen569="http://example.com/xml/ns/mySchema">c:extension/gen569:user_employment_parent</path>
</source>
<expression>
<assignmentTargetSearch>
<targetType>RoleType</targetType>
<oid>047dcf56-3555-4940-aac4-50b15b9071a3</oid>
</assignmentTargetSearch>
</expression>
<condition>
<script>
<code>if (user_employment_parent == "EMP001002")
{return true}
else{return false}</code>
</script>
</condition>
</mapping>
</item>
oid меняте на свои
Тут прямолинейно расписано что выдавать в зависимости от аттрибутов Forward роли
Утыкаем этот Object Template в архетип POCE Forward Role ArcheTypeв AcrheType Policy\ArcheType Policy
Создаем ресурс на коннекторе ConnId com.evolveum.polygon.connector.ldap.LdapConnector v3.8 под названием Windows MS AD OOO ODIN Forward Roles я его копирую из рабочего Windows MS AD OOO ODIN тут нужны просто настройки коннектора для подключения, все Schema Handler удаляем
Добавляем свой Schema Handler
<schemaHandling>
<objectType id="6">
<kind>entitlement</kind>
<intent>innie MS AD OOO ODIN Groups to Forward Roles</intent>
<displayName>MS AD OOO ODIN Groups to Forward Roles</displayName>
<delineation>
<objectClass>ri:group</objectClass>
</delineation>
<focus>
<type>c:RoleType</type>
<archetypeRef oid="b527aa8f-9097-45d7-94c9-8c2d79e53832" relation="org:default" type="c:ArchetypeType">
<!-- POCE Forward Role ArcheType -->
</archetypeRef>
</focus>
<attribute id="8">
<ref>ri:cn</ref>
<inbound id="9">
<name>01 name</name>
<strength>strong</strength>
<expression>
<script>
<code>"OOO ODIN FR:" + basic.stringify(input)</code>
</script>
</expression>
<target>
<path>name</path>
</target>
</inbound>
<inbound id="14">
<name>03 system type</name>
<strength>strong</strength>
<expression>
<script>
<code>return "MSAD"</code>
</script>
</expression>
<target>
<path>extension/role_root_system</path>
</target>
</inbound>
<inbound id="15">
<name>04 purpose Position or Employment</name>
<strength>weak</strength>
<expression>
<script>
<code>return "Position"</code>
</script>
</expression>
<target>
<path>extension/role_purpose_type</path>
</target>
</inbound>
<inbound id="16">
<name>05 root company</name>
<strength>strong</strength>
<expression>
<script>
<code>return "EMP001001"</code>
</script>
</expression>
<target>
<path>extension/user_employment_parent</path>
</target>
</inbound>
<inbound id="30">
<name>06</name>
<strength>weak</strength>
<expression>
<script>
<code>return "5"</code>
</script>
</expression>
<target>
<path>riskLevel</path>
</target>
</inbound>
</attribute>
<attribute id="11">
<ref>ri:distinguishedName</ref>
<inbound id="12">
<name>02</name>
<strength>strong</strength>
<target>
<path>identifier</path>
</target>
</inbound>
</attribute>
<correlation>
<correlators>
<items id="26">
<item id="27">
<ref>identifier</ref>
</item>
</items>
</correlators>
</correlation>
<synchronization>
<reaction id="19">
<situation>unmatched</situation>
<actions>
<addFocus id="20"/>
</actions>
</reaction>
<reaction id="21">
<situation>linked</situation>
<actions>
<synchronize id="22"/>
</actions>
</reaction>
<reaction id="23">
<situation>unlinked</situation>
<actions>
<link id="32"/>
</actions>
</reaction>
</synchronization>
</objectType>
</objectType>
</schemaHandling>
...
</resource>
Так как мы знаем что эти Forward роли от комапнии OOO ODIN прописываем сразу как заполнять атрибут extension/user_employment_parent. А так же заполняем riskLevel и extension/role_purpose_type но маппинг делаем weak чтобы можно было в Midpoint их менять(уточнять).
Запускаем реконсиляцию нашего Scheme Handler и получаем кучу Forward ролей
Вот так они заполнены

В Forward роли в Assignment ограничивающие роли политики...

А в Inducement роли политики действий на носители Forward роли!

Накидаем назначению Forward ролей

Заполнился список Forward Roles, в строке вся нужная информация, с ней и будем работать дальше!
Этот список нам надо втащить в роли Трудоустройства и Назначения на должность - надо будет дозаполнить схему из первой части!
Добавляем в архетип POCE Employment Role ArcheType
код
<inducement id="1226">
<lifecycleState>active</lifecycleState>
<focusMappings>
<mapping id="7">
<documentation>get Forward Roles from user employment</documentation>
<authoritative>true</authoritative>
<strength>strong</strength>
<expression>
<script>
<relativityMode>absolute</relativityMode>
<code>
linkedDATA = midpoint.findLinkedSource('From role to Employment User')
if (basic.isEmpty(linkedDATA))
{return "NO CONNECTION"}
else
{
return basic.getExtensionPropertyValues(linkedDATA, "http://example.com/xml/ns/mySchema", "person_forward_roles")
}
</code>
</script>
</expression>
<target>
<path>$focus/extension/user_forward_roles</path>
<set>
<predefined>all</predefined>
</set>
</target>
</mapping>
</focusMappings>
</inducement>
А в архетип POCE Employment User ArcheType
<inducement id="1222">
<policyRule>
<name>Recompute Employment Role on Employment User Forward Roles change</name>
<policyConstraints>
<or id="13">
<modification id="14">
<item xmlns:gen604="http://example.com/xml/ns/mySchema">c:extension/gen604:person_forward_roles</item>
</modification>
</or>
</policyConstraints>
<policyActions>
<scriptExecution id="16">
<object>
<linkTarget id="17">
<linkType>from Employment Role to me</linkType>
</linkTarget>
</object>
<executeScript xmlns:s="http://midpoint.evolveum.com/xml/ns/public/model/scripting-3">
<s:recompute/>
</executeScript>
</scriptExecution>
</policyActions>
</policyRule>
</inducement>
А также
В архетип POCE Position Role ArcheType
<inducement id="1226">
<focusMappings>
<mapping id="7">
<name>11</name>
<documentation>get Forward Roles from user</documentation>
<authoritative>true</authoritative>
<strength>strong</strength>
<expression>
<script>
<code>linkedDATA = midpoint.findLinkedSource('from me Position Role to Position User')
if (basic.isEmpty(linkedDATA))
{return "NO CONNECTION"}
else
{
return basic.getExtensionPropertyValues(linkedDATA, "http://example.com/xml/ns/mySchema", "person_forward_roles")}</code>
</script>
</expression>
<target>
<path>$focus/extension/user_forward_roles</path>
<set>
<predefined>all</predefined>
</set>
</target>
</mapping>
</focusMappings>
</inducement>
А в архетип POCE Position User ArcheType
<inducement id="1222">
<policyRule>
<name>Recompute Position Role on Position User Forward Roles change</name>
<policyConstraints>
<or id="13">
<modification id="3342">
<item>c:activation/c:administrativeStatus</item>
</modification>
<modification id="3344">
<item xmlns:gen26="http://example.com/xml/ns/mySchema">c:extension/gen26:person_forward_roles</item>
</modification>
</or>
</policyConstraints>
<policyActions>
<scriptExecution id="16">
<object>
<linkTarget id="17">
<linkType>From Position Role to Position User</linkType>
</linkTarget>
</object>
<executeScript xmlns:s="http://midpoint.evolveum.com/xml/ns/public/model/scripting-3">
<s:recompute/>
</executeScript>
</scriptExecution>
</policyActions>
</policyRule>
</inducement>
И еще, я так же хочу чтобы записи о Forward ролях из назначения попадали в трудоустройство.
В Архетипе POCE Employment User ArcheType добавляем
<inducement id="122">
<focusMappings>
<mapping id="8">
<name>01</name>
<authoritative>false</authoritative>
<exclusive>true</exclusive>
<strength>strong</strength>
<expression>
<script>
<code>
fr_list = []
fr_postion_roles = midpoint.findLinkedTargets('from me to Position role').collect { basic.getExtensionPropertyValues(it, "http://example.com/xml/ns/mySchema", "user_forward_roles")}
for (i in fr_postion_roles)
{
fr_num = 0
for(ii in i)
{
if (ii.startsWith("NICKNAME") or ii.startsWith("NO CONNECTION"))
fr_num = 1
}
if (fr_num !=1)
{for (ii in i){fr_list.add(ii)}}
}
return fr_list
</code>
</script>
</expression>
<target>
<path>extension/person_forward_roles_inherited</path>
</target>
</mapping>
</focusMappings>
</inducement>
И в архетип POCE Position Role ArcheType
<inducement id="1234">
<policyRule>
<name>Recompute Users Employment</name>
<policyConstraints>
<or id="13">
<modification id="22">
<item xmlns:gen365="http://example.com/xml/ns/mySchema">c:extension/gen365:user_forward_roles</item>
</modification>
<modification id="222"/>
<modification id="21227"/>
</or>
</policyConstraints>
<policyActions>
<scriptExecution id="16">
<object>
<linkSource id="131">
<name>from Employment User to me</name>
</linkSource>
</object>
<executeScript xmlns:s="http://midpoint.evolveum.com/xml/ns/public/model/scripting-3">
<s:recompute/>
</executeScript>
</scriptExecution>
</policyActions>
</policyRule>
</inducement>
Теперь создаем роль которая будет выдавать учетку AD для трудоустройства
Назовем её OOO ODIN FR: Employment MS AD Account и будет она с архетипом POCE Forward Role ArcheType
Заполнием её так

За счет архетипа на неё сразу навешиваются нужные политики
Не сразу но было замечено что мы не можем в User что-то писать(как делают наши Forward роли) и одновременно создавать из него Persona все свершается но вылезает ошибка
Object with conflicting normalized name '600667 emp002001 employment ms ad account' already exists
Поэтому для создание Persona создаем обычную черную роль POCE Persona Employment Account
в ней код
<inducement id="4">
<personaConstruction>
<targetType>UserType</targetType>
<objectMappingRef oid="781070d6-01f0-45a9-aaa7-93a721b6f62c" relation="org:default" type="c:ObjectTemplateType">
<!-- POCE Person Employment MS AD Account Object Template -->
</objectMappingRef>
<archetypeRef oid="48077d8a-7cce-4999-b7cb-d31620983c1d" relation="org:default" type="c:ArchetypeType">
<!-- POCE Employment MS AD Account -->
</archetypeRef>
</personaConstruction>
</inducement>
Этот код создает Person User тому User с этой ролью, тут мы указывает OID архетипа который будет у Person и Object Template который их связывает на уровне перетекания данных. Чтобы эта роль выдалась надо в Object Template POCE Position User Object Template
<item id="16">
<ref>assignment</ref>
<displayName>Auto assignment Persona role for Employment Acount</displayName>
<mapping id="9">
<name>Name</name>
<authoritative>true</authoritative>
<strength>strong</strength>
<source>
<path xmlns:gen569="http://example.com/xml/ns/mySchema">c:extension/gen569:person_forward_roles</path>
</source>
<expression>
<assignmentTargetSearch>
<targetType>RoleType</targetType>
<oid>22b6f13a-7275-4fe2-af4c-f5f8b13f6ca9</oid>
</assignmentTargetSearch>
</expression>
<condition>
<script>
<relativityMode>absolute</relativityMode>
<code>
response = 0
for (i in person_forward_roles)
{
if (i.startsWith("NICKNAME"))
{response = 1}
}
if (response == 0)
{return false}
else
{return true}</code>
</script>
</condition>
</mapping>
</item>
Роль OOO ODIN FR: Employment MS AD Account пишет нам в person_forward_roles строку начинающеюся на NICKNAME на этом оснавании выдаем роль Persona и информационные потоки не тупят.
Создаем Object Template
В Configuration\Object Template\ добавляем под именем POCE Person Employment MS AD Account Object Template
<mapping id="8">
<name>01</name>
<source>
<path>c:givenName</path>
</source>
<expression/>
<target>
<path>c:givenName</path>
</target>
</mapping>
<mapping id="9">
<name>02</name>
<source>
<path>c:familyName</path>
</source>
<expression/>
<target>
<path>c:familyName</path>
</target>
</mapping>
<mapping id="10">
<name>03</name>
<source>
<path>c:additionalName</path>
</source>
<expression/>
<target>
<path>c:additionalName</path>
</target>
</mapping>
<mapping id="11">
<name>04</name>
<source>
<path>c:personalNumber</path>
</source>
<expression/>
<target>
<path>c:personalNumber</path>
</target>
</mapping>
<mapping id="12">
<name>05</name>
<source>
<path>c:personalNumber</path>
</source>
<source>
<path>c:organizationalUnit</path>
</source>
<expression>
<script>
<code>personalNumber + " " + organizationalUnit + " Employment MS AD Account"</code>
</script>
</expression>
<target>
<path>c:name</path>
</target>
</mapping>
<mapping id="13">
<name>06</name>
<source>
<path>c:costCenter</path>
</source>
<expression/>
<target>
<path>c:costCenter</path>
</target>
</mapping>
<mapping id="14">
<name>07</name>
<source>
<path>c:organizationalUnit</path>
</source>
<expression/>
<target>
<path>c:organizationalUnit</path>
</target>
</mapping>
<mapping id="15">
<name>08</name>
<source>
<path>c:organization</path>
</source>
<expression/>
<target>
<path>c:organization</path>
</target>
</mapping>
<mapping id="16">
<name>09</name>
<source>
<path xmlns:gen974="http://example.com/xml/ns/mySchema">c:extension/gen974:person_employment_type</path>
</source>
<expression/>
<target>
<path xmlns:gen695="http://example.com/xml/ns/mySchema">c:extension/gen695:person_employment_type</path>
</target>
</mapping>
<mapping id="17">
<name>10</name>
<exclusive>true</exclusive>
<source>
<path>c:extension/person_forward_roles</path>
</source>
<source>
<_metadata id="136">
<provenance>
<acquisition id="137">
<actorRef oid="00000000-0000-0000-0000-000000000002" relation="org:default" type="c:UserType">
<!-- administrator -->
</actorRef>
<channel>http://midpoint.evolveum.com/xml/ns/public/common/channels-3#user</channel>
<timestamp>2025-04-03T09:54:04.257Z</timestamp>
</acquisition>
</provenance>
</_metadata>
<path xmlns:gen492="http://example.com/xml/ns/mySchema">c:extension/gen492:person_forward_roles_inherited</path>
</source>
<expression>
<script>
<relativityMode>absolute</relativityMode>
<code>
result = []
for (i in person_forward_roles)
{
if (i.startsWith("MSAD"))
{ii = i.tokenize( '|' )
result.add(ii[2])}
}
for (i in person_forward_roles_inherited)
{
if (i.startsWith("MSAD"))
{ii = i.tokenize( '|' )
if (ii[5] != "1")
{
result.add(ii[2])
}
}
}
return result.unique()</code>
</script>
</expression>
<target>
<path>c:extension/person_forward_roles</path>
</target>
</mapping>
<mapping id="110">
<name>adminStatus to persona</name>
<source>
<path>c:activation/c:administrativeStatus</path>
</source>
<expression/>
<target>
<path>c:activation/c:administrativeStatus</path>
</target>
</mapping>
<mapping id="111">
<name>11</name>
<authoritative>true</authoritative>
<strength>strong</strength>
<expression>
<script>
<code>'0'</code>
</script>
</expression>
<target>
<path xmlns:gen23="http://example.com/xml/ns/mySchema">c:extension/gen23:person_account_number</path>
</target>
</mapping>
В мапинге 10 прописано что в Persona из extension/person_forward_roles будут попадать только записи начинающиеся на MSAD из трудоустройства, и с MSAD и person_account_number не 1 из назначений. Отдельно пищем 0 в person_account_number в персоне, это будет влиять на формировании логина в AD.
Создаем Архетип
В Configuration\ArchyTypes создаем архетип под названием POCE Employment MS AD Account
в нем код
...
<archetypePolicy>
<display>
<icon>
<cssClass>fa fa-hard-hat</cssClass>
<color>#d88222</color>
</icon>
</display>
</archetypePolicy>
</archetype>
Тут пока пусто, только подкрасили
Создаем Object Template
В Configuration\Object Template\ добавляем под именем POCE Employment MS AD Account Object Template
Утыкаем этот Object Template в архетип POCE Employment MS AD Account в AcrheType Policy\ArcheType Policy
Даем Трудоустройству User роль OOO ODIN FR: Employment MS AD Account
Первым делом она в extension/person_forward_roles у Трудоустройства User добавляет
NICKNAME|EMP001001|Employment MS AD Account
Потом создает Person копируя все что есть и нужно из Трудоустройства User получается так

У нас в Forward Roles в User аккаунт для User трудоустройства скопировались по Persona Object Template все AD группы какие должны быть на аккаунте по трудоустройству и из назначений.

Снизу еще два назначения в EMP002001 трудоустройстве
У нас от прошлой части в роли назначения на позицию статус от пользователя трудойстройства, втаскиваем туда стаус от пользователя назначение на должность
В архетипе POCE Position Role ArcheType
менем код этот
<inducement id="56">
<focusMappings>
<mapping id="7">
<documentation>administrativeStatus From User to Position Role</documentation>
<authoritative>false</authoritative>
<strength>strong</strength>
<expression>
<script>
<code>
import com.evolveum.midpoint.xml.ns._public.common.common_3.ActivationStatusType;
linkedSource = midpoint.findLinkedSource('from Employment User to me')
if (linkedSource != null)
{return basic.stringify(linkedSource.activation.administrativeStatus)}
else
{return 'UNDEFIEND'}
</code>
</script>
</expression>
<target>
<path>$focus/extension/user_administrativeStatus</path>
</target>
</mapping>
</focusMappings>
</inducement>
на вот этот
<inducement id="56">
<focusMappings>
<mapping id="7">
<documentation>administrativeStatus From User Position to Position Role</documentation>
<authoritative>false</authoritative>
<strength>strong</strength>
<expression>
<script>
<code>
import com.evolveum.midpoint.xml.ns._public.common.common_3.ActivationStatusType
linkedSource = midpoint.findLinkedSource('from me Position Role to Position User')
if (basic.isEmpty(linkedSource))
{return 'NO CONNECTION'}
else
{
if (basic.isEmpty(linkedSource.activation.effectiveStatus))
{return 'UNDIFIEND'}
else
{return basic.stringify(linkedSource.activation.effectiveStatus)}
}
</code>
</script>
</expression>
<target>
<path>$focus/extension/user_administrativeStatus</path>
</target>
</mapping>
</focusMappings>
</inducement>
В архетипе POCE Position User ArcheType в политике Recompute Position Role on Position User Forward Roles change добавляем при изменении administrativeStatus тоже!
Теперь nickName как роль

Нам нужно сгенерировать nickName но хорошо бы его генерировать не здесь, но сейчас. Этот nickName понадобиться другим учеткам сотрудника, так что он должен быть сам по себе и он будет ролью! Роль которая добавится User сотруднику, вытянет из него ФИО, и добавиться User аккаунту, отдаст ему nickName. Тут все нужное есть, кроме назначения роли пользователя, Midpoint этого не делает, роль не знает кому она назначена и вообще не следит за этим, можно только пользователю назначить роль - и придётся делать это политикой!(кстати в 3-ей части это будет полность переделано, из-за ненастраиваемых прав запускать эту политику не админом)
Создаем черную роль называем POCE POLICY: Assiment of User to Role from extension\user_personalNumber
вставляем код
<inducement id="9">
<_metadata id="8">
<storage>
<modifyTimestamp>2025-03-31T07:46:02.350Z</modifyTimestamp>
<modifierRef oid="00000000-0000-0000-0000-000000000002" relation="org:default" type="c:UserType">
<!-- administrator -->
</modifierRef>
<modifyChannel>http://midpoint.evolveum.com/xml/ns/public/common/channels-3#user</modifyChannel>
</storage>
</_metadata>
<policyRule>
<name>Assiment of a User to Role</name>
<policyConstraints>
<modification id="16">
<name>add</name>
<operation>add</operation>
</modification>
</policyConstraints>
<policyActions>
<scriptExecution id="11">
<name>Some script</name>
<object>
<currentObject>
<type>c:RoleType</type>
</currentObject>
</object>
<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.*
role = midpoint.getObject(RoleType.class, input.oid)
userId = basic.stringify(basic.getExtensionPropertyValue(role, "http://example.com/xml/ns/mySchema", "user_personalNumber"))
query_user = midpoint.queryFor(UserType.class, "personalNumber = '$userId' and archetypeRef matches (oid = '00000000-0000-0000-0000-000000000702')")
result_USER = midpoint.searchObjects(query_user)
if (result_USER)
{
user_oid = basic.stringify(result_USER.oid)
user = midpoint.getObject(UserType.class, user_oid)
assRole = new ObjectReferenceType()
assRole.setOid(input.oid)
assRole.setType(RoleType.COMPLEX_TYPE)
addAssignment = new AssignmentType()
addAssignment.setTargetRef(assRole)
def delta = []
delta = prismContext.deltaFor(UserType.class).item(FocusType.F_ASSIGNMENT).add(addAssignment.asPrismContainerValue()).asObjectDelta(user.oid)
midpoint.modifyObject(delta, ModelExecuteOptions.createRaw())
}
midpoint.recompute(UserType, user.oid)
midpoint.recompute(RoleType, input.oid)
</code>
</s:value>
</s:parameter>
</s:action>
</s:pipeline>
</executeScript>
</scriptExecution>
</policyActions>
</policyRule>
<activation/>
</inducement>
...
</role>
Странное поведение, команда в коде Assignment вроде делает свою работу но не до конца надо еще обоих рекомпутить... Тоесть на пользователе в Assigment все выдалось, а до All Access не дошло!
Создаем Object Template под названием POCE nickName Object Template
вставляем код
<item id="17">
<ref>assignment</ref>
<displayName>Auto assignment to POCE POLICY: Assiment of User to Role from extension\user_personalNumber</displayName>
<mapping id="9">
<authoritative>true</authoritative>
<strength>strong</strength>
<expression>
<assignmentTargetSearch>
<targetType>RoleType</targetType>
<oid>7656e85c-0652-4a1a-839f-b823a8d7c246</oid>
</assignmentTargetSearch>
</expression>
</mapping>
</item>
<mapping id="2">
<name>generate-displayname</name>
<description>Generate displayName for nickName Role</description>
<strength>strong</strength>
<source>
<path>extension/user_personalNumber</path>
</source>
<source>
<path>emailAddress</path>
</source>
<expression>
<script>
<code>
user_personalNumber + " nickName [" + emailAddress + "]"
</code>
</script>
</expression>
<target>
<path>displayName</path>
</target>
</mapping>
<mapping id="2222">
<name>Generate uniq nickName for AD and Midpoint</name>
<lifecycleState>active</lifecycleState>
<strength>strong</strength>
<source>
<path>c:extension/user_givenName</path>
</source>
<source>
<path>c:extension/user_familyName</path>
</source>
<source>
<path>c:extension/user_additionalName</path>
</source>
<expression>
<script>
<code>
import com.evolveum.midpoint.xml.ns._public.common.common_3.*;
import com.evolveum.midpoint.prism.query.OrderDirection;
import com.evolveum.midpoint.prism.path.ItemPath;
iterationToken = 0
is_it_ok = false
resource_oid = "b8618fba-cf8b-416c-8e3b-32ea34cf003d"
resource_kind = "ACCOUNT"
resource_intent = "intent MS AD account"
do {
userGivenName = basic.stringify(user_givenName)?.tr(' ', '')
userFamilyName = basic.stringify(user_familyName)?.tr(' ', '')
useradditionalName = basic.stringify(user_additionalName)?.tr(' ', '')
national_letters =['Я','я','Ю','ю','Ч','ч','Ш','ш','Щ','щ','Ж','ж','А','а','Б','б','В','в','Г','г','Д','д','Е','е','Ё','ё','Э','э','З','з','И','и','Й','й','К','к','Л','л','М','м','Н','н', 'О','о','П','п','Р','р','С','с','Т','т','У','у','Ф','ф','Х','х','Ц','ц','Ы','ы','Ь','ь','Ъ','ъ',' ']
latin_letters =['Ya','ya','Yu','yu','Ch','ch','Sh','sh','Sh','sh','Zh','zh','A','a','B','b','V','v','G','g','D','d','E','e','E','e','E','e','Z','z','I','i','J','j','K','k','L','l','M','m','N','n', 'O','o','P','p','R','r','S','s','T','t','U','u','F','f','H','h','C','c','Y','y','','','','','']
for( i = 0; i < national_letters.size(); i++)
{
userGivenName = userGivenName?.replace(national_letters[i],latin_letters[i]);
userFamilyName = userFamilyName?.replace(national_letters[i],latin_letters[i]);
useradditionalName = useradditionalName?.replace(national_letters[i],latin_letters[i]);
}
if ((iterationToken.toInteger() & 1) == 0)
{
second_letter = iterationToken.toInteger() / 2;
first_letter = second_letter.toInteger() + 1;
}
else
{
first_letter = (iterationToken.toInteger() + 1) / 2;
second_letter = first_letter;
}
if(first_letter.toInteger() > userGivenName.length() && second_letter.toInteger() > useradditionalName.length())
{
user_number = iterationToken.toInteger()
first_full = userGivenName.length()
second_full = useradditionalName.length()
if(useradditionalName.length() != 0)
{
name_login_for_user = userGivenName.substring(0,first_letter.toInteger()) + useradditionalName.substring(0,second_letter.toInteger()) + userFamilyName
def query = midpoint.queryFor(ShadowType.class, "resourceRef matches (oid = '" + resource_oid + "') and kind = '" + resource_kind + "' and intent = '" + resource_intent + "' and attributes/cn = '" + name_login_for_user + "'")
def result_ad_login = midpoint.searchObjects(query);
if (midpoint.isUniquePropertyValue(user, 'name', name_login_for_user.toString()) > !result_shadow_search)
{return name_login_for_user
is_it_ok = true}
}
else
{return userGivenName.substring(0,first_full.toInteger()) + userFamilyName + user_number}
}
else
{if (first_letter.toInteger() > userGivenName.length())
{
first_letter = userGivenName.length();
}
if (second_letter.toInteger() > useradditionalName.length())
{
second_letter = useradditionalName.length();
}
name_login_for_user = userGivenName.substring(0,first_letter.toInteger()) + useradditionalName.substring(0,second_letter.toInteger()) + userFamilyName
def query = midpoint.queryFor(ShadowType.class, "resourceRef matches (oid = '" + resource_oid + "') and kind = '" + resource_kind + "' and intent = '" + resource_intent + "' and attributes/sAMAccountName = '" + name_login_for_user + "'")
def result_ad_login = midpoint.searchObjects(query);
if (midpoint.isUniquePropertyValue(user, 'name', name_login_for_user.toString()) && !result_ad_login)
{return name_login_for_user
is_it_ok = true}
}
iterationToken = iterationToken + 1
} while (!is_it_ok)
</code>
</script>
</expression>
<target>
<path>emailAddress</path>
</target>
</mapping>
...
</objectTemplate>
oid роли OCE POLICY: Assiment of User to Role from extension\user_personalNumber
Никнейм генерю в emailAddress тем же способом что и в статьях ранее.
Создаем Arche Type под названием POCE nickName Role archeType
<archetypePolicy>
<display>
<icon>
<cssClass>fa fa-passport</cssClass>
<color>#d88222</color>
</icon>
</display>
...
</archetypePolicy>
</archetype>
Утыкаем его в Object Template под названием POCE nickName Object Template
Теперь идем в Object Template для User Аккаунта POCE Employment MS AD Account Object Template
вставляем код
<item id="1">
<ref>assignment</ref>
<displayName>Assignment or creation of nickName role</displayName>
<mapping id="9">
<source>
<path>personalNumber</path>
</source>
<expression>
<assignmentTargetSearch>
<targetType>RoleType</targetType>
<filter>
<q:text>extension/user_personalNumber = $personalNumber and archetypeRef matches (oid = "7f71dcf3-c89d-4e23-930b-215423af3849")</q:text>
</filter>
<createOnDemand>true</createOnDemand>
<populateObject>
<populateItem>
<expression>
<script>
<code>
personalNumber + " nickName"
</code>
</script>
</expression>
<target>
<path>name</path>
</target>
</populateItem>
<populateItem>
<expression>
<script>
<code>
personalNumber
</code>
</script>
</expression>
<target>
<path>extension/user_personalNumber</path>
</target>
</populateItem>
<populateItem>
<expression>
<assignmentTargetSearch>
<targetType>ArchetypeType</targetType>
<oid>7f71dcf3-c89d-4e23-930b-215423af3849</oid>
</assignmentTargetSearch>
</expression>
<target>
<path>assignment</path>
</target>
</populateItem>
</populateObject>
</assignmentTargetSearch>
</expression>
</mapping>
</item>
Этот айтем говорит дай нам роль nickName и тут же если её нет то создай с такими то данными и таким то Архетипом.
Теперь в Архетипе POCE Employment MS AD Account
добавляем
<inducement id="372">
<focusMappings>
<mapping id="8">
<authoritative>false</authoritative>
<strength>strong</strength>
<expression>
<script>
<relativityMode>absolute</relativityMode>
<code>
linkedDATA = midpoint.findLinkedTarget('from nickName Role to User Account AD Emp')
return linkedDATA.emailAddress
</code>
</script>
</expression>
<target>
<path>nickName</path>
</target>
<condition>
<script>
<code>
linkedDATA = midpoint.findLinkedTarget('from nickName Role to User Account AD Emp')
if (basic.isEmpty(linkedDATA))
{return false}
else
{return !basic.isEmpty(linkedDATA.emailAddress)}
</code>
</script>
</condition>
</mapping>
</focusMappings>
</inducement>
...
<targetLink id="12">
<name>from nickName Role to User Account AD Emp</name>
<selector>
<type>c:RoleType</type>
<archetypeRef oid="7f71dcf3-c89d-4e23-930b-215423af3849" relation="org:default" type="c:ArchetypeType">
<!-- POCE nickName Role archeType -->
</archetypeRef>
</selector>
</targetLink>
</links>
</archetypePolicy>
</archetype>
Читаем emailAddress из роли nickName и пишим в nickName
Даем нашему Трудоустройству роль OOO ODIN FR: Employment MS AD Account
Видим появился User аккаунт 600667 EMP002001 Employment MS AD Account
Посмотрим в него

В Forward Roles собрано все что касается MSAD с трудоутсройства и назначений. Уже есть nickName. Смотрим что в Assignment видим там роль 600667 nickName [ATigr] уже с никнеймом. Посмотрим кто у ней в member

Все правильно в членах - и User сотрудник и User аккаунт трудоустройство - роль nickName и связывает и генерит nickName. На User аккаунт назначение не смотрите он из будущего.
Осталось еще чуть-чуть - сделать AD учетку и выдать Системные AD группы роли!
В архетип POCE Employment MS AD Account
<inducement id="2">
<construction>
<resourceRef oid="b8618fba-cf8b-416c-8e3b-32ea34cf003d" relation="org:default" type="c:ResourceType">
<!-- Windows MS AD OOO ODIN -->
</resourceRef>
<kind>account</kind>
<intent>intent MS AD account</intent>
</construction>
<focusType>c:UserType</focusType>
<condition>
<source>
<path>nickName</path>
</source>
<expression>
<script>
<code>!basic.isEmpty(nickName)</code>
</script>
</expression>
</condition>
</inducement>
Делать учетку nickName если там не пусто
И рекомпутим если nickName только появился или поменялся
<inducement id="375">
<policyRule>
<name>Recompute on nickName change</name>
<policyConstraints>
<or id="384">
<modification id="3815">
<operation>add</operation>
<item>c:nickName</item>
</modification>
<modification id="3816">
<operation>modify</operation>
<item>c:nickName</item>
</modification>
</or>
</policyConstraints>
<policyActions>
<scriptExecution id="377">
<name>Script</name>
<object>
<currentObject>
<type>c:UserType</type>
</currentObject>
</object>
<executeScript xmlns:s="http://midpoint.evolveum.com/xml/ns/public/model/scripting-3">
<s:recompute/>
</executeScript>
</scriptExecution>
</policyActions>
</policyRule>
</inducement>
В ресуре AD наш старый Schema Handler MS AD account переделываем на этот
<schemaHandling>
<objectType id="5">
<kind>account</kind>
<intent>intent MS AD account</intent>
<displayName>MS AD account</displayName>
<default>true</default>
<delineation>
<objectClass>ri:user</objectClass>
<filter>
<_metadata id="11539">
<provenance>
<acquisition id="11540">
<actorRef oid="00000000-0000-0000-0000-000000000002" relation="org:default" type="c:UserType">
<!-- administrator -->
</actorRef>
<channel>http://midpoint.evolveum.com/xml/ns/public/common/channels-3#user</channel>
<timestamp>2024-12-04T07:28:42.043Z</timestamp>
</acquisition>
</provenance>
</_metadata>
<q:text>attributes/sAMAccountName not startsWith "virt-"</q:text>
</filter>
</delineation>
<focus>
<type>c:UserType</type>
</focus>
<attribute id="18">
<ref>ri:sn</ref>
<outbound>
<name>03 out</name>
<strength>strong</strength>
<source>
<path>familyName</path>
</source>
</outbound>
</attribute>
<attribute id="20">
<ref>ri:givenName</ref>
<outbound>
<name>01 out</name>
<strength>strong</strength>
<source>
<path>givenName</path>
</source>
</outbound>
</attribute>
<attribute id="24">
<ref>ri:employeeNumber</ref>
<outbound>
<name>10 out</name>
<strength>strong</strength>
<source>
<path>personalNumber</path>
</source>
</outbound>
<inbound id="742">
<name>01 in</name>
<strength>strong</strength>
<target>
<path>personalNumber</path>
</target>
<use>correlation</use>
</inbound>
</attribute>
<attribute id="37">
<ref>ri:cn</ref>
<outbound>
<name>04 out</name>
<strength>strong</strength>
<source>
<path>nickName</path>
</source>
<source>
<path>extension/person_account_number</path>
</source>
<expression>
<script>
<code>na_person_account_number = basic.getExtensionPropertyValue(user, 'http://example.com/xml/ns/mySchema', 'person_account_number')
if (basic.isEmpty(na_person_account_number))
{return nickName}
else
{if (na_person_account_number == '0')
{return nickName}
else
{
result = basic.stringify(nickName) + "--" + iterationToken
return result}
}</code>
</script>
</expression>
</outbound>
<inbound id="3066513">
<name>02 in</name>
<strength>strong</strength>
<expression>
<script>
<code>return "AD login is:" + input</code>
</script>
</expression>
<target>
<path>fullName</path>
</target>
</inbound>
</attribute>
<attribute id="104">
<ref>ri:sAMAccountName</ref>
<outbound>
<name>05 out</name>
<strength>strong</strength>
<source>
<path>nickName</path>
</source>
<expression>
<script>
<code>na_person_account_number = basic.getExtensionPropertyValue(user, 'http://example.com/xml/ns/mySchema', 'person_account_number')
if (basic.isEmpty(na_person_account_number))
{return nickName}
else
{if (na_person_account_number == '0')
{return nickName}
else
{
result = basic.stringify(nickName) + "--" + iterationToken
return result}
}</code>
</script>
</expression>
</outbound>
</attribute>
<attribute id="105">
<ref>ri:dn</ref>
<outbound>
<name>06 out new script for OU</name>
<strength>strong</strength>
<source>
<path>locality</path>
</source>
<source>
<path>nickName</path>
</source>
<expression>
<script>
<code>na_person_account_number = basic.getExtensionPropertyValue(user, 'http://example.com/xml/ns/mySchema', 'person_account_number')
if (basic.isEmpty(na_person_account_number))
{its_nickName = nickName}
else
{if (na_person_account_number == '0')
{its_nickName = nickName}
else
{
result = basic.stringify(nickName) + "--" + iterationToken
its_nickName = result
}
}
if (locality) {
dn_value = 'CN=' + its_nickName + ',' + locality
} else {
baseContext = basic.getResourceIcfConfigurationPropertyValue(resource, 'baseContext')
dn_value = 'CN=' + its_nickName + ',OU=New_Employees,' + baseContext
}
return dn_value</code>
</script>
</expression>
</outbound>
<inbound id="775">
<name>04 in write OU to locality</name>
<strength>strong</strength>
<expression>
<script>
<code>return ((basic.stringify(input).split(',')).tail()).join(',');</code>
</script>
</expression>
<target>
<path>locality</path>
</target>
</inbound>
</attribute>
<attribute id="106">
<ref>ri:displayName</ref>
</attribute>
<attribute id="328">
<ref>ri:middleName</ref>
<outbound>
<name>02 out</name>
<strength>strong</strength>
<source>
<path>additionalName</path>
</source>
</outbound>
</attribute>
<attribute id="554">
<ref>ri:pwdLastSet</ref>
<limitations id="557">
<access>
<read>true</read>
<add>true</add>
<modify>false</modify>
</access>
</limitations>
<fetchStrategy>explicit</fetchStrategy>
<outbound>
<name>08 out change pass on next login</name>
<strength>strong</strength>
<expression>
<script>
<code>// -1 next logon reset password required
// 0 no previous loggon
return "0"</code>
</script>
</expression>
<condition>
<script>
<code>"add".equals(operation.toString()) && midpoint.isEvaluateNew()</code>
</script>
</condition>
</outbound>
</attribute>
<attribute id="699">
<ref>ri:userPrincipalName</ref>
<outbound>
<name>09 out</name>
<strength>strong</strength>
<source>
<path>nickName</path>
</source>
<expression>
<script>
<code>return nickName + "@168testserverhome.com"</code>
</script>
</expression>
</outbound>
</attribute>
<attribute id="770">
<ref>ri:distinguishedName</ref>
</attribute>
<attribute id="3066489">
<ref>ri:employeeType</ref>
<outbound>
<name>11 out</name>
<strength>strong</strength>
<source>
<path>personalNumber</path>
</source>
<source>
<path>organizationalUnit</path>
</source>
<expression>
<script>
<code>if (basic.isEmpty(organizationalUnit))
{return personalNumber}
else
{return organizationalUnit}</code>
</script>
</expression>
</outbound>
<inbound id="3066491">
<name>06 in</name>
<strength>strong</strength>
<target>
<path>organizationalUnit</path>
</target>
<use>correlation</use>
</inbound>
</attribute>
<association id="306">
<ref>AD shared group membership to Midpoint</ref>
<inbound id="4781">
<strength>normal</strength>
<channel>http://midpoint.evolveum.com/xml/ns/public/common/channels-3#reconciliation</channel>
<expression>
<assignmentTargetSearch>
<targetType>RoleType</targetType>
<filter>
<q:equal>
<q:path>identifier</q:path>
<expression>
<script>
<code>
return basic.getAttributeValue(entitlement, 'dn')
</code>
</script>
</expression>
</q:equal>
</filter>
<createOnDemand>true</createOnDemand>
<populateObject>
<populateItem>
<expression>
<script>
<code>
basic.getAttributeValue(entitlement, 'cn')
</code>
</script>
</expression>
<target>
<path>name</path>
</target>
</populateItem>
<populateItem>
<expression>
<script>
<code>
basic.getAttributeValue(entitlement, 'dn')
</code>
</script>
</expression>
<target>
<path>identifier</path>
</target>
</populateItem>
<populateItem>
<expression>
<assignmentTargetSearch>
<targetType>ArchetypeType</targetType>
<filter>
<q:equal>
<q:path>name</q:path>
<expression>
<value>AD Group Shared ArcheType</value>
</expression>
</q:equal>
</filter>
</assignmentTargetSearch>
</expression>
<target>
<path>assignment</path>
</target>
</populateItem>
</populateObject>
</assignmentTargetSearch>
</expression>
<target>
<path>assignment</path>
</target>
</inbound>
<kind>entitlement</kind>
<intent>intent Groups Shared Folder POC</intent>
<direction>objectToSubject</direction>
<associationAttribute>ri:member</associationAttribute>
<valueAttribute>ri:dn</valueAttribute>
</association>
<association id="307">
<ref>AD group membership to Midpoint</ref>
<tolerant>false</tolerant>
<kind>entitlement</kind>
<intent>intent MS AD group</intent>
<direction>objectToSubject</direction>
<associationAttribute>ri:member</associationAttribute>
<valueAttribute>ri:dn</valueAttribute>
<explicitReferentialIntegrity>false</explicitReferentialIntegrity>
</association>
<iteration>
<maxIterations>15</maxIterations>
<tokenExpression>
<script>
<code>
return iteration + 1
</code>
</script>
</tokenExpression>
</iteration>
<activation>
<administrativeStatus>
<outbound id="794">
<name>Just Block</name>
<strength>strong</strength>
<source>
<path>activation/administrativeStatus</path>
</source>
</outbound>
</administrativeStatus>
<lockoutStatus/>
</activation>
<credentials>
<password>
<outbound id="765">
<name>initial</name>
<strength>weak</strength>
<expression>
<generate>
<mode>policy</mode>
<valuePolicyRef oid="00000000-0000-0000-0000-000000000003" type="c:ValuePolicyType" xsi:type="c:ObjectReferenceType"/>
</generate>
</expression>
</outbound>
</password>
</credentials>
<correlation>
<correlators>
<items id="820">
<name>personalNumber correlation</name>
<item id="821">
<ref>personalNumber</ref>
</item>
<item id="3066493">
<ref>organizationalUnit</ref>
</item>
<composition/>
</items>
</correlators>
</correlation>
<synchronization>
<reaction id="755">
<situation>linked</situation>
<actions>
<synchronize id="756"/>
</actions>
</reaction>
<reaction id="757">
<situation>unlinked</situation>
<actions>
<link id="758"/>
</actions>
</reaction>
<reaction id="835">
<situation>deleted</situation>
<actions>
<deleteResourceObject id="836"/>
</actions>
</reaction>
</synchronization>
</objectType>
...
<schemaHandling>
Если у User аккаунта в person_account_number 0 то выдается логин как nickName, если 1 то включается перебор и логин вида nickName--1
И навешиваем реальные AD роли группы они же системные. В обжект теплейт POCE Employment MS AD Account Object Template добавляем
<item id="33">
<ref>assignment</ref>
<displayName>Assignment to AD group Roles</displayName>
<mapping id="9">
<name>Mapping</name>
<authoritative>true</authoritative>
<strength>strong</strength>
<source>
<path>c:extension/person_forward_roles</path>
</source>
<source>
<path>c:nickName</path>
</source>
<expression>
<assignmentTargetSearch>
<targetType>RoleType</targetType>
<filter>
<q:text>identifier = $person_forward_roles and archetypeRef matches (oid = "e9eda47b-b097-4814-9605-7177a2482fa1")</q:text>
</filter>
</assignmentTargetSearch>
</expression>
<condition>
<script>
<code>!basic.isEmpty(nickName)</code>
</script>
</condition>
</mapping>
</item>
Не буду тут останавливаться, это работает, а теперь что если мы хотим на назначения на позиции тоже делать AD аккаунт и так чтобы права на AD группы в этом случае не передавались в AD аккунт трудоустройства? Можно!
Создаем Object Template под именем POCE Person Position MS AD Account Object Template
в нем вставляем код
<mapping id="4">
<name>01</name>
<source>
<path>c:givenName</path>
</source>
<expression/>
<target>
<path>c:givenName</path>
</target>
</mapping>
<mapping id="5">
<name>02</name>
<source>
<path>c:familyName</path>
</source>
<expression/>
<target>
<path>c:familyName</path>
</target>
</mapping>
<mapping id="6">
<name>03</name>
<source>
<path>c:additionalName</path>
</source>
<expression/>
<target>
<path>c:additionalName</path>
</target>
</mapping>
<mapping id="7">
<name>04</name>
<source>
<path>c:personalNumber</path>
</source>
<expression/>
<target>
<path>c:personalNumber</path>
</target>
</mapping>
<mapping id="8">
<name>05</name>
<strength>strong</strength>
<source>
<path>c:personalNumber</path>
</source>
<source>
<path>c:name</path>
</source>
<source>
<path xmlns:gen974="http://example.com/xml/ns/mySchema">c:extension/gen974:person_employment_parent</path>
</source>
<expression>
<script>
<code>personalNumber + " " + person_employment_parent + " " + name + " Position MS AD Account"</code>
</script>
</expression>
<target>
<path>c:name</path>
</target>
</mapping>
<mapping id="9">
<name>06</name>
<source>
<path>c:costCenter</path>
</source>
<expression/>
<target>
<path>c:costCenter</path>
</target>
</mapping>
<mapping id="10">
<name>07</name>
<source>
<path>c:extension/person_employment_parent</path>
</source>
<expression/>
<target>
<path>c:extension/person_employment_parent</path>
</target>
</mapping>
<mapping id="11">
<name>08</name>
<source>
<path>c:organization</path>
</source>
<expression/>
<target>
<path>c:organization</path>
</target>
</mapping>
<mapping id="12">
<name>09</name>
<source>
<path xmlns:gen974="http://example.com/xml/ns/mySchema">c:extension/gen974:person_employment_type</path>
</source>
<expression/>
<target>
<path xmlns:gen695="http://example.com/xml/ns/mySchema">c:extension/gen695:person_employment_type</path>
</target>
</mapping>
<mapping id="13">
<name>10</name>
<exclusive>true</exclusive>
<source>
<path>c:extension/person_forward_roles</path>
</source>
<expression>
<script>
<relativityMode>absolute</relativityMode>
<code>
result = []
for (i in person_forward_roles)
{
if (i.startsWith("MSAD"))
{ii = i.tokenize( '|' )
result.add(ii[2])}
}
return result</code>
</script>
</expression>
<target>
<path>c:extension/person_forward_roles</path>
</target>
</mapping>
<mapping id="14">
<name>adminStatus to persona</name>
<source>
<path>c:activation/c:administrativeStatus</path>
</source>
<expression/>
<target>
<path>c:activation/c:administrativeStatus</path>
</target>
</mapping>
<mapping id="15">
<name>11</name>
<authoritative>true</authoritative>
<strength>strong</strength>
<expression>
<script>
<code>'1'</code>
</script>
</expression>
<target>
<path xmlns:gen23="http://example.com/xml/ns/mySchema">c:extension/gen23:person_account_number</path>
</target>
</mapping>
<mapping id="112">
<name>088</name>
<source>
<path>c:name</path>
</source>
<expression/>
<target>
<path>c:organizationalUnit</path>
</target>
</mapping>
Создаем Object Template под именем POCE Position MS AD Account Object Template
в нем вставляем код
<item id="2">
<ref>assignment</ref>
<displayName>Assignment to Position Roles</displayName>
<mapping id="3">
<source>
<path>c:organizationalUnit</path>
</source>
<expression>
<assignmentTargetSearch>
<targetType>RoleType</targetType>
<filter>
<q:text>locality = $organizationalUnit and archetypeRef matches (oid = "47374624-553c-4661-b116-d07952900451")</q:text>
</filter>
</assignmentTargetSearch>
</expression>
</mapping>
</item>
<item id="6">
<ref>assignment</ref>
<displayName>Assignment to AD group Roles</displayName>
<mapping id="7">
<name>Mapping</name>
<authoritative>true</authoritative>
<strength>strong</strength>
<source>
<path>c:extension/person_forward_roles</path>
</source>
<source>
<path>c:nickName</path>
</source>
<expression>
<assignmentTargetSearch>
<targetType>RoleType</targetType>
<filter>
<q:text>identifier = $person_forward_roles and archetypeRef matches (oid = "e9eda47b-b097-4814-9605-7177a2482fa1")</q:text>
</filter>
</assignmentTargetSearch>
</expression>
<condition>
<script>
<code>!basic.isEmpty(nickName)</code>
</script>
</condition>
</mapping>
</item>
<item id="4">
<ref>assignment</ref>
<displayName>Assignment or creation of nickName role</displayName>
<lifecycleState>active</lifecycleState>
<mapping id="5">
<source>
<path>personalNumber</path>
</source>
<expression>
<assignmentTargetSearch>
<targetType>RoleType</targetType>
<filter>
<q:text>extension/user_personalNumber = $personalNumber and archetypeRef matches (oid = "7f71dcf3-c89d-4e23-930b-215423af3849")</q:text>
</filter>
<createOnDemand>true</createOnDemand>
<populateObject>
<populateItem>
<expression>
<script>
<code>
personalNumber + " nickName"
</code>
</script>
</expression>
<target>
<path>name</path>
</target>
</populateItem>
<populateItem>
<expression>
<script>
<code>
personalNumber
</code>
</script>
</expression>
<target>
<path>extension/user_personalNumber</path>
</target>
</populateItem>
<populateItem>
<expression>
<assignmentTargetSearch>
<targetType>ArchetypeType</targetType>
<oid>7f71dcf3-c89d-4e23-930b-215423af3849</oid>
</assignmentTargetSearch>
</expression>
<target>
<path>assignment</path>
</target>
</populateItem>
</populateObject>
</assignmentTargetSearch>
</expression>
</mapping>
</item>
Создаем Архетип под названием POCE Position MS AD Account
код
<inducement id="2">
<lifecycleState>active</lifecycleState>
<construction>
<resourceRef oid="b8618fba-cf8b-416c-8e3b-32ea34cf003d" relation="org:default" type="c:ResourceType">
<!-- Windows MS AD OOO ODIN -->
</resourceRef>
<kind>account</kind>
<intent>intent MS AD account</intent>
</construction>
<focusType>c:UserType</focusType>
<condition>
<source>
<path>nickName</path>
</source>
<expression>
<script>
<code>!basic.isEmpty(nickName)</code>
</script>
</expression>
</condition>
</inducement>
<inducement id="372">
<focusMappings>
<mapping id="8">
<authoritative>false</authoritative>
<strength>strong</strength>
<expression>
<script>
<relativityMode>absolute</relativityMode>
<code>
linkedDATA = midpoint.findLinkedTarget('from nickName Role to User Account AD Pos')
return linkedDATA.emailAddress
</code>
</script>
</expression>
<target>
<path>nickName</path>
</target>
<condition>
<script>
<code>
linkedDATA = midpoint.findLinkedTarget('from nickName Role to User Account AD Pos')
if (basic.isEmpty(linkedDATA))
{return false}
else
{return !basic.isEmpty(linkedDATA.emailAddress)}
</code>
</script>
</condition>
</mapping>
</focusMappings>
</inducement>
<inducement id="375">
<lifecycleState>active</lifecycleState>
<policyRule>
<name>Recompute on nickName change</name>
<policyConstraints>
<or id="384">
<modification id="3815">
<operation>add</operation>
<item>c:nickName</item>
</modification>
<modification id="3816">
<operation>modify</operation>
<item>c:nickName</item>
</modification>
</or>
</policyConstraints>
<policyActions>
<scriptExecution id="377">
<name>Script</name>
<object>
<currentObject>
<type>c:UserType</type>
</currentObject>
</object>
<executeScript xmlns:s="http://midpoint.evolveum.com/xml/ns/public/model/scripting-3">
<s:recompute/>
</executeScript>
</scriptExecution>
</policyActions>
</policyRule>
</inducement>
<archetypePolicy>
<display>
<icon>
<cssClass>fa fa-hard-hat</cssClass>
<color>#2d860a</color>
</icon>
</display>
<objectTemplateRef oid="d7845c6d-ca3c-4afd-bfda-e48f66c5968b" relation="org:default" type="c:ObjectTemplateType">
<!-- POCE Position MS AD Account Object Template -->
</objectTemplateRef>
<links>
<targetLink id="5">
<name>from nickName Role to User Account AD Pos</name>
<selector>
<type>c:RoleType</type>
<archetypeRef oid="7f71dcf3-c89d-4e23-930b-215423af3849" relation="org:default" type="c:ArchetypeType">
<!-- POCE nickName Role archeType -->
</archetypeRef>
</selector>
</targetLink>
<targetLink id="3826">
<name>3545345</name>
<selector>
<type>c:RoleType</type>
<archetypeRef oid="47374624-553c-4661-b116-d07952900451" relation="org:default" type="c:ArchetypeType">
<!-- POCE Position Role ArcheType -->
</archetypeRef>
</selector>
</targetLink>
</links>
</archetypePolicy>
</archetype>
Создаем Forward роль под названием OOO ODIN FR: Position MS AD Account
заполняем

Oна indicement в роль для создания персоны под названием POCE Persona Position Account
<inducement id="125">
<personaConstruction>
<targetType>UserType</targetType>
<objectMappingRef oid="3ac53f67-f1c9-4051-8991-8f935145f1b7" relation="org:default" type="c:ObjectTemplateType">
<!-- POCE Person Position MS AD Account Object Template -->
</objectMappingRef>
<archetypeRef oid="87471a3d-2d25-4309-b58a-af261683adfa" relation="org:default" type="c:ArchetypeType">
<!-- POCE Position MS AD Account -->
</archetypeRef>
</personaConstruction>
</inducement>
В Object Template под названием POCE Position Role Object Template
добавляем
<mapping id="5">
<name>Account number property</name>
<exclusive>true</exclusive>
<strength>strong</strength>
<source>
<path>c:extension/user_forward_roles</path>
</source>
<expression>
<script>
<relativityMode>absolute</relativityMode>
<code>for (i in user_forward_roles)
{
if (i.startsWith("NICKNAME"))
{return "1"}
}
return "NONE"</code>
</script>
</expression>
<target>
<path>c:extension/user_account_number</path>
</target>
</mapping>
Хотя он по сути не участвует, но зато видно
Кажется все описал, мог что то упустить, много раз дописывал и переписвал, вот схемка для проверки

Тест
Имеем по учеткам

Смотрим в проэкцию в AD у 600667 EMP002001 Employment MS AD Account что ему выдано из AD группы

Выдаем назначению POS000101 роль OOO ODIN FR: Position MS AD Account и по группам сразу все меняется. У 600667 EMP002001 Employment MS AD Account теперь

На две AD группы меньше, назначение POS000101 их теперь не отдает трудоустройству, а выдает своей учетке 600667 EMP002001 POS000101 Position MS AD Account

Вот так можно быстро, а главное просто, в Midpoint назначать роли трудоустройствам и назначениям в админке!