Úprava schvalovacího workflow v Oracle Identity Managementu


Tento článek popisuje jakým způsobem je možné upravit standardní schvalovací workflow v systému Sun Identity Manager (Oracle Waveset) tak, aby došlo ke schválení i v případě, kdy jeden ze schvalovatelů tuto akci zamítne. Dále zde bude rozebrána funkčnost, kdy jsou schvalovatelé definováni tím, že mají přiřazenou k tomu určenou roli.

Zde uvedu přesnější zadání problému, jehož řešení článek popisuje:

  • Upravte schvalovací proces identit tak, aby se Identity Manager choval následujícím způsobem:
    • Uživatel U1 požádá o role R1 a R2.
    • Uživatel U2 je schvalovatelem role R1.
    • Uživatel U3 je schvalovatelem role R2.
    • Uživatel U2 zamítne žádost o přidělení role R1 pro uživatele U1.
    • Uživatel U3 schválí žádost o přidělení role R2 pro uživatele U1.
    • Uživateli je přidělena role R2.
    • Uživatel je notifikován o výsledku žádosti.
    • (pozn.: Standardně by akce skončila už po zamítnutí žádosti uživatelem U2 a uživateli U1 by nebyla přidělena žádná role. Stejně by skončila akce i v případě, kdy by nejprve U2 roli R1 schválil a poté U3 roli R2 zamítl.)
  • Přidejte extended atribut k roli. Do atributu se budou ukládat business role. Uživatelé, kteří mají tuto business roli budou schvalovateli role, která má v novém extended atributu uvedenou danou business roli.

 

Následující obrázek naznačuje posloupnost volání jednotlivých workflow v Sun Identity Manageru:

Původní funkčnost:

  • po zavolání checkinView se spustí proces „Update user“
  • odtud se spustí proces „Lighthouse approvals“, které má za úkol zjistit, jestli je potřeba změny schvalovat
  • proces „Lighthouse approvals“ načte schvalovatele a spustí proces „Approval evaluator“
  • „Approval evaluator“ spouští schvalovací proces. Rekurzivně volá sebe samo pokud je velikost seznamu (který obsahuje např. přidané role) v proměnné „approvals.approvals“ větší než nula. Pokud je definován schvalovatel (aktuálního elementu seznamu) spustí se proces „Multi approval“.
  • „Multi approval“ pro každého schvalovatele, kterého dostane na vstupu vytvoří „Work item“ zavoláním procesu „Approval“.
  • V případě, že jeden ze schvalovatelů žádost zamítne, nejsou změny v userView uloženy do repository.

Vyřešení „zamítnutí updatu jako celku“

  • Bylo třeba zajistit, aby se při schvalování rolí vracela informace o tom, kdo a jakou roli schválil či neschválil. Tato funkcionalita je zajištěna ve workflow „Approval Evaluator“ v aktivitě „Process Approvals“. Pokud se jedná o schvalování role, přidá se do seznamu do proměnné „bcvAttList“ s hodnotami
    • <s>roleName</s>
    • <ref>approvals.objectName</ref>
    • <s>status</s>
    • <ref>approvals.approved</ref>
    • <s>actualApprover</s>
    • <ref>actualApprover</ref>
  • Seznam je po dokončení procesu „Approval Evaluator“ přidáván do seznamu se stejným jménem (tzn. vzniká seznam seznamů).
  • Pokud se v procesu „Update user“ zjistí, že tento seznam není null, tzn. že došlo ke schvalování rolí, přejde se do aktivity „Update User Variable“ (námi přidaná aktivita).
  • „Update User Variable“ zajistí odstranění neschválených rolí z userView (konkrétně z „user.waveset.roleInfos“).
  • Poznámka: zamítnutí jako celku je řešeno pouze při schvalování rolí. Tzn. že pokud by byl v rámci jednoho checkinView uživatel zařazen do nějaké organizace a byla mu přidělena nějaká role (předpoklad: obě operace vyžadují schválení), tak v případě schválení přidělení role a zamítnutí přesunu do organizace se role uživateli nepřidá!
