Выражения#

Выражения это элемент конфигураций IDM, который содержит основную логику обработки данных. Они используются в маппингах, выражениях корелляции и подтверждения, и других.

Содержание#

  • Вступление

  • Операторы выражений *

Вступление#

Выражения позволяют создавать как простые и эффективные конструкции, которые просто передают или генерируют значения, так и более сложные скрипты.

Пример простого выражения

<expression>
    <script>
        <code>
            givenName + ' ' + familyName
        </code>
    </script>
</expression>

Выражения выполняются через вызов одного из операторов выражения. Оператор это код, который принимает входящие данные (переменные) и производит какое-либо значение. Операторы могут быть как очень простыми, например "as is", так и более сложными, использующими языки для скриптов.

Каждое выражение принимает набор переменных на вход. Каждая переменная имеет имя и набор значений. Значения переменных также могут быть динамическими, предоставляя не только конкретное значение, но и то, как оно изменяется (то есть свою дельту). Выражение затем обрабатывает переменные, чтобы получить результат, значение которого также может быть динамическим.

Операторы выражений#

Операторы выражений это индивидуальные условия, которые создают или трансформируют значение. Каждая конструкция должна иметь хотя бы один оператор.

Литерал#

Литеральный оператор создает явное значение. Одиночное строковое значение может быть задано простым указанием этой строки в тэги, как в примере ниже:

Выражение с одиночным строковым литералом:

<expression>
    <value>Bloody Pirate</value>
</expression>

Выражение с несколькими литеральными значениями:

<expression>
    <value>Pirate</value>
    <value>Sailor</value>
</expression>

Пустое значение (в совокупности с ограничениями tolerant=false и strength=strong) можно выразить так:

<expression>
    <value/>
</expression>

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

<expression>
    <value>
        <armament>
            <weapon>Cutlass</weapon>
            <placement>right hand</placement>
        </armament>
        <armament>
            <weapon>Pistol</weapon>
            <placement>belt</placement>
        </armament>
    </value>
</expression>

As is#

Выражение "as is" используется, если никаких изменений значения не требуется, и есть ясное определение входящего значения. "As is" просто копирует значение в результат выражения. Обычно это выражение используется во входящих и исходящих маппингах чтобы синхронизировать пароли и статус активации учетных записей.

<expression>
    <asIs/>
</expression>

Выражение "as is" работает только в том случае, если у выражения есть только один источник (переменная со входящими данными). Если источника нет, то выражение не сможет выдать никакого результата. Если источников больше одного, то выражение не сможет определить, какое из значений нужно взять.

В случае, когда источников несколько, но нужно передать значение только одного, следует использовать оператор пути.

"As is" функционально идентичен передаче значения переменной input через выражение пути:

<expression>
    <path>$input</path>
</expression>

Оператор "as is" всегда используется по умолчанию, если никакой другой оператор не был указан, например, в маппингах.

Путь#

Выражение пути — наиболее надежный и эффективный способ обратиться к параметру или переменной. Выражение просто проходит по указанному Prism пути и берет желаемое значение.

Например, чтобы получить значение параметра name из переменной user (смотрите Переменные в маппингах), можно воспользоваться следующим выражением:

<expression>
    <path>$user/name</path>
</expression>

Скрипт#

Конструктор скриптов это наиболее гибкий из всех операторов, так как он позволяет вызвать выражение, составленное на одном из нескольких языков скриптов.

Языки скриптов#

В данный момент поддерживаются следующие языки:

  • Groovy

  • JavaScript (ECMAScript, в зависимости от используемого JDK)

  • Python

Несмотря на доступность нескольких языков, рекомендуется использовать Groovy, так как он напрямую поддерживается IDM. Поддержка JavaScript/ECMAScript зависит от того, входит ли этот язык в JDK (нет, начиная с Java 15). Python тоже по умолчанию отключен, хотя включить его можно через JDK.

Все примеры скриптов в дальнейшем будут приводиться на Groovy.

Структура скриптового выражения#

