Заставляем IDM Midpoint работать с внешними заявками

от автора

IDM от Evolveum Midpoint обещает много, делает только то, что считает нужным. Крайне редкое и малоописанное решение, но в России его любят. СберТех построил на Midpoint свой Platform V IDM и не скрывает этого, по некоторым данным даже обновляет его до последней версии, но demo нет, так что не проверить. По слухам есть ещё две российские IDM, которые тоже выстроены на Midpoint, но на старых версиях, и они это не афишируют.

Evolvеum в своих презенташках упирает на то, что Midpoint всё может, а что не может во-первых не нужно, а во-вторых это в старых IDM так было, а мы новые — мы лучше! Так, например, отлично проработана выдача ролей, а вот их отъём в принципе нет, официальный ответ «Не думайте о том как отобрать роль, думайте как её не выдать». Midpoint работает с состояниями, забирает их в себя, хочет быть посредником (это прям в его названии), какие-то команды получать не хочет, хотя есть REST API и можно управлять ими командами, но для этого надо писать внешний сервис для внешних заявок — посредника для посредника!

Но всё равно работу в самом Midpoint c внешними заявками можно реализовать имеющимися инструментами.

Evolveum называет Midpoint Open Source проектом, по мне так он истерично коммерческий, хотя брать бесплатно пользоваться можно да, но есть нюансы:

Документация: docs.evolveum.com

Частично или нет. Худший пример — описание в разделе Midpoint\Features\Current Features\Parametric Role docs.evolveum.com/midpoint/features/current/parametric-role просто заголовок и пусто.

А единственное официальное (да и просто единственное) руководство заканчивается на самом интересном месте фразой «дайте денег, а то дальше писать не буду» docs.evolveum.com/book/practical-identity-management-with-midpoint.html#98-to-be-continued Автор ждёт с 22-го года…

Примеры: github.com/Evolveum/midpoint-samples

Частично или нет или очень старые

Поддержка сообщества:

Нет сообщества

Поддержка разработчиков: support.evolveum.com/projects/midpoint/

Только разработка новых функций и баги, отвечают только платным подписчикам. Типовые ответы: посылание в документацию, посылание к интеграторам, обещание исправить в следующих релизах.

Обучение: evolveum.com/services/training-and-certification/?target=training-courses-offer

Есть платные курсы, может там показывают настоящую документацию! Бесплатно есть несколько видео по некоторым функциям на www.youtube.com/@Evolveum/videos больше презентации, чем обучение.

Demo: demo.evolveum.com/midpoint/

Пустое но есть!

Но Midpoint разгрызть можно и без нормальной документации и поддержки, что продемонстрирую ниже, с примерами и всем, чтобы можно было воспроизвести проверить.

Исходные данные: Midpoint 4.8.4 в версии 4.8.3 не работает, не основная, но нужная функция — фильтр в Resource в Schema Handler.

Упрощённая схема, чтобы понять что происходит

— Midpoint подключён к системам 1,2,3 через Resource. На входе в Resource стоит Connector решает вопросы как подключаться к системам и забирает данные и отдаёт их Sсhema Handler, который решает что и как делать с ними в Midpoint.

— Resource в результате синхронизации создаёт в Midpoint — 4 профиль user. 5 роль для выдачи AD группы, 6 роль WO заявку.

— При создании роль WO заявки в её атрибуты записывают данные заявки, ей назначается ArcheType (источник свойств, функционала), из которого назначается Object Template (источник обработки атрибутов). Также ей назначается роль WO Core, в которой прописана Policy Role, которая будет запускать скрипт.

— Скрипт запускается при любом событии с ролью WO заявка, если она не заблокирована. Скрипт ищет есть ли пользователь и роль. Если есть выполняет с ними действие из атрибутов роли WO заявка, в конце пишет комментарий в роль WO заявка и проставляет атрибут, что всё сделано (Object Template на этом основание блокирует роль WO заявку). Если же роли или пользователя нет, то он пишет в роль WO заявку, что есть ошибка, и она остаётся не сделанной, то есть не заблокированной, поэтому скрипт опять сработает при изменении в роли или при синхронизации ресурса.