Ukázka: definování proměnné bcvAttList:
<Activity id='2' name='Process Approvals' audit='true' hidden='true'>
        <Comments>      Iterate over the approvers for a particular object.      Call the standard "Multi Approval" subprocess.          </Comments>
        <Action id='0' process='Multi Approval'>
          <Argument name='approvers' value='$(approvals.approvers)'/>
          <Argument name='delegators' value='$(approvals.delegators)'/>
          <Argument name='approverObjects' value='$(approvals.approverObjects)'/>
          <Argument name='objectType' value='$(approvals.objectType)'/>
          <Argument name='objectName' value='$(approvals.objectName)'/>
          <Argument name='targetId' value='$(approvals.targetId)'/>
          <Argument name='targetName' value='$(approvals.targetName)'/>
          <Argument name='targetObjectClass' value='$(approvals.targetObjectClass)'/>
          <Argument name='authType' value='$(approvals.authType)'/>
          <Argument name='itemType' value='$(approvals.itemType)'/>
          <Argument name='timeout' value='$(approvals.timeout)'/>
          <Argument name='timeoutTask' value='$(approvals.timeoutTask)'/>
          <Argument name='escalators' value='$(approvals.escalators)'/>
          <Argument name='approvalForm' value='$(form)'/>
          <Argument name='approvalTemplate' value='$(template)'/>
          <Argument name='style' value='$(style)'/>
          <Return from='approved' to='approvals.approved'/>
        </Action>

        <Action id="1">
            <expression>
                <block>

                    <defvar name="objectType">
                        <ref>object.objectType</ref>
                    </defvar>

                    <if>
                        <eq>
                            <ref>objectType</ref>
                            <s>Role</s>
                        </eq>
                        <block>
                            <!--
                                Pokud se schvaluje role, nebude vysledny status nastaven.
                                Duvodem je to, aby se po neschvaleni jedne role nestopla cela editace.
                                Cela editace se stopne pri neschvaleni ostatnich veci, napr. organizace.
                                Vysledek schvaleni se prida do seznamu, ktery obsahuje kdo co schvalil ci neschvalil.
                            -->

                            <append name="bcvAttList">
                                <s>roleName</s>
                                <ref>approvals.objectName</ref>

                                <s>status</s>
                                <ref>approvals.approved</ref>

                                <s>actualApprover</s>
                                <ref>actualApprover</ref>
                            </append>

                            <setvar name="approvals.approved">
                                <s></s>
                            </setvar>
                        </block>
                    </if>

                </block>
            </expression>
        </Action>
        <Transition to='end'/>
      </Activity>
Ukázka kódu procesu Update User, aktivita Update User Variable
<Activity id="20" name="Update User Variable">

        <Action id="1">
            <expression>
                <block>

                    <!--
                         Role, ktere byly zamitnuty je treba
                         odebrat z user.waveset.roleInfos.
                    -->

                    <setvar name="user">
                        <rule name='removeNonApprovedRoleInfos'>
                            <argument name='user' value='$(user)'/>
                            <argument name='bcvAttList' value='$(bcvAttList)'/>
                        </rule>
                    </setvar>

                </block>
            </expression>
        </Action>

          <Transition to="Provision"/>
      </Activity>
Pravidlo, které upraví userView – odstraní neschválené role
<Rule id='#ID#Rule:removeNonApprovedRoleInfos' name='removeNonApprovedRoleInfos'>
    <MemberObjectGroups>
        <ObjectRef type='ObjectGroup' id='#ID#Top' name='Top'/>
    </MemberObjectGroups>

    <Comments>
        Odstrani z view z user.waveset.roleInfos role,
        ktere nebyly schvaleny.
    </Comments>

    <RuleArgument name="user"/>
    <RuleArgument name="bcvAttList">
        <Comments>
            Seznam ve formatu
            - List
              - jeden zaznam, @see getRulesFromBcvAttList
        </Comments>
    </RuleArgument>

    <!-- Uprava bcvAttList do lepsi podoby -->
    <defvar name="bcvAttListValues">
        <get>
            <ref>bcvAttList</ref>
            <i>0</i>
        </get>
    </defvar>

    <!-- Role, ktere budou odebrany z roleInfos. -->
    <defvar name="roleForRemove">
        <rule name='getRolesFromBcvAttList'>
            <argument name='status' value='false'/>
            <argument name='bcvAttListValues' value='$(bcvAttListValues)'/>
        </rule>
    </defvar>

    <defvar name="counter">
        <i>0</i>
    </defvar>

    <defvar name="actualRoleForRemove"/>

    <defvar name="bcvWavesetRoleInfos">
        <ref>user.waveset.roleInfos</ref>
    </defvar>

    <!--
        Prochazime seznam roli k odebrani.
        Kazdou roli odebereme z roleInfos.
        Seznam roleInfos obsahuje objekty com.waveset.object.GenericObject.
    -->

    <while>
        <lt>
            <ref>counter</ref>
            <length>
                <ref>roleForRemove</ref>
            </length>
        </lt>
        <block>
            <setvar name="actualRoleForRemove">
                <get>
                    <ref>roleForRemove</ref>
                    <ref>counter</ref>
                </get>
            </setvar>
            <setvar name="counter">
                <add>
                    <ref>counter</ref>
                    <i>1</i>
                </add>
            </setvar>

            <!-- Hledame roli s nazvem 'actualRoleForRemove' v roleInfos -->
            <defvar name="counter2">
                <i>0</i>
            </defvar>
            <defvar name="actualRoleInfos"/>
            <defvar name="actualRoleInfosName"/>

            <while>
                <lt>
                    <ref>counter2</ref>
                    <length>
                        <ref>bcvWavesetRoleInfos</ref>
                    </length>
                </lt>
                <block>
                    <setvar name="actualRoleInfos">
                        <get>
                            <ref>bcvWavesetRoleInfos</ref>
                            <ref>counter2</ref>
                        </get>
                    </setvar>
                    <setvar name="counter2">
                        <add>
                            <ref>counter2</ref>
                            <i>1</i>
                        </add>
                    </setvar>

                    <!-- Jmeno aktualni role z roleInfos -->
                    <setvar name="actualRoleInfosName">
                        <invoke name="getName">
                            <ref>actualRoleInfos</ref>
                        </invoke>
                    </setvar>

                    <if>
                        <eq>
                            <ref>actualRoleForRemove</ref>
                            <ref>actualRoleInfosName</ref>
                        </eq>
                        <remove name="bcvWavesetRoleInfos">
                            <ref>actualRoleInfos</ref>
                        </remove>
                    </if>

                </block>
            </while>

        </block>
    </while>

    <ref>user</ref>