Все выражения со скриптами имеют одинаковую структуру, вне зависимости от того, где и как они используются в IDM. Эту структуру можно проиллюстрировать следующим примером:

<expression>
    <script>
        <language>http://midpoint.evolveum.com/xml/ns/public/expression/language#Groovy</language>
        <code>
            'Mr. ' + user.getFamilyName();
        </code>
    </script>
</expression>

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

Поле

Обязательноть

Описание

<language>

опциональный

URL используемого языка. Определяет язык, на котором написано скриптовое выражение. Если поле не указано, по умолчанию используется Groovy

<trace>

опциональный

Явно отслеживать исполнение данного выражения. Исполнение будет логироваться на уровне INFO, если данное свойство установлено в true

<returnType>

опциональный

Тип результата работы выражения. Возможные значения — list или scalar. Если поле не указано, тип будет автоматически определен из типа данных цели выражения. Данный параметр следует задавать только в том случае, если автоматическое определение срабатывает некорректно

<relativityMode>

опциональный

Определяет релятивность выражения. Возможные значения — relative или absolute. Подробнее смотрите в разделе Абсолютные и релятивные выражения

<includeNullInputs>

опциональный

Если параметр установлен в true (значение по умолчанию), скрипт будет обрабатываться с null значением в аргументе, если входящие данные изменяются на пустое значение или наоборот. Параметр можно установить в false для оптимизации

<code>

обязательный

Код выражения. Зависит от типа языка, используемого в выражении. Как правило является строкой, но может быть указано и в XML. Обратите внимание, что code уже встроен в XML, поэтому в таком случае необходимо корректное XML экранирование

Переменные#

В выражениях используются те же переменные, что и в маппингах. Подробнее можно прочитать в разделе Маппинги.

При необходимости можно добавить свои переменные в выражение, используя элемент <variable>:

<expression>
    <variable>
        <name>jack</name>
        <objectRef oid="c0c010c0-d34d-b33f-f00d-111111111111" type="UserType"/>
    </variable>
    <path>$jack/givenName</path>
</expression>

Функции#

В скриптовых выражениях также можно использовать функции, напрямую работающие с моделью данных IDM.

Имя функции

Описание

getObject(type, oid, [ options ])

Возвращает объект по указанному OID. Функция достает объект из соответствующего источника (внутренняя БД IDM, целевой ресурс или оба), по необходимости выполняя объединение данных, обработку политик, кеширование и т.д.

searchObjects(type, query, [ options ]), searchObjectsIterative(type, query, handler, [ options ]), searchObjectByName(type, name)

Поиск объекта указанного типа. Возвращает список объектов, соответствующих критериям

countObjects(type, query)

Подсчет объектов. Возвращает количество объектов указанного типа

executeChanges(deltas, [ options ])

Выполняет приведенные в качестве аргументов дельты

addObject(newObject, [ options ])

Добавляет приведенный объект в БД IDM, и в целевой ресурс, если применимо

modifyObject(delta, [ options ])

Изменяет объект в БД IDM и в целевом ресурсе, если применимо, согласно приведенной дельте

deleteObject(type, oid, [ options ])

Удаляет указанный объект из БД IDM и из целевого ресурса, если применимо

recompute(type, oid)

Производит перерасчет указанного объекта и всех его проекций

findShadowOwner(oid)

Возвращает владельца указанной тени

testResource(oid)

Тестирует подключение к ресурсу по указанной тени

Пример использования функций:

<outbound>
     <expression>
        <script>
            <code>
                import com.evolveum.midpoint.xml.ns._public.common.common_2a.*;
                ship = midpoint.getObject(GenericObjectType.class, "54195419-5419-5419-5419-000000000001");
                'The crew of ' + ship.getName();
            </code>
        </script>
     </expression>
</outbound>

Абсолютные и релятивные выражения#

IDM всегда работает с релятивными изменениями. В случае выражений это означает, что выражение получит каждое значение во входящих данных, и должно будет трансформировать их в результат. Так, если во входящих данных значение было удалено, в результате выражение вернет дельту удаления. Если значение было добавлено, то результатом будет дельта добавления.