Как это выглядит в Midpoint

Во вкладке Roles у нас имеется вкладка Work Order (где собрано всё с ArcheType, который мы назначили ролям WO заявкам).

Списком видны все роли WO заявки с информативным Description. Выполненные заявки заблокированы, так же они уже не участвуют в синхронизации с Resource и в системе помечено, что они выполнены (Resource фильтром берёт из системы только записи без пометки «исполнено»). А роли WO заявки с ERROR ждут редактирования админом, или может при следующей синхронизации такой пользователь или группа появится, тогда они сработают.

Можем зайти в пользователя из последней заявки в History и посмотреть что с ним делалось

Видим, что некто WO_administrator выдал assigned пользователю роль! Под WO_administrator запускается синхронизация в ресурсе.

Настройка

1. В примере участвуют дополнительные атрибуты, надо их добавить в Midopint

Кладём в /opt/midpoint/var/schema файл some.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="DROLER_owner" type="xsd:string" minOccurs="0" maxOccurs="1">         <xsd:annotation>           <xsd:appinfo>             <a:indexed>true</a:indexed>             <a:displayName>DROLER OWNER</a:displayName>             <a:displayOrder>136</a:displayOrder>             <a:help>ToDo</a:help>           </xsd:appinfo>         </xsd:annotation>       </xsd:element>       <xsd:element name="DROLER_role" type="xsd:string" minOccurs="0" maxOccurs="1">         <xsd:annotation>           <xsd:appinfo>             <a:indexed>true</a:indexed>             <a:displayName>DROLER ROLE</a:displayName>             <a:displayOrder>138</a:displayOrder>             <a:help>ToDo</a:help>           </xsd:appinfo>         </xsd:annotation>       </xsd:element>     </xsd:sequence>   </xsd:complexType> </xsd:schema>

Чтобы появились новые атрибуты Midpoint надо перезагрузить. Если у вас уже есть такой файл, то добавьте только то, что в тегах complexType

2. Создадим Object Template

Идём в CONFIGURATION\Object Templates и создаём Object Template под названием WO Object Template. Заходим в него. Всё, что касается Object Template хорошо прописано в GUI Midpoint и можно сделать его кнопками, но для наглядности (и скорости) полезем в RAW Code

Нажимаем Edit Raw

Перед

</objectTemplate>

Вставляем

<mapping id="2">   <name>Block</name>   <strength>strong</strength>   <source>     <path>c:documentation</path>   </source>   <expression>     <script>       <code>import com.evolveum.midpoint.xml.ns._public.common.common_3.ActivationStatusType; return ActivationStatusType.DISABLED</code>     </script>   </expression>   <target>     <path>c:activation/c:administrativeStatus</path>   </target>   <condition>     <script>       <code>documentation == 'DONE'</code>     </script>   </condition>   <evaluationPhase>afterAssignments</evaluationPhase> </mapping>

Если в роли WO заявки заполняется атрибут Documentation словом DONE, то роль деактивируется.

Вот так это же выглядит в GUI

3. Создаём архетип под роли WO заявки

Идём в CONFIGURATION\ArcheTypes и создаём ArcheTypes под названием WO ArcheType Заходим в него. Попробуем всё сделать кнопками. Заходим во вкладку Archetype Policy. Midpoint все незаполненные атрибуты прячет по умолчанию. Заполняем как на картинке

И в этом есть смысл в Object template reference вам надо ткнуть в свой WO Object Template, так как у него отличается OID и перенести его кодом с другого Midpoint не получится.

Да, совсем без залезания в code не получится, нам надо ещё сказать, что этот архетип относится к Role, кнопки для этого в Assigments нет, так что

Нажимаем Edit Raw