</Rule>


Poté, co odstraníme neschválené role, můžeme udělat checkinView. Tímto jsme vyřešili první část úkolu. Nyní se pusťme do přidání dalšího úkolu.

Přidání dalších schvalovatelů

  • Vyhledání „standardních“ schvalovatelů se provádí v aktivitě „Get Approvals“ procesu „Lighthhouse Approvals“. Po vyhledání standardních schvalovatelů jsme přidali akci, která zajistí přidání dalších schvalovatelů do seznamu schvalovatelů.
  • Nejprve se vyhledají v userView role, které byly přímo přidány. Přímým přidáním se myslí to, že přímo o danou roli bylo zažádáno, tedy ne o případ, kdy se do view přidala i role, kterou obsahovala ta role, o kterou si uživatel zažádal.
  • Poté procházíme seznam přímo přidaných rolí. Pro každou roli vyhledáme role v seznamu rolí z extended atributu a pro každou takovouto roli vyhledáme osoby, které danou roli mají.
  • Každou takto nalezenou osobu přidáne do objektu „approvals“.
  • Při přidávání je nutné kontrolovat zda nedocházi ke speciálním případům (viz. dále) a pokud ano, ošetřit je. Speciálními případy (situacemi) se myslí:
    • Situace, kdy není definován žádný standardní schvalovatel („approvals„ je NULL). V takovém případě je třeba (pokud budeme schvalovatele přidávat) vytvořit tento objekt a v něm potřebné atributy).
    • Situace kdy je definován schvalovatel jiné role (seznam schvalovatelů rolí v „approvals“ neobsahuje roli, které chceme přidat schvalovatele). V této situaci musíme takový objekt vytvořit a přidat do seznamu.
    • Situace kdy je definován jen např. schvalovatel organizace a současně se změnou role uživatel přechází i do organizace s definovaným schvalovatelem. V tomto případě je nutné vytvořit s seznamu schvalovatelů objekt, který bude uchovávat seznam schvalovatelů pro role.

Pro badatele přidávám ještě pravidlo, které vrátí seznam přímo přidaných rolí z userView :-)