Однако в некоторых случаях может быть необходимо обрабатывать все значения в абсолютном виде. Например, если у вас есть многозначный параметр UID в LDAP, и вам нужно выбрать одно правильное значение, соответствующее DN, то вам нужен список всех значений параметра. Однако в релятивном подходе каждое из значений параметра будет обрабатываться как отдельная строка.

Вид обработки выражения можно задать при помощи элемента <relativityMode>:

<expression>
    <script>
        <relativityMode>absolute</relativityMode>
        <code>
             //...
        </code>
    </script>
</expression>

Входящими данными в этом скрипте будет список всех значений, а результатом — список новых значений. IDM всегда работает в релятивном режиме, поэтому после обработки выражения IDM автоматически проведет дифф значения, чтобы получить дельту. Абсолютный вид обработки следует использоваться, когда нужно обработать все значения как единую группу, а не по одиночке, например, когда нужно выбрать какое-то определенное значение, или когда ваш алгоритм зависит от остальных значений.

Абсолютный вид обработки особенно полезен, когда нужно выбрать наиболее значимое значение из многозначного объекта:

<inbound>
    <expression>
        <script>
            <relativityMode>absolute</relativityMode>
            <code>basic.determineLdapSingleAttributeValue(basic.getAttributeStringValues(account, 'dn'), 'uid', input)</code>
        </script>
    </expression>
    <target>
        <path>name</path>
    </target>
</inbound>

Функция determineLdapSingleAttributeValue() из примера выше принимает список всех значений LDAP параметра, и затем выбирает только одно значение, которое возвращается как результат. Такой подход может быть использован для упрощения многозначных параметров LDAP до однозначных свойств IDM.

Безопасность скриптовых выражений#

Скриптовые выражения по своей сути это код, который исполняется на сервере IDM. При этом, IDM не имеет особых ограничений на этот код, так как скрипты должны быть гибкими. Хоть IDM и имеет ряд систем безопасности, таких как проверка авторизации при исполнении функций, их все еще можно обойти при помощи скриптов. Поэтому будьте осторожны, и не позволяйте недоверенным пользователям изменять выражения.

Примеры скриптов#

Выражение со скриптом на Groovy:

<expression>
<script>
  <language>http://midpoint.evolveum.com/xml/ns/public/expression/language#Groovy</language>
  <code>
    'uid=' + user.getName() + ',ou=people,dc=example,dc=com'
  </code>
</script>

Выражение со скриптом на JavaScript/EMCAScript:

<expression>
<script>
  <language>http://midpoint.evolveum.com/xml/ns/public/expression/language#ECMAScript</language>
  <code>
    'uid=' + user.getName() + ',ou=people,dc=example,dc=com'
  </code>
</script>

Генератор#

Выражение-генератор используется для создания случайного значения. Значение генерируется согласно политике значений.