Перед (а Midpoint сам потом поставит куда ему нужно, вот только id сам поменять не сможет, если ругается на них или удалить или сменить номер)

</archetype>

Вставляем code

<assignment id="1">   <identifier>holderType</identifier>   <activation>     <effectiveStatus>enabled</effectiveStatus>   </activation>   <assignmentRelation id="2">     <holderType>RoleType</holderType>   </assignmentRelation> </assignment>

4. Добавляем вьюху для ролей WO заявку

Тут всё просто, тыканьем удалось найти место, где это задаётся

CONFIGURATION\System\Admin GUI Configuration\Object collection view

Добавляем вьюху с названием WO VIEW

Тут нам подойдёт минимальное заполнение. А можно ещё например в Column расписать что хотим видеть, а вот в Display ничего писать нельзя, сразу ошибка 500.

Заполняем как на картинке

5. Роль WO Core

В ADMINISTRATION\Role\All Roles создаём просто роль (чёрную) под названием WO Core. Заходим в неё.

Нажимаем Edit Raw

Вставляем перед

</role>

следующий code

<inducement id="2">   <policyRule>     <name>WO Core enigine</name>     <policyConstraints>       <alwaysTrue id="13">         <name>yes</name>       </alwaysTrue>     </policyConstraints>     <policyActions>       <scriptExecution id="5">         <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.* import static com.evolveum.midpoint.schema.constants.SchemaConstants.C_ORG_TYPE import javax.xml.namespace.QName  //get host role WO info role = midpoint.getObject(RoleType.class, input.oid) actionId = basic.stringify(role.costCenter) userId = basic.stringify(basic.getExtensionPropertyValue(role, "http://example.com/xml/ns/mySchema", "DROLER_owner")) roleId = basic.stringify(basic.getExtensionPropertyValue(role, "http://example.com/xml/ns/mySchema", "DROLER_role")) //find and get user from role WO query_user = midpoint.queryFor(UserType.class, "personalNumber = '$userId'")  result_USER = midpoint.searchObjects(query_user) //find and get role from role WO query_role_ass = midpoint.queryFor(RoleType.class, "identifier = '$roleId'")  result_ROLE_ASS = midpoint.searchObjects(query_role_ass)   //if role and user exists if (result_USER  &amp;&amp; result_ROLE_ASS &amp;&amp; basic.stringify(role.activation.administrativeStatus) != "DISABLED") { user_oid = basic.stringify(result_USER.oid) user = midpoint.getObject(UserType.class, user_oid)  if (actionId == 'DELETE') { def assignmentsToDelete = [] for (a in user.assignment) { if (a.targetRef?.oid == basic.stringify(result_ROLE_ASS.oid)) { def removeAssignment = new AssignmentType() removeAssignment.id = a.id assignmentsToDelete.add removeAssignment.asPrismContainerValue() } } if (!assignmentsToDelete.empty) { //log.info "Assignments to delete: " + assignmentsToDelete   delta = prismContext.deltaFor(UserType.class).item(UserType.F_ASSIGNMENT).delete(assignmentsToDelete).asObjectDelta(user.oid) //log.info "Deleting" midpoint.modifyObject(delta, ModelExecuteOptions.createRaw()) roleDescription = 'OK: Deleted role identfier:' + roleId + ' from user personalNumber:' + userId delta = prismContext.deltaFor(RoleType.class).item(RoleType.F_DESCRIPTION).replace(roleDescription).asObjectDelta(input.oid) midpoint.modifyObject(delta, ModelExecuteOptions.createRaw()) roleDescription = 'DONE' delta = prismContext.deltaFor(RoleType.class).item(RoleType.F_DOCUMENTATION).replace(roleDescription).asObjectDelta(input.oid) midpoint.modifyObject(delta, null) } else { roleDescription = 'WARNING: User with personalNumber:' + userId + ' did not have assigned role with identfier:' + roleId  delta = prismContext.deltaFor(RoleType.class).item(RoleType.F_DESCRIPTION).replace(roleDescription).asObjectDelta(input.oid) midpoint.modifyObject(delta, ModelExecuteOptions.createRaw()) roleDescription = 'DONE' delta = prismContext.deltaFor(RoleType.class).item(RoleType.F_DOCUMENTATION).replace(roleDescription).asObjectDelta(input.oid) midpoint.modifyObject(delta, null) } }  if (actionId == 'ADD') { orgUnit = new ObjectReferenceType() orgUnit.setOid(result_ROLE_ASS.oid) orgUnit.setType(RoleType.COMPLEX_TYPE) addAssignment = new AssignmentType() addAssignment.setTargetRef(orgUnit) def delta = [] delta = prismContext.deltaFor(UserType.class).item(FocusType.F_ASSIGNMENT).add(addAssignment.asPrismContainerValue()).asObjectDelta(user.oid) midpoint.modifyObject(delta, ModelExecuteOptions.createRaw()) roleDescription = 'OK: Added role identfier:' + roleId + ' to user personalNumber:' + userId delta = prismContext.deltaFor(RoleType.class).item(RoleType.F_DESCRIPTION).replace(roleDescription).asObjectDelta(input.oid) midpoint.modifyObject(delta, ModelExecuteOptions.createRaw()) roleDescription = 'DONE' delta = prismContext.deltaFor(RoleType.class).item(RoleType.F_DOCUMENTATION).replace(roleDescription).asObjectDelta(input.oid) midpoint.modifyObject(delta, null)  } } else { //if role or user does not exists if (!result_USER &amp;&amp; result_ROLE_ASS &amp;&amp; basic.stringify(role.activation.administrativeStatus) != "DISABLED") {  roleDescription = 'ERROR: User personalNumber:' + userId + ' not present in Midpoint!' owdelta = prismContext.deltaFor(RoleType.class).item(RoleType.F_DESCRIPTION).add(roleDescription).asObjectDelta(input.oid) midpoint.modifyObject(owdelta, ModelExecuteOptions.createRaw())}  if (result_USER &amp;&amp; !result_ROLE_ASS &amp;&amp; basic.stringify(role.activation.administrativeStatus) != "DISABLED") {roleDescription = 'ERROR: Role identfier:' + roleId + ' not present in Midpoint!' owdelta = prismContext.deltaFor(RoleType.class).item(RoleType.F_DESCRIPTION).replace(roleDescription).asObjectDelta(input.oid) midpoint.modifyObject(owdelta, ModelExecuteOptions.createRaw())}  if (!result_USER &amp;&amp; !result_ROLE_ASS &amp;&amp; basic.stringify(role.activation.administrativeStatus) != "DISABLED") {roleDescription = 'ERROR: Role identfier:' + roleId + ' and User personalNumber:' + userId + ' not present in Midpoint!' owdelta = prismContext.deltaFor(RoleType.class).item(RoleType.F_DESCRIPTION).replace(roleDescription).asObjectDelta(input.oid) midpoint.modifyObject(owdelta, ModelExecuteOptions.createRaw())}  }   </code>                 </s:value>               </s:parameter>             </s:action>           </s:pipeline>         </executeScript>       </scriptExecution>     </policyActions>   </policyRule>   <condition>     <expression>       <script>         <code>import com.evolveum.midpoint.xml.ns._public.common.common_3.ActivationStatusType; if (ActivationStatusType.ENABLED) {return true}</code>       </script>     </expression>   </condition> </inducement>