<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE Rule PUBLIC 'waveset.dtd' 'waveset.dtd'>
<Rule id='#ID#Rule:getAddedRolesInView' name='getAddedRolesInView'>

    <!--
        Vrati seznam primo pridanych roli uzivateli.

        Pr. role KapOd obsahuje roli Kap. Pridame uzivateli roli
        KapOd  (tzv. primo), nasledkem toho se prida i role Kap.
        Toto pravidlo vsak do vysledku roli Kap nezahrne!
    -->

    <RuleArgument name="user">
        <Comments>User view</Comments>
    </RuleArgument>

    <MemberObjectGroups>
        <ObjectRef type='ObjectGroup' id='#ID#Top' name='Top'/>
    </MemberObjectGroups>

    <defvar name="updateAccounts">
        <ref>user.update.accounts</ref>
    </defvar>

    <!-- Ziskani Lighthouse uctu -->

    <defvar name="counter">
        <i>0</i>
    </defvar>
    <defvar name="bcvUserAccount"/>

    <while>
        <lt>
            <ref>counter</ref>
            <length>
                <ref>updateAccounts</ref>
            </length>
        </lt>
        <block>
            <setvar name="bcvUserAccount">
                <get>
                    <ref>updateAccounts</ref>
                    <ref>counter</ref>
                </get>
            </setvar>
            <setvar name="counter">
                <add>
                    <ref>counter</ref>
                    <i>1</i>
                </add>
            </setvar>

            <if>
                <eq>
                    <invoke name="getName">
                            <ref>bcvUserAccount</ref>
                    </invoke>
                    <s>Lighthouse</s>
                </eq>
                <setvar name="counter">
                    <length>
                        <ref>updateAccounts</ref>
                    </length>
                </setvar>
                <setvar name="bcvUserAccount">
                    <null/>
                </setvar>
            </if>
        </block>
    </while>

    <defvar name="bcvChanges">
        <invoke name="get">
            <ref>bcvUserAccount</ref>
            <s>changes</s>
        </invoke>
    </defvar>

    <defvar name="counter2">
        <i>0</i>
    </defvar>

    <defvar name="bcvRoleInfos"/>

    <!-- Ziskani changes objektu roleInfos, ktery obsahuje seznamy novych a starych roli -->

    <while>
        <lt>
            <ref>counter2</ref>
            <length>
                <ref>bcvChanges</ref>
            </length>
        </lt>
        <block>
            <setvar name="bcvRoleInfos">
                <get>
                    <ref>bcvChanges</ref>
                    <ref>counter2</ref>
                </get>
            </setvar>
            <setvar name="counter2">
                <add>
                    <ref>counter2</ref>
                    <i>1</i>
                </add>
            </setvar>

           <if>
                <eq>
                    <invoke name="getName">
                            <ref>bcvRoleInfos</ref>
                    </invoke>
                    <s>roleInfos</s>
                </eq>
                <setvar name="counter2">
                    <length>
                        <ref>bcvChanges</ref>
                    </length>
                </setvar>
                <setvar name="bcvRoleInfos">
                    <null/>
                </setvar>
            </if>
        </block>
    </while>

    <!-- Nyni je treba ziskat seznamy Stringu s nazvy puvodnich a pridanych roli -->

    <defvar name="bcvAddedAsListOfGenericAttribute">
        <removeAll>
            <appendAll>
                <invoke name="get">
                    <ref>bcvRoleInfos</ref>
                    <s>new</s>
                </invoke>
            </appendAll>
            <appendAll>
                <invoke name="get">
                    <ref>bcvRoleInfos</ref>
                    <s>old</s>
                </invoke>
            </appendAll>
        </removeAll>
    </defvar>

    <!-- Nyni je treba projit bcvAddedAsListOfGenericAttribute a do vysledku pridat nazvy roli -->

    <defvar name="counter3">
        <i>0</i>
    </defvar>

    <defvar name="bcvActualGenericAttribute"/>

    <defvar name="result"/>

    <while>
        <lt>
            <ref>counter3</ref>
            <length>
                <ref>bcvAddedAsListOfGenericAttribute</ref>
            </length>
        </lt>
        <block>
            <setvar name="bcvActualGenericAttribute">
                <get>
                    <ref>bcvAddedAsListOfGenericAttribute</ref>
                    <ref>counter3</ref>
                </get>
            </setvar>
            <setvar name="counter3">
                <add>
                    <ref>counter3</ref>
                    <i>1</i>
                </add>
            </setvar>
            <!-- Z generic atributu se vytvori genericobject, na nem se zavola getName -->

            <defvar name="bcvActualGenericObject">
                <invoke name="get">
                    <ref>bcvActualGenericAttribute</ref>
                  </invoke>
            </defvar>

            <defvar name="bcvAssignedBy">
                <invoke name="get">
                    <ref>bcvActualGenericObject</ref>
                    <s>assignedBy</s>
                </invoke>
            </defvar>

            <if>
                <isnull>
                    <ref>bcvAssignedBy</ref>
                </isnull>
                <append name="result">
                    <invoke name="getName">
                      <ref>bcvActualGenericObject</ref>
                    </invoke>
                </append>
            </if>
        </block>
    </while>

    <ref>result</ref>
</Rule>


Doufám, že tento poměrně zdlouhavý návod na řešení ne úplně snadného problému někomu usnadní práci. Kdybych já osobně měl na začátku práce něco podobného k dispozici, ušetřil bych asi 40% času :-)