Если с целевым параметром уже ассоциирована политика значений, то в выражении будет достаточно указать элемент <generate/>, соответствующая политика будет автоматически определена и использована. Это, как правило, относится к парольным политикам. Если же для целевого параметра нет очевидной политики, или же требуется использовать определенную политику, ее можно задать элементом <value{olicyRef>:

<expression>
    <generate>
        <valuePolicyRef oid="d4c010c0-d34d-b3af-fe4d-11241a11101f"/>
    </generate>
</expression>

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

Когда выражение-генератор без параметров (<generate/>) используется в маппинге для создания пароля, оно автоматически подбирает парольную политику, подходящую к целевому параметру. Так, в исходящем маппинге будет использована парольная политика ресурса, а во входящем — парольная политика юзера. Если данное поведение вам не подходит, вы можете указать требуемую политику через элемент <valuePolicyRef>.

Поиск цели назначения#

Маппинги и выражения часто используются для создания назначений. Данный оператор специально создан для упрощения этой операции. Оператор использует запрос чтобы найти целевой объект в БД IDM. Если такой объект найден, оператор создает назначение для этой цели. Такие выражения особенно полезны для шаблонов объектов.

Например:

<expression>
    <assignmentTargetSearch>
        <targetType>c:OrgType</targetType>
        <filter>
            <q:equal>
                <q:path>c:name</q:path>
                <expression>
                    <path>$organizationalUnit</path>
                </expression>
            </q:equal>
        </filter>
    </assignmentTargetSearch>
</expression>

Данное выражение поиска цели назначения будет искать объекты типа OrgType в БД по параметру name. В случае, если имя объекта равно значению системной переменной organizationalUnit, будет создана соответствующая структура назначения и OID объекта организации будет помещен в нее.

Параметр выражений <includeNullInputs> по умолчанию отключен для выражений поиска. Это сделано потому, что как правило null значения на входе являются не важны для поиска, и их пропуск снижает нагрузку на систему. Однако, если есть необходимость в обработке null значений при поиске, например, для назначения цели некоего значения по умолчанию при пустом источнике, можно явно включить параметр <includeNullInputs>, выставив его в true.

Параметры отношения#

Если вы хотите задать какие-либо нестандартные отношения для назначения и цели поиска, их нужно указать через элемент <relation>:

<expression>
    <assignmentTargetSearch>
        <targetType>c:OrgType</targetType>
        <filter>
            <q:equal>
                <q:path>c:name</q:path>
                <expression>
                    <path>$organizationalUnit</path>
                </expression>
            </q:equal>
        </filter>
        <assignmentProperties>
            <relation xmlns:org="http://midpoint.evolveum.com/xml/ns/public/common/org-3">org:manager</relation>
        </assignmentProperties>
    </assignmentTargetSearch>
</expression>

После такого выражения, в UI юзер с таким назначением будет отображен как менеджер организации.

Параметры активации#

Если вам нужно создать назначение для юзера с определенными настройками активации, это можно сделать следующим образом:

<expression>
    <assignmentTargetSearch>
        <targetType>c:RoleType</targetType>
        <oid></oid>
        <populate>
            <populateItem>
                <expression>
                    <script>
                        <code>
                            import com.evolveum.midpoint.xml.ns._public.common.common_3.ActivationStatusType
                            return ActivationStatusType.ENABLED
                        </code>
                    </script>
                </expression>
                <target>
                    <path>activation/administrativeStatus</path>
                </target>
            </populateItem>
            <populateItem>
                <expression>
                    <script>
                        <code>
                            return basic.parseDateTime("yyyy-MM-dd'T'HH:mm:ss.SSS", "2016-12-31T23:59:59.000");
                        </code>
                    </script>
                </expression>
                <target>
                    <path>activation/validTo</path>
                </target>
            </populateItem>
        </populate>
    </assignmentTargetSearch>
</expression>

Когда выражение из примера выше будет применено к юзеру, каждая роль, назначенная для него, будет иметь параметр administrativeStatus со значением ENABLED, и дату истечения активации в параметре validTo равную 31.12.2016 EOD.

Этот же механизм можно использовать для установки параметров роли и других свойств.

Создание по требованию#

Данный оператор также имеет дополнительную способность создавать цели назначений по требованию. Это может быть полезно в случае, если организационные подразделения существуют только как имена в параметрах аккаунта, и IDM нужно создать для каждого нового значения объект организации.

Например:

<expression>
    <assignmentTargetSearch>
        <targetType>c:OrgType</targetType>
        <filter>
            <q:equal>
                <q:path>c:name</q:path>
                <expression>
                    <path>$organizationalUnit</path>
                </expression>
            </q:equal>
        </filter>
        <createOnDemand>true</createOnDemand>
        <populateObject>
            <populateItem>
                <expression>
                    <path>$organizationalUnit</path>
                </expression>
                <target>
                    <path>name</path>
                </target>
            </populateItem>
        </populateObject>
    </assignmentTargetSearch>
</expression>

Если оператор не находит подходящий к запросу объект, то выражение создаст новый объект типа OrgType. Этот объект будет наполнен указанными в элементах <populateItem> значениями.

IDM позволяет помещать выражения в выражения. Так, например, можно разместить оператор поиска назначения в операторе поиска назначения.

<expression>
    <assignmentTargetSearch>
        <targetType>c:OrgType</targetType>
        <filter>
            <q:equal>
                <q:path>c:name</q:path>
                <expression>
                    <path>$organizationalUnit</path>
                </expression>
            </q:equal>
        </filter>
        <createOnDemand>true</createOnDemand>
        <populateObject>
            <populateItem>
                <expression>
                    <path>$organizationalUnit</path>
                </expression>
                <target>
                    <path>name</path>
                </target>
            </populateItem>
            <populateItem>
                <expression>
                    <assignmentTargetSearch>
                        <targetType>c:OrgType</targetType>
                        <filter>
                            <q:equal>
                                <q:path>c:name</q:path>
                                <expression>
                                    <value>TOP</value>
                                </expression>
                            </q:equal>
                        </filter>
                    </assignmentTargetSearch>
                </expression>
                <target>
                    <path>assignment</path>
                </target>
            </populateItem>
        </populateObject>
    </assignmentTargetSearch>
</expression>

Этот пример будет создавать новую организацию по требованию, и назначать ее юзеру. При этом, эта новая организация сама будет иметь назначение на некую верхнеуровневую организацию TOP.

Последовательное значение#

В выражениях можно использовать последовательности одним из двух способов: оператором или функцией в скрипте.

  • Оператор

Оператор для работы с последовательностями в выражениях — <sequentialValue>:

<mapping>
    <name>sequenceUID</name>
    <strength>weak</strength>
    <expression>
        <sequentialValue>
            <sequenceRef oid="7d4acb8c-65e3-11e5-9ef4-6382ba96fe6c"/>
        </sequentialValue>
    </expression>
    <target>
        <path>extension/uidNumber</path>
    </target>
</mapping>

Такой маппинг будет брать значение счетчика из указанной последовательности и передавать его в целевой параметр. В случае, если обработка будет неуспешной, маппинг вернет значение счетчика в последовательность для переиспользования.

  • Функция в скрипте

Для работы с последовательностями также можно использовать функцию getSequenceCounter:

<mapping>
    <name>sequenceGID</name>
    <strength>weak</strength>
    <expression>
        <script>
            <code>
                1000 - midpoint.getSequenceCounter("02cb7caa-6618-11e5-87a5-7b6c6776a63e")
            </code>
        </script>
    </expression>
    <target>
        <path>extension/gidNumber</path>
    </target>
</mapping>

Этот метод дает больше возможностей управления вычислением или форматированием значения последовательности. Пример выше демонстрирует, как задавать последовательные GID UNIX, с 999 до 0. Следует помнить, что последовательности могут только увеличиваться.

Константа#

Это оператор выражений, позволяющий получить значение константы, определенной для IDM. Подробнее о создании и использовании констант смотрите в разделе Константы.

Безопасность в выражениях#

Запуск от имени пользователя#

Обычно выражения обрабатываются от имени пользователя IDM, запустившего операцию (например, синхронизацию), используя его разрешения безопасности. Однако в некоторых случаях, выражениям могут требоваться данные, на доступ к которым у текущего пользователя нет разрешений. В таком случае операция будет завершена с ошибкой.

IDM предоставляет возможность запускать выражения от лица другого пользователя, используя элемент <runAsRef>:

<expression>
    <runAsRef oid="e5e0f2fe-0aea-11e7-b02b-2b6815aa719e"/>
    <script>
        ....
    </script>
</expression>

Такое выражение будет обрабатывать скрипт, используя разрешения безопасности пользователя с OID e5e0f2fe-0aea-11e7-b02b-2b6815aa719e. Этот пользователь также будет отображен в записях аудита как инициатор обработки выражения.

При этом, переменная actor, доступная в большинстве выражений, все еще будет возвращать пользователя, запустившего операцию, так как элемент <runAs> не влияет на нее.

Используйте этот механизм с особой осторожностью, так как он может повлечь за собой риски безопасности. Учитывайте раздел Безопасность скриптовых выражений, и назначайте роли с возможностью редактирования выражений только доверенным пользователям!