policyConstraints — прописано, что скрипт запускается всегда но

в condition — прописано, что назначение этой Policy Rule активно только если роль WO заявка не заблокирована

6. Настраиваем Resource

Нам нужен файл данных для симуляции системы заявок WO

В папку /opt/midpoint/var/ кладем файл wo.csv с содержимым

wo_num;wo_user;wo_role;wo_action;wo_satus 1;47128562bad;ROLE2144255;ADD;todo 2;47128562;ROLE2144255;ADD;todo 3;47128562;ROLE2144255;DELETE;todo 4;47128562;ROLE2144255;DELETE;todo

wo_num — уникальный номер заявки в системе WO

wo_user — это номер сотрудника в Midpoint в атрибуте personalNumber

wo_role — это номер роли в Midpoint в атрибуте identifier

wo_action — это действие или ADD или DELETE

wo_satus — это статус если он не DONE Midpoint будет пытаться исполнить эту заявку

Создайте своих пользователей и роли и заполните соответствующие атрибуты

Создаём ресурс, идём в ADMINISTRATION\Resources\New Resorce\From Scratch выбираем CsvConnector

Далее как на картинках, пишем своё имя ресурса WO ticket system from CSV

Путь к файлу /opt/midpoint/var/wo.csv

Указываем уникальный идентификатор в системе wo_num Midpoint’у он очень нужен

И всё, далее далее

Заходим в созданный ресурс, нажимаем Edit Raw

и добавляем перед

<capabilities>

следующий code

<schemaHandling>         <objectType id="7">             <kind>entitlement</kind>             <intent>intent WO</intent>             <displayName>WO</displayName>             <default>true</default>             <delineation>                 <objectClass>ri:AccountObjectClass</objectClass>                 <filter>                     <q:text>attributes/wo_satus not contains 'DONE'</q:text>                 </filter>             </delineation>             <focus>                 <type>c:RoleType</type>                 <archetypeRef oid="42ed9c9f-693d-4bfe-9cfe-28656b8ee9da" relation="org:default" type="c:ArchetypeType">                     <!-- WO ArcheType -->                 </archetypeRef>             </focus>             <attribute id="9">                 <ref>ri:wo_num</ref>                 <inbound id="10">                     <name>01 inbound</name>                     <strength>strong</strength>                     <target>                         <path>identifier</path>                     </target>                 </inbound>                 <inbound id="11">                     <name>02 inbound</name>                     <strength>strong</strength>                     <expression>                         <script>                             <code>"WO:" + input.padLeft(14,'0')</code>                         </script>                     </expression>                     <target>                         <path>name</path>                     </target>                 </inbound>                 <inbound id="472">                     <name>03 add POLICY to WO</name>                     <lifecycleState>active</lifecycleState>                     <strength>strong</strength>                     <expression>                         <assignmentTargetSearch>                             <targetType>c:RoleType</targetType>                             <oid>d489d8d8-b0e5-4e9e-a078-46e2be7f6de0</oid>                         </assignmentTargetSearch>                     </expression>                     <target>                         <path>$focus/assignment</path>                     </target>                 </inbound>             </attribute>             <attribute id="12">                 <ref>ri:wo_role</ref>                 <inbound id="13">                     <name>04 inbound</name>                     <strength>weak</strength>                     <target>                         <path>extension/DROLER_role</path>                     </target>                 </inbound>             </attribute>             <attribute id="14">                 <ref>ri:wo_user</ref>                 <inbound id="15">                     <name>05 inbound</name>                     <strength>weak</strength>                     <target>                         <path>extension/DROLER_owner</path>                     </target>                 </inbound>             </attribute>             <attribute id="16">                 <ref>ri:wo_action</ref>                 <inbound id="17">                     <name>06 inbound</name>                     <strength>weak</strength>                     <target>                         <path>costCenter</path>                     </target>                 </inbound>             </attribute>             <attribute id="21">                 <ref>ri:wo_satus</ref>                 <outbound>                     <name>01 outbound</name>                     <strength>strong</strength>                     <source>                         <path>$focus/documentation</path>                     </source>                     <condition>                         <script>                             <code>documentation == 'DONE'</code>                         </script>                     </condition>                 </outbound>             </attribute>             <correlation>                 <correlators>                     <items id="30">                         <item id="31">                             <ref>c:identifier</ref>                         </item>                     </items>                 </correlators>             </correlation>             <synchronization>                 <reaction id="23">                     <situation>unmatched</situation>                     <actions>                         <addFocus id="26"/>                     </actions>                 </reaction>                 <reaction id="24">                     <situation>linked</situation>                     <actions>                         <synchronize id="27"/>                     </actions>                 </reaction>                 <reaction id="25">                     <situation>unlinked</situation>                     <actions>                         <synchronize id="28"/>                     </actions>                 </reaction>             </synchronization>         </objectType>     </schemaHandling>

Это наш schemaHandling

<delineation> — прописан filter, по которому мы берём записи у которых wo_status не DONE

<focus> — прописано, что мы будем делать роли и назначать им архетип, созданный ранее

<attribute id="*"> — прописано, что Midpoint берёт и куда кладёт, он группирует маппинги по атрибуту, здесь могут быть и inbound и outbound

<correlation> — по какому атрибуту происходит привязка

<synchronization> — и описано что делать при синхронизации, мы если у нас нет заявки её создаём:

                <reaction id="23">                     <situation>unmatched</situation>                     <actions>                         <addFocus id="26"/>                     </actions>                 </reaction>

Всё это есть в GUI, посмотрим на Mappings, вот тут GUI не всё понимает

03 add POLICY to WO — тут захаркодено назначение всем ролям WO, заявкам роль с WO core, GUI этого не понимает. Прописан OID WO Core у вас будет другой, его можно найти в CONFIGURATION\Repository Object\All object\Выбрать type Role ну или покапаться в RAW в WO Core

<inbound id="472">                     <name>03 add POLICY to WO</name>                     <lifecycleState>active</lifecycleState>                     <strength>strong</strength>                     <expression>                         <assignmentTargetSearch>                             <targetType>c:RoleType</targetType>                             <oid>d489d8d8-b0e5-4e9e-a078-46e2be7f6de0</oid>                         </assignmentTargetSearch>                     </expression>                     <target>                         <path>$focus/assignment</path>                     </target>                 </inbound>

02 inbound — берёт номер из системы wo_num и кладёт в name в Midpoint. Ему главное чтобы этот namе уникален в Midpoint поэтому дописываем ему WO: и нулей через Script

"WO:" + input.padLeft(14,'0')

04 inbound, 05 inbound, 06 inbound — у этих mapping’ов если нажать справа на них, у мусорного ведра, карандашик с бумажкой, откроет окно с ещё настройками. Я не хочу, чтобы Resource мне всегда перезаписывал эти атрибуты как в себе, я планирую их редактировать в Midpoint поэтому Strength поставлен Weak, если они уже заполнены в роли WO заявки ресурс не будет их менять.

Outbound маппинги, которые пишут в систему WO через ресурс, прячутся тут же в правой вкладке

Я пишу то, что в Midpoint в documentation в атрибут в ресрусе wo_status, но не всегда а когда в documentation слово DONE. Если нажать на карандашик там это прописано в Сondition

Практически всё, осталось настроить запуск синхронизации, она не прописана в RAW коде ресурса, так что пойдём по картинкам.

В ресурcе идём в Defined Task и нажимаем + и выбираем Reconcilation task

Заполняем как на картинке

Тут обратите, что я поставил в Owner пользователя WO_administrator, все заявки созданные при синхронизации будут от него. Можно оставить тут пусто, а можете создать тоже WO_administrator и дать ему роль Superuser (авторизация в Midpoint отдельная удивительная история).

Тут можете поставить interval будет запускать каждый цать секунд или Cron-like вида «5 5 21 * * ?» на 5 секунде 5 минуте в 21 часа.

Далее далее в конце нажимаем Save & Run

И это всё, вот так вот просто можно настроить Midpoint под свои legacy системы заявок!

В Roles\Work Orders вот такая картинка теперь — по тестовым данным!


ссылка на оригинал статьи https://habr.com/ru/articles/842756/


Комментарии

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

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