czechidm-progdoc - Programátorská dokumentace
Transkript
czechidm-progdoc - Programátorská dokumentace
CzechIdM 1.1 czechidm-progdoc Programátorská dokumentace Jan Gregor Jaromír Mlejnek Jana Moudrá Vojtěch Matocha czechidm-progdoc CzechIdM 1.1 czechidm-progdoc Programátorská dokumentace Vydání 0 Autor Autor Autor Autor Jan Gregor Jaromír Mlejnek Jana Moudrá Vojtěch Matocha © 2012 BCV solutions s.r.o.. Red Hat, Red Hat Enterprise Linux, Shadowman logo, JBoss, MetaMatrix, Fedora, Infinity Logo a RHCE jsou ochranné známky Red Hat, Inc. registrované v USA a dalších zemích. Linux® je ochranná známka Linuse Trovaldse v USA a dalších zemích. Java® je ochranná známka Oracle. 7. května 1168/70 Praha 4 - Chodov, 149 00Czech Republic Phone: +420 602 275 444 Tento text vznikl jako programátorská dokumentace Identity Manageru CzechIdM. Obsahuje stručný přehled důležitých součástí CzechIdM a bližší vysvětlení některých komplikovanějších oblastí. Dokumentace by měla sloužit všem vývojářům CzechIdM, kromě obecného modelu obsahuje návody na vytváření workflow, pravidel, e-mailových šablon, testů, konektorů a nových stránek a formulářů. Text napomůže čtenáři proniknout do existujícího kódu CzechIdM a ozřejmí mu vztahy mezi datovou, aplikační a prezentační vrstvou a klíčové metody, jimiž mezi sebou tyto vrstvy komunikují. Dokument není uživatelským manuálem CzechIdM, je určen člověku, který chce aktivně měnit zdrojový kód CzechIdM nebo pochopit souvislosti v kódu již existujícím. Preface ix 1. Hlášení chyb .................................................................................................................. ix 1. Struktura systému 1.1. Třívrstvá architektura .................................................................................................... 1.2. Moduly a jejich topologie .............................................................................................. 1.3. Webové rozhraní .......................................................................................................... 1.3.1. Implementační detaily ........................................................................................ 1.3.2. Repository ......................................................................................................... 1.3.3. Použité technologie ........................................................................................... 1 1 2 5 5 6 7 2. Aplikační vrstva 9 2.1. Přihlašování ................................................................................................................. 9 2.2. Workflow, pravidla a e-mailové šablony ....................................................................... 10 2.2.1. Předávání proměnných mezi procesy ................................................................ 10 2.3. Bussiness konstanty ................................................................................................... 11 2.4. Pravidlo getExtendedAttributesNames ......................................................................... 11 2.5. Modul Utils ................................................................................................................. 11 3. Datová vrstva 3.1. Entity ......................................................................................................................... 3.1.1. Hierarchie entit ................................................................................................ 3.1.2. View ............................................................................................................... 3.2. Obecné třídy uchovávající data ................................................................................... 3.2.1. DTOModifiable <T> .................................................................................... 3.2.2. DTO ................................................................................................................. 3.2.3. DTOModification ......................................................................................... 3.2.4. DTOGroup ....................................................................................................... 3.2.5. DTOList<E> ................................................................................................... 3.2.6. DTOLinkedList<E> ....................................................................................... 3.2.7. View ............................................................................................................... 3.2.8. GenericView ................................................................................................. 3.2.9. UserView ....................................................................................................... 3.2.10. Extended atributy ........................................................................................... 3.2.10.1. Návrh ................................................................................................. 3.2.10.2. Třída ExtendedAttribute ................................................................ 3.3. View handlery ............................................................................................................ 3.3.1. Metoda ViewHandler.createEmptyView .............................................................. 3.3.2. Metoda ViewHandler.createView ....................................................................... 3.3.3. Metoda ViewHandler.processViewChanges ....................................................... 3.4. Třída Data ................................................................................................................. 3.4.1. Získání pohledu na existující entitu - metoda Data.checkoutView ................... 3.4.1.1. Získání příslušného managementu ......................................................... 3.4.1.2. Metoda createView v rozhraní ViewHandler ..................................... 3.4.1.3. Vytvoření pohledu na identitu ................................................................. 3.4.2. Získání pohledu pro čtení na existující entitu - metoda Data.getReadOnlyView ........................................................................................ 3.4.3. Získání ořezaného pohledu na existující entitu - metoda Data.checkoutTrimmedView ................................................................................. 3.4.4. Zpracování změn ve View - metoda Data.checkinView ................................. 3.4.4.1. Získání příslušného managementu ......................................................... 3.4.4.2. Metoda saveView ................................................................................ 3.4.4.3. Provisioning .......................................................................................... 13 13 13 13 14 14 14 14 14 15 15 15 15 15 16 16 17 17 17 17 18 18 18 18 18 18 19 19 19 19 20 20 iii czechidm-progdoc 3.4.4.4. Uvolnění zámku .................................................................................... 3.4.5. Specifika metody Data.checkinView pro parametr typu UserView ................. 3.4.5.1. Workflow "user.before.checkin" v metodě beforeSaveView ................... 3.4.5.2. Metoda processViewChanges na třídě UserViewHandler .................. 3.4.5.3. UserProvisioning ................................................................................... 3.4.6. Další užitečné metody na třídě Data ................................................................ 3.4.6.1. Data.addRoleWithoutApprovement ................................................. 3.4.6.2. Data.delete ...................................................................................... 3.4.6.3. Data.getEntityId ............................................................................ 3.4.6.4. Data.getLoggedUserName ................................................................ 3.4.6.5. Data.getSchemaId ............................................................................ 3.4.6.6. Data.listIds .................................................................................... 3.4.6.7. Data.setUserAccountsEnabled ...................................................... 3.4.6.8. Data.listResourceSchemas ............................................................ 3.4.6.9. Data.deleteAccount ........................................................................ 3.5. Criteria ................................................................................................................. 20 20 20 21 22 22 22 22 22 22 22 23 23 23 23 23 4. Prezentační vrstva 4.1. Použité technologie .................................................................................................... 4.2. Členění uživatelského rozhraní ................................................................................... 4.3. Třída GenericManagedBean ..................................................................................... 4.3.1. Metoda getPageParameters ......................................................................... 4.3.2. Metoda processSubmit ................................................................................. 4.3.3. Metoda processAction ................................................................................. 4.3.4. Metoda processActionWithNewWorkflow ................................................... 4.4. JSF šablony ............................................................................................................... 4.5. Psaní formulářů pomocí JSF a workflow ...................................................................... 4.5.1. Spuštění nového workflow ................................................................................ 4.5.2. Propojení JSF šablon a workflow ...................................................................... 4.5.3. Zobrazení dat workflow v JSF stránce .............................................................. 4.5.4. Ovlivnění již běžícího workflow ......................................................................... 4.5.5. Internacionalizace ............................................................................................ 25 25 25 25 25 25 25 26 26 26 26 27 28 29 29 5. Workflow 5.1. Jak vytvořit workflow .................................................................................................. 5.1.1. Úvod ............................................................................................................... 5.1.2. Ukázkové workflow .......................................................................................... 5.2. Notifikace pomocí e-mailu ........................................................................................... 5.3. Workflow engine ......................................................................................................... 5.3.1. Předdefinované proměnné ............................................................................... 5.4. Metody související s workflow na třídě Application ........................................................ 5.5. Výstupní hodnota ....................................................................................................... 31 31 31 32 36 36 37 38 38 6. Pravidla 6.1. Pravidla ..................................................................................................................... 6.2. Ukázkové pravidlo ...................................................................................................... 6.3. Rule engine ............................................................................................................... 6.4. Transformační pravidla ............................................................................................... 6.5. Pravidla pro výpočet nové hodnoty atributu .................................................................. 6.6. Korelační pravidla ....................................................................................................... 6.7. Pravidla pro blokování účtů ......................................................................................... 6.8. Pravidla before provisioning a after provisioning ........................................................... 39 39 39 40 40 41 41 42 42 iv 7. Logování 7.1. Úvod do logování v CzechIdM .................................................................................... 7.2. Co všechno je logováno? ........................................................................................... 7.3. Auditní informace ....................................................................................................... 7.4. Uživatelské požadavky ............................................................................................... 7.5. Identifikátor požadavku ............................................................................................... 7.6. Začátek a konec uživatelského požadavku .................................................................. 7.7. Zápis logu .................................................................................................................. 7.8. Jak logovat z kódu ..................................................................................................... 45 45 45 45 45 46 46 47 47 8. Schvalování 8.1. Schvalovací proces .................................................................................................... 8.1.1. Pohled uživatele .............................................................................................. 8.1.2. Pohled administrátora ...................................................................................... 8.1.3. Pohled vývojáře ............................................................................................... 8.1.4. Příklad vytvoření schvalovací úlohy ve workflow ................................................ 8.2. Delegace schvalovacích procesů ................................................................................. 8.2.1. Pohled uživatele .............................................................................................. 8.2.2. Pohled vývojáře ............................................................................................... 49 49 49 49 50 52 52 52 52 9. Plánování úloh 55 9.1. Scheduler a task executer .......................................................................................... 55 10. Koncové systémy 10.1. Definice koncového systému ..................................................................................... 10.1.1. Pravidla spouštěná před a po operaci na systému ........................................... 10.1.2. Nastavení způsobu blokování (a odblokování) účtu .......................................... 10.2. Přilinkování uživatelského účtu .................................................................................. 10.3. Vypnutí reálných modifikací systému ......................................................................... 10.4. Aktualizace hodnot atributů účtů na koncových systémech .......................................... 10.4.1. Aktualizace z hlediska identity ........................................................................ 10.4.2. Aktualizace z hlediska rolí .............................................................................. 10.4.3. Standardní řešení výpočtu hodnot typu "merge" ............................................... 57 57 57 57 57 58 58 59 59 60 11. Identity Connectors 11.1. Definice konektoru .................................................................................................... 11.2. Lokální a vzdálený connector server .......................................................................... 11.3. Funcionalita konektorů .............................................................................................. 11.4. Spouštění externích skriptů na koncových systémech ................................................. 11.5. Struktura tříd Universal JDBC konektoru .................................................................... 11.6. Jak použít Universal JDBC Connector ....................................................................... 11.6.1. Konfigurace konektoru .................................................................................... 11.6.2. Skript CREATE .............................................................................................. 11.6.3. Skript CREATE .............................................................................................. 11.6.4. Skript UPDATE .............................................................................................. 11.6.5. Skript DELETE ............................................................................................... 11.6.6. Skript LISTALL ............................................................................................... 11.6.7. Skript SCHEMA ............................................................................................. 11.6.8. Skript GET ..................................................................................................... 11.6.9. Skript SYNC .................................................................................................. 11.6.10. Skript GET_LAST_SYNC_TOKEN ................................................................. 11.7. Database Table konektor ........................................................................................... 11.8. SSH konektor ........................................................................................................... 63 63 63 63 64 64 65 65 66 66 69 72 74 76 77 79 80 80 82 v czechidm-progdoc 11.8.1. 11.8.2. 11.8.3. 11.8.4. 11.8.5. 11.8.6. 11.8.7. 11.8.8. 11.8.9. Konfigurace konektoru .................................................................................... Skript Create ................................................................................................. Skript Update ................................................................................................. Skript Delete .................................................................................................. Skript DisableUser ......................................................................................... Skript EnableUser .......................................................................................... Skript ListObjects ........................................................................................... Skript AttributesSchema ................................................................................. Příklad univerzálního skriptu ........................................................................... 82 83 84 84 84 84 85 85 85 12. Typické procesy správy životního cyklu identit 93 12.1. Závislosti mezi procesy ............................................................................................. 93 12.2. Proces "Příchod zaměstnance" .................................................................................. 94 12.2.1. Souhrn .......................................................................................................... 94 12.2.2. Popis procesu "Příchod zaměstnance" ............................................................ 94 12.2.3. Diagram ........................................................................................................ 95 12.3. Proces "Přidat oprávnění uživateli" ............................................................................ 96 12.3.1. Souhrn .......................................................................................................... 96 12.3.2. Popis procesu "Přidat oprávnění uživateli" ....................................................... 96 12.3.3. Diagram ........................................................................................................ 97 12.4. Proces "Odebrat oprávnění uživateli" ......................................................................... 97 12.4.1. Souhrn .......................................................................................................... 97 12.4.2. Popis procesu "Odebrat oprávnění uživateli" .................................................... 98 12.4.3. Diagram ........................................................................................................ 99 12.5. Proces "Povolení systémů" ....................................................................................... 99 12.5.1. Souhrn .......................................................................................................... 99 12.5.2. Popis procesu "Povolení systémů" ................................................................ 100 12.5.3. Diagram ...................................................................................................... 100 12.6. Proces "Zakázání systémů" ..................................................................................... 101 12.6.1. Souhrn ........................................................................................................ 101 12.6.2. Popis procesu "Zakázání systémů" ............................................................... 101 12.6.3. Diagram ...................................................................................................... 102 12.7. Proces "Změna popisných dat uživatele" .................................................................. 102 12.7.1. Souhrn ........................................................................................................ 102 12.7.2. Popis procesu "Změna popisných dat uživatele" ............................................. 103 12.7.3. Diagram ...................................................................................................... 104 12.8. Proces "Změna uživatelských atributů" ..................................................................... 104 12.8.1. Souhrn ........................................................................................................ 104 12.8.2. Popis procesu "Změna uživatelských atributů" ............................................... 105 12.8.3. Diagram ...................................................................................................... 105 12.9. Proces "Změna pracovní pozice" ............................................................................. 105 12.9.1. Souhrn ........................................................................................................ 105 12.9.2. Popis procesu "Změna pracovní pozice" ........................................................ 106 12.9.3. Diagram ...................................................................................................... 107 12.10. Proces "Vyjmutí z evidenčního počtu" .................................................................... 107 12.10.1. Souhrn ...................................................................................................... 107 12.10.2. Popis procesu "Vyjmutí z evidenčního počtu" ............................................... 107 12.10.3. Diagram ..................................................................................................... 108 12.11. Proces "Přesun pracovní pozice" ........................................................................... 108 12.11.1. Souhrn ....................................................................................................... 108 12.11.2. Popis procesu "Přesun pracovní pozice" ...................................................... 109 12.11.3. Diagram ..................................................................................................... 109 vi 12.12. Proces "Provedení časované události" ................................................................... 12.12.1. Souhrn ...................................................................................................... 12.12.2. Popis procesu "Provedení časované události" .............................................. 12.12.3. Diagram ..................................................................................................... 12.13. Proces "Ukončení PPV" ........................................................................................ 12.13.1. Souhrn ...................................................................................................... 12.13.2. Popis procesu "Ukončení PPV" ................................................................... 12.13.3. Diagram ..................................................................................................... 12.14. Proces "Smazání uživatele" ................................................................................... 12.14.1. Souhrn ...................................................................................................... 12.14.2. Popis procesu "Smazání uživatele" ............................................................. 12.14.3. Diagram ..................................................................................................... 12.15. Proces "Vyjmutí z karantény" ................................................................................. 12.15.1. Souhrn ...................................................................................................... 12.15.2. Popis procesu "Vyjmutí z karantény" ........................................................... 12.15.3. Diagram ..................................................................................................... 12.16. Obecné řešení karantény ...................................................................................... 110 110 110 111 111 111 111 112 112 112 112 113 113 113 114 114 114 13. Konfigurace 115 13.1. Přehled konfiguračních parametrů ........................................................................... 115 14. Webová služba 14.1. Návrhový diagram tříd ............................................................................................. 14.1.1. Metoda login ................................................................................................ 14.1.2. Metoda logout .............................................................................................. 14.1.3. Metoda launchWorkflowAsynchronously ........................................................ 14.1.4. Metoda launchWorkflowSynchronously .......................................................... 14.1.5. Metoda launchWorkflowAsynchronouslyStringVars ......................................... 14.1.6. Metoda launchWorkflowSynchronouslyStringVars ........................................... 14.2. Asynchronní volání metod JAX-WS ......................................................................... 14.3. Zamezení logování WARN [StatelessBeanContext] EJBTHREE-1337 ......................... 117 117 117 117 118 118 118 118 119 119 15. Password filter 15.1. Instalace password filteru na Windows ..................................................................... 15.2. Konfigurace password filteru .................................................................................... 15.3. Vytvoření password filteru ....................................................................................... 15.4. Komunikace password filteru s CzechIdM přes webovou službu ................................. 15.5. Synchronizace hesel na straně CzechIdM ................................................................ 15.6. Zabezpečení přes SSL ............................................................................................ 15.7. Rizika a bezpečnostní opatření ................................................................................ 15.8. Užitečné informace pro vývoj a testování ................................................................. 121 121 121 122 122 123 123 124 124 16. Testování 16.1. Testování kódu ....................................................................................................... 16.2. Jak napsat jednoduchý test ..................................................................................... 16.3. Práce s výjimkami .................................................................................................. 16.4. Když záleží na pořadí ............................................................................................. 16.5. Psaní testů EJB session bean ................................................................................. 16.6. Psaní testů Seam bean .......................................................................................... 16.7. Jak testovat workflow .............................................................................................. 16.8. Pokrytí testy ........................................................................................................... 127 127 128 129 130 131 132 132 133 17. Nástroje 135 17.1. IdM Uploader .......................................................................................................... 135 vii czechidm-progdoc 17.2. IdMKeyGenerator .................................................................................................... 135 17.3. JBPMSyntaxCheck ................................................................................................. 135 A. Revision History 137 Rejstřík 139 viii Preface 1. Hlášení chyb Jestliže naleznete chyby v tomto dokumentu nebo pokud máte nápad jak dokument vylepšit, rádi vás vyslechneme. Prosíme zašlete zprávu na náš email [email protected] ix x Struktura systému 1.1. Třívrstvá architektura Při návrhu systému byla použita třívrstvá architektura. Aplikace se tedy skládá z následujících vrstev: • Prezentační vrstvy, která zajišťuje interakci s uživatelem. Systém může implementovat více různých prezentačních vrstev, například v podobě webového rozhraní nebo třeba v podobě Java aplikace. Byly vytvořeny dvě nezávislé implementace webového rozhraní. Jedno rozhraní je určeno administrátorům a druhé je pro obyčejné uživatele. Úplným oddělením těchto dvou rozhraní předcházíme nechtěné situaci, kdy uživatel pracuje z daty, která jsou určena pouze administrátorům. Kromě webového rozhraní poskytuje CzechIdM i webovou službu. • Aplikační vrstvy, která implementuje business logiku systému. V této vrstvě se nachází veškerý výkonný kód aplikace, například provisioning, synchronizace, workflows, atd. Aplikační vrstva je z velké části tvořena právě pravidly a workflow, což jsou externí programy napsané v jazyce BeanShell, uchovávané v datovém úložišti. Výhodou tohoto přístupu je jejich snadná údržba; je-li potřeba nějaká změna v běžícím CzechIdM, stačí upravit konkrétní workflow nebo pravidlo, aniž by bylo nutné provádět build na celé řešení. • Datové vrstvy, která slouží pro ukládání a načítání dat z a do trvalého úložiště neboli repository. Tato vrstva odstiňuje aplikační vrstvu od použitého datového úložiště. Tím může být libovolná relační databáze. Mezi vrstvami se data předávají v podobě DTO (Data Transfer Object) objektů. Zvažován byt také způsob předávání dat pomocí J2EE Entit. To jsou objekty, se kterými pracuje vrstva datová. Zapouzdřují data jednoho záznamu z nějaké tabulky databáze. Naproti tomu DTO je obyčejný Java objekt obsahující pouze ty atributy, které ostatní vrstvy potřebují. Tento přístup má oproti předávání Entit následující výhody: • úplná nezávislost vrstev • v DTO nejsou obsažena žádná interní data. Jedinou výjimkou je atribut version obsahující informaci o verzi Entity, ze které byl DTO vytvořen. Ten bude pouze pro čtení a i v případě jeho změny nemůže dojít k nekonzistentnosti. • kontrola komunikace s databázi. Nejsou žádná volání databáze z prezentační vrstvy pro "dotažení" dalších objektů. Tím nemůže dojít k jinak velmi časté lazy loading exception. • v rámci datové vrstvy se DTO vyskytují pouze v rozhraní, která jsou určena pro komunikaci s vrstvou aplikační. Pro komunikaci mezi jednotlivými moduly uvnitř datové vrstvy mohou být používány přímo Entity. 1 Kapitola 1. Struktura systému Obrázek 1.1. Třívrstvá architektura 1.2. Moduly a jejich topologie Každá vrstva popsaná v předchozí kapitole je dále rozdělena na moduly. Moduly jsou navzájem odděleny na úrovni adresářové struktury, v níž každému modulu odpovídá jeden adresář (s případnými podadresáři). Toto rozdělení slouží především k lepší čitelnosti a celkové správě zdrojového kódu. Rozvržení se také promítlo do webového rozhraní, a to v podobě položek v hlavním menu. Moduly prezentační vrstvy obsahují především xhtml, css soubory a obrázky. Dále pak JSF managed beany, které slouží jako action listenery a upravují data do podoby vhodné pro prezentaci ve webovém rozhraní. Kontrolují také navigaci mezi stránkami. Moduly webové (prezentační) vrstvy: Adresář Popis admin/authentication Obsahuje přihlašovací stránku do administračního rozhraní. admin/configuration Zde jsou umístěny stránky konfigurace. Jedná se především o vylistování, zobrazení, editace workflow, pravidel a emailových šablon. admin/help Adresář pro stránky s nápovědou. Nápověda je v podadresářích s kódem jazyka. Například "cs" a "en". admin/layout Zde je xhtml pro menu a základní šablona pro vzhled administračního rozhraní. admin/policy Obsahuje formuláře pro vylistování, zobrazení a editaci politik hesel. admin/report Adresář se stránkami pro reporty. admin/role Obsahuje formuláře pro vylistování, zobrazení a editaci rolí. admin/system Obsahuje formuláře pro vylistování, zobrazení a editaci koncových systémů. admin/task Obsahuje formuláře pro vylistování, zobrazení a editaci naplánovaných (pravidelně spouštěných) úloh. 2 Moduly a jejich topologie Adresář Popis admin/user Obsahuje formuláře potřebné pro správu uživatelů. admin/userTask Adresář se stránkami pro vylistování a zobrazení detailu uživatelských úkolů (schvalování). admin/workflow Obsahuje stránky na zobrazení seznamu worfklow s možností jejich spuštění. img Obsahuje loga, iconky a další obrázky. include Obsahuje xhtml, které se vkládají do více různých stránek. js Adresář obsahující soubory s javascripty. stylesheets Zde jsou umístěny kaskádové styly pro stránky i některé komponenty. user/authentication Adresář s přihlašovací stránkou do uživatelského rozhraní. user/help Adresář pro stránky s nápovědou v uživatelském rozhraní. Nápověda je v podadresářích s kódem jazyka. Například "cs" a "en". user/layout Zde je xhtml pro menu a základní šablona pro vzhled uživatelského rozhraní. user/password Obsahuje stránku pro změnu vlastního hesla v uživatelském rozhraní. user/personal Obsahuje stránky s přehledem osobních údajů a přidělenách rolí. user/settings V tomto adresáři jsou formuláře, na kterých si uživatel může nastavit webové rozhraní. user/userTask Adresář se stránkami pro vylistování a zobrazení detailu uživatelských úkolů (schvalování). Tabulka 1.1. Adresáře Moduly aplikační vrstvy jsou implementovány pomocí Enterprise Java Beans. Tato vrstva je navržena tak, aby bylo možné budoucí umístění některých jejích modulů na jiný server. Půjde zejména o moduly, u kterých se dá předpokládat velká výpočetní náročnost, například modul synchronizace. Mezi moduly v rámci této vrstvy jsou data předávána v podobě DTO získaných z vrstvy datové. Balíček Popis ...app.constant Obsahuje message katalogy pro zprávy které, pochází z aplikační vrstvy - tedy i z workflow a pravidel. Bude přesunuto do balíčku eu.bcvsolutions.idm.app.i18n. ...app.core Obsahuje třídy pro synchronizaci, provisioning a asynchronní zpracování. V budoucnu bude pravděpodobně rozděleno do samostatných balíků. ...app.email Zde jsou třídy pro posílání emailů (zpracování emailové fronty). ...app.i18n Obsahuje message katalogy pro zprávy které, pochází z aplikační vrstvy - tedy i z workflow a pravidel. ...app.page Zde jsou třídy sloužící jako prostředník s vrstvou prezentační. ...app.rule V tomto balíčku jsou třídy pro spouštění pravidel. ...app.security Jedná se o balíček zajišťující autentizaci a autorizaci. 3 Kapitola 1. Struktura systému Balíček Popis ...app.scheduler Třída v tomto balíčku zodpovídá za spouštění pravidelných úloh. ...app.sso Obsahuje servlet filter pro zajištění interakce s SSO agentem. ...app.user Zde je především třída pro provisioning uživatelů na koncové systémy. Ta je využívaná obecnou třídou z "core" balíčku. ...app.util Pomocné třídy. ...app.workflow Třídy rozšiřující a řídící workflow. ...app.ws Balíček tříd pro webovou službu. Tabulka 1.2. Balíčky Následuje přehled modulů datové vrstvy. Jejich funkce jsou využívaný vesměs skrze fasádu v podobě třídy "Data". Balíček Popis ...data.audit Jsou zde třídy pro získávání auditních informací. ...data.configurations Obsahuje třídy pro práci s nastavením CzechIdM uloženým v repository. ...data.dto V tomto balíčku jsou třídy reprezentující DTO. Tedy objekty sloužící pro přenos dat mezi vrstvami. ...data.emailtemplate Obsahuje třídy pro správu emailových šablon. ...data.entity Zde jsou třídy reprezentující datový model (strukturu repository). ...data.lock Obsahuje třídy, které zajišťují zamykání objektů. ...data.logging Balíček tříd pro logování provozních i auditních záznamů do repository. ...data.organisation Obsahuje třídy pro správu organizací (organizačních jednotek). ...data.policy Obsahuje třídy pro správu politik hesel. ...data.report Obsahuje třídy pro správu reportů. ...data.resource Obsahuje jednak třídy pro správu informací o koncových systémech uložených v repository, ale také i třídy sloužící ke komunikaci s connector frameworkem. ...data.resourcetype Obsahuje třídy pro správu typů systémů. ...data.role Obsahuje třídy pro správu rolí. ...data.rule Obsahuje třídy pro správu pravidel. ...data.security Třídy v tomto balíku slouží pro práci a autentizačními a autorizačními informacemi uloženými v repository. ...data.scheduler Obsahuje třídy, které plánovač využívá pro získávání a ukládání dat o naplánovaných úlohách do repository. ...data.user Obsahuje třídy pro správu uživatelů a částěčně i jejich účtů. ...data.usertask Obsahuje třídy pro správu uživatelských úkolů (požadavků na schválení). ...data.util Pomocné třídy 4 Webové rozhraní Balíček Popis ...data.view Obsahuje třídy reprezentující pohledy na různé objekty. Spolu s DTO slouží pro předávání informací mezi vrstvami. ...data.workflow Obsahuje třídy pro správu workflow. Tabulka 1.3. Moduly datové vrstvy 1.3. Webové rozhraní Uživatelské rozhraní je přístupné pomocí webového prohlížeče. Je vytvořeno pomocí XHTML stránek. Jejich generování zajišťuje standardizovaný framework JSF (Java Server Faces) od firmy Sun Microsystems. Při vývoji prezentační části aplikace jsme vycházeli z „Pravidel tvorby přístupného webu“. (Tato pravidla byla napsána pro účely novely zákona č. 365/2000 Sb. o informačních systémech veřejné správy, provedenou zákonem č. 81/2006 Sb.) Výhodou přístupu k BCV IdM přes webové rozhraní je zejména to, že je aplikace uživatelům i administrátorům přístupná z libovolného počítače, na kterém je nainstalován webový prohlížeč, který má zapnutý JavaScript. Není tedy nutné na jednotlivé uživatelské stanice instalovat žádný další software. Díky přístupu přes webové rozhraní navíc ani nezáleží na tom, jaký operační systém má uživatel na počítači nainstalován. Uživatelské rozhraní je rozděleno na dvě části – na část administrátorskou a na část uživatelskou. Obě části mají podobný vzhled – ve svrchní části stránky se nachází vlevo logo zákazníka, vpravo informace o aktuálně přihlášeném uživateli, následuje horizontální menu, ve střední a spodní části stránky je blok se specifickými prvky (např. formuláře či tabulky) pro aktuálně zobrazenou stránku. Tento blok obsahuje mimo jiné i lištu s ovládacími tlačítky, která se nachází ve vrchní i spodní části. V uživatelském rozhraní zobrazuje IdM jen ty položky, na které má aktuálně přihlášený uživatel právo. Uživatelské rozhraní je plně lokalizováno do českého a anglického jazyka. Jazykovou variantu si uživatel zvolí při přihlášení nebo v nastavení svého účtu. Administrátorské rozhraní je lokalizováno do českého jazyka. 1.3.1. Implementační detaily Webové rozhraní je implementováno pomocí JavaServer Faces s využitím Facelets. Struktura rozhraní je tedy definována sadou xhtml souborů. Většina stránek (ty, které se obejdou bez nějaké speciální funkčnosti či bindigu) je obsluhována jednou obecnou managed beanou GenericManagedBean. Ta je určena ke zpracování požadavků vyvolaných ze stránky. Obsahuje dvě metody: • processSubmit - nastaví kontext aktuálního workflow a tomu následně pošle signál pro přesun do dalšího stavu. • processEvent - provede mapování události na název workflow, nastaví jeho kontext a po té ho spustí. Pro práci s workflow slouží komponenta WorkflowControler. Vizte kapitolu Workflows. Mapování událostí na názvy workflow stejně jako mapování na nazvy formulářu má na starosti komponenta EventsMapping. Existuje ve scope application. Kontext workflow je nastavován podle hodnoty atributu variables komponenty PageParameters. Ta existuje ve scope Page. 5 Kapitola 1. Struktura systému Typický proces zobrazení stránky a zpracování na ní umístěném formuláře vypadá následovně: 1. Běžící workflow vyvolá akci ShowPageAction, čímž dojde k jeho pozastavení 2. Je spuštěna metoda ShowPageAction.execute. Ta získá, případně vytvoří komponentu PageParameters a do atributu variables nakopíruje obsah kontextu workflow. Případně může přidat nějaké zprávy různého typu (info, error, warning, ...). Poté za pomoci komponenty EventsMapping zjistí název stránky, která se má zobrazit. Pomocí třídy Redirect tuto stránku zobrazí. 3. Uživatel edituje formulář na zobrazené stránce. Hodnoty jsou získávány a nastavovány v komponentě PageParameters (buď přímo nebo prostřednictvím managed beany stránky). Po stisknutí tlačítka pro odeslání formuláře bude obvykle zavolána metoda GenericManagedBean.processSubmit. 4. Metoda processSubmit přetransformuje data a pomocí komponenty WorkflowControler nastaví kontext čekajícího workflow a následně mu pošle signál pro přechod do dalšího stavu. 1.3.2. Repository Pojmem repository se označuje úložiště interních informací IdM. BCV IdM ukládá svá data do relační databáze, která podporuje JDBC protokol. Mezi interní informace patří především: • informace o identitách - v repository je uloženo minimum těchto informací. Většina je ponechána na koncových systémech. Tím je zabráněno zbytečné redundanci a nekonzistentnosti mezi repository a koncovým systémem • informace o koncových systémech • auditní logy • definice workflow a pravidel • archivy výsledků reportů, workflow, ... • konfigurace • jBPM informace Pro práci s repositorý je použit ORM framework Hibernate (jedna z implementací JPA). Ten umožnuje pracovat s relačními daty stejným způsobem, jako se pracuje s objekty. Pole jednoho (případně více) záznamu v databázové tabulce jsou tak mapovány na atributy objektu, který se v tomto kontextu nazývá Entita. Některé entity obsahují: • podpis - pro kontrolu neoprávněné změny • zámek - pro kontrolu konkurenčního přístupu • auditní atributy Toto je implementováno pomocí hierarchie tříd • SignedEntity - obsahuje atribut sign typu String 6 Použité technologie • LockableEntity - rozšiřuje třídu SignedEntity o atribut lock typu DateTime • AuditedEntity - rozšiřuje třídu LockableEntity o auditní atributy Každá entita, která potřebuje jednu z výše uvedených funkčností, dědí od odpovídající třídy. Existuje několik způsobů, jak lze inheritenci promítnout do struktury databázové tabulky. My používáme způsob InheritanceType.TABLE_PER_CLASS, který atributy předků umístí do stejné tabulky společně s atributy entity. V databázi jsou tabulky, které odpovídají entitám znázorněných v diagramu konceptuálního modelu, a tabulky, které pro svůj běh potřebuje jBPM. 1.3.3. Použité technologie Systém je postaven na JEE 5.0 technologiích: Komponenta Verze Popis JavaServer Faces (JSF) http:// java.sun.com/javaee/javaserverfaces/ 1.2 framework od společnosti Sun určený pro vytváření webového rozhraní Enterprise Java Beans (EJB) http:// java.sun.com/products/ejb/index.jsp 3.0 komponentová architektura pro vytváření business vrstvy enterprise aplikací na platformě Java. Je to standardní způsob vytváření distribuovaných, transakčních, bezpečných a přenositelných aplikací. Komponenta Verze Popis JBoss Seam Framework http:// seamframework.org/ 2.2.0 Integrační framework kombinující především JSF a EJB. JBoss Hibernate http://www.hibernate.org/ 3.x Framework pro Object-relational mapping (ORM). Implementace Java Persistance API JBoss jBPM http://www.jboss.org/jbpm 3.x Business Process Management (BPM) Suite. Technologie pro vytváření a správu procesů Identity Connectors Framework and Toolkit https://identityconnectors.dev. java.net/ 1.0 Technologie pro používaní a vytváření konektorů, které slouží pro přípojení a komunikaci s externími systémy BeanShell http://www.beanshell.org/ 2.0b4 Interpreter zdrojového kódu Javy s podporou skriptování TestNG http://testng.org/ 5.9 Framework určený pro testování aplikace Apache log4j http://logging.apache.org/ log4j/ 1.2/index.html 1.2 Nástroj pro vytváření logů Tabulka 1.4. JEE technologie Další používané technologie: Tabulka 1.5. Další technologie Komponenta Verze Popis JBoss AS http://www.jboss.org/ jbossas 5.1.0 Aplikační server pro deploy IdM 7 Kapitola 1. Struktura systému Komponenta Verze Popis MySQL http://www.mysql.com/ 5 Databázový server, který bude používán pro uložení repository IdM Tabulka 1.6. Servery 8 Aplikační vrstva 2.1. Přihlašování O přihlášení uživatele do CzechIdM se stará klíčová třída Authenticator, v níž dojde k ověření uživatelského jména a hesla. Jméno a zadané heslo je nejprve načteno z credentials a následně je (podle toho, zda se uživatel snaží přihlásit do administrátorského, nebo uživatelského rozhraní) zavolána metoda authenticateIdentity na třídě Data s parametrem pro administrátorské nebo běžné uživatelské rozhraní. CzechIdM umožňuje přihlašování přes single sign-on. Tuto možnost je možné povolit nebo zakázat v konfiguračním souboru (viz kapitolu "Konfigurace"). V případě, že sso není používáno, dojde k porovnání hesla třídou SecurityManagementBean CzechIdM umožňuje přihlašování proti heslu v repository, nebo proti heslu na koncovém systému. Obvyklé je přihlašování proti heslu v repository; zde se ovšem neporovnávají plná hesla; z důvodu bezpečnosti jsou v repository uloženy pouze jejich otisky. Je-li tedy v repository nalezena identita s příslušným uživatelským jménem a otiskem hesla, je její totožnost považována za ověřenou a uživateli je umožněn přístup do uživatelského nebo administrátorského rozhraní. V některých situacích může být výhodné ověřovat identitu pomocí hesla v některém koncovém systému, například v centrálním LDAPu. Tuto možnost je třeba před spuštěním aplikačního serveru stanovit v konfiguračním souboru idm_configuration.properties. Konkrétně je zde třeba nastavit properties "auth_resource" na název systému, vůči němuž má proběhnout autentizace, "auth_schema" na schéma na tomto systému a "auth_id_attribute" na název atributu, který obsahuje identifikátor účtu (viz též kapitolu Konfigurace). Jsou-li tyto tři properties korektně nastaveny, probíhá přihlašování automaticky vůči zvolenému koncovému systému metodou authenticate v konektoru. Uživatel se přihlašuje svým běžným uživatelským jménem v CzechIdM. Nejprve proběhne autentizace proti nastavenému koncovému systému. Pokud neproběhne úspěšně, CzechIdM se může pokusit autentizovat uživatele vůči otisku hesla ve své repository. Toto chování je nutné explicitně zapnout nastavením atributu "auth_also_with_repository" na hodnotu "true". To pak umožní přihlášení například identitám - administrátorům CzechIdM, kteří nemají žádné účty na koncových systémech. # Fill in the resource name as defined in CzechIdM (e.g. Unix LDAP) auth_resource=Portal # Fill in the schema name as defined in CzechIdM (e.g. default) auth_schema=default # Fill in the name of the attribute, which holds identifier used by connector as Uid (e.g. loginName) auth_id_attribute=loginName #This flag is taken into consideration if and only if a remote system authentication is configured. auth_also_with_repository=true Po technické stránce je autentizace proti koncovému systému realizována v metodě authenticateInRemoteResource na třídě Data. Ta volá metodu authenticate na třídě ResourceManagementBean, ve které proběhne samotné ověření proti konektoru. V případě, že konektor po zavolání příslušné metody authenticate vrátí uid daného uživatele, autentizace úspěšně proběhla. 9 Kapitola 2. Aplikační vrstva try { if (checkPassword) { Uid uid = connection.connector.authenticate(ObjectClass.ACCOUNT, name, new GuardedString(password.toCharArray()), null); if (uid != null) { result = true; } } else { ConnectorObject accountObj = connection.connector.getObject(ObjectClass.ACCOUNT, new Uid(name), null); if (accountObj != null) { result = true; } } } catch (RuntimeException e) { result = false; } 2.2. Workflow, pravidla a e-mailové šablony Aplikační vrstva CzechIdM je tvořena kromě několika obecných tříd v aplikačních modulech zejména sadou workflow, pravidel a e-mailových šablon. Workflow a pravidlům jsou v této dokumentaci věnovány zvláštní kapitoly. V nich je popsáno, jak workflow a pravidla fungují a jak je vytvářet. Workflow jsou základním stavebním kamenem aplikační vrstvy, nějaké workflow je spuštěno za každým formulářem, každou zobrazenou stránkou a naprostou většinou akcí nad koncovými systémy, identitami a rolemi. 2.2.1. Předávání proměnných mezi procesy O spouštění workflow se stará třída eu.bcvsolutions.idm.app.workflow.WorkflowControlerBean. Spuštěné workflow je potom reprezentováno třídou eu.bcvsolutions.idm.app.workflow.WorkflowInstance. Každé instance této třídy udržuje proměnné v proměnné variables, z ostatních tříd je možné tyto proměnné získat nebo nastavit metodami getVariables() a setVariables(). V běžícím procesu jsou aktuální proměnné drženy v instanci třídy ContextInstance. Třída WorkflowInstance synchronizuje své proměnné právě s proměnnými v instanci ContextInstance, z níž lze proměnné získat metodou getVariables(). Proměnné jsou předávány právě tímto kontextem a lze si je představit jako mapu Map<String,Object>. Nastavení proměnných pro workflow zajišťuje třída WorkflowControllerBean, která ve své metodě createWorkflow() na základě definice workflow parsováním xml vytvoří novou instanci WorkflowInstance a nastaví proměnné pomocí statické metody createFromWorkflowDefinition na třídě WorkflowInstance. Případné spouštění podprocesu z workflow je podrobně popsáno v kapitole Workflow pomocí metod na třídě Application nebo přímo coby subprocesu definovaného v jPDl. Podobně jako workflow jsou aplikační vrstvou spouštěna pravidla. Správu pravidel má na starost třída RuleControlerBean. Na rozdíl od workflow však při provádění pravidel není na základě definice vytvořena žádná instance reprezentující pravidlo, ale je spuštěn interpreter bsh.Interpreter, který provede přímo příkazy uvedené ve zdrojovém souboru. 10 Bussiness konstanty 2.3. Bussiness konstanty Chod CzechIdM je možné konfigurovat pomocí takzvaných "bussiness" konstant. Jsou to konstanty definované pro konkrétní nasazení CzechIdM, mohou obsahovat například délku karantény, některé přihlašovací údaje k jednotlivým koncovým systémům nebo názvy specifických extended atributů. Nejde tak o konfiguraci v obvyklém slova smyslu, jde o konstanty, které by v jiném nasazení zpravidla vůbec nebyly definovány. O načítání bussiness konstant se stará pravidlo getBusinessConstants. V něm je vytvořena mapa konstant, do které může zasahovat administrátor. Tyto konstanty jsou poté přes své klíče dostupné ze všech workflow i pravidel. HashMap result = new HashMap(); result.put("ROLE_ATTR_PARENT", "parentDirPath"); // Perioda eskalace (bussiness kalendář lze upravit v konfiguračním souboru jbpm.cfg.xml) // Ve workflow a pravidlech lze odted pracovat s konstantou ESCALATION_PERIOD, ma pevnou retezcovou hodnotu "30 days". result.put("ESCALATION_PERIOD", "30 days"); 2.4. Pravidlo getExtendedAttributesNames V pravidle getExtendedAttributesNames je definována mapa extended atributů a jejich typů. To je výhodné zejména pro zobrazování extended atributů v administrátorském rozhraní. Aplikace pomocí pravidla nalezne typ atributu a je schopna ho přijatelným způsobem formátovat. 2.5. Modul Utils Modul eu.bcvsolutions.idm.app.util obsahuje pomocné metody pro práci v aplikační vrstvě. Konkrétně jde především o samotnou třídu Utils, ve které lze najít sadu užitečných metod pro práci s view, s atributy koncových systémů, s extended atributy nebo některé často používané metody pro formátování. Do této třídy by měly být přidávány všechny metody, které mají pro pravidla a workflow pomocnou funkci a je pravděpodobné, že budou volány z mnoha různých míst v aplikační vrstvě. 11 12 Datová vrstva 3.1. Entity CzechIdM uchovává informace o identitách, rolích, atributech na účtech, organizacích atd. pomocí technologie Hibernate, která umožňuje mapovat jednotlivé instance objektů přímo na řádky tabulky v databázi. Atributy objektů jsou v kódu označeny příslušnými Hibernate anotacemi, s jejichž pomocí je Hibernate mapuje do hodnot jednotlivých sloupců. Třída, jejíž instance jsou takto uchovávány, se nazývá entita. Příkladem takové entity mohou být naříklad třídy Identity nebo Role. Anotace @Entity říká, že tato entita bude ukládána do repository, anotace @Table určuje, do které tabulky budou entity ukládány, anotací @NamedQueries lze specifikovat některé předchystané query. @Entity @Table(name = "roles") @NamedQueries({ @NamedQuery( name = "Role.selectByName", query = "SELECT r FROM Role AS r WHERE r.name = :name" ) }) public class Role extends AbstractRole { ... } 3.1.1. Hierarchie entit Všechny entity v CzechIdM dědí od jednoho společného předka IdmEntity. Na něm je definován jedinečný identifikátor entityId, kterým lze odlišit dvě libovolné entity v CzechIdM. Pomocí tohoto identifikátoru jsou také implementovány relační extended atributy. Další důležitou nadtřídou některých entit je třída ViewMainEntity, jejíž potomci jsou ty třídy, na jejichž základě vzniká nějaké view. View je datový objekt, obsahující informace o entitě, který poskytuje datová vrstva vrstvě aplikační. Od této entity dědí právě například Identity nebo Role. Je-li navíc entita potomkem třídy AuditedEntity, jsou u každého záznamu dané entity logovány i informace o tom, kdo ji vytvořil, modifikoval a kdy se tak stalo. Sama tato třída dědí od třídy SignedEntity, která uchovává heš svých atributů, a není tedy možné neoprávněně měnit její atributy. Tím je zajištěna pravost a nepopiratelnost záznamů v auditním logu. Heš je počítána funkcí SHA na třídě SecurityUtils. 3.1.2. View Rozhraní View slouží k předání informací mezi datovou a aplikační vrstvou. Pohledy implementují návrhový vzor DTO a jsou vytvářeny pro přenos dat určitého typu. Za tímto účelem mohou byt rozšířeny o atributy specifické pro daný typ dat. Jako příklad poslouží nejdůležitější pohled a to UserView. Ten seskupuje nejen informace o identitě dostupné z repository, ale i data z příslušných účtů na koncových systémech. Návíce je UserView rozšířené o atributy, jejichž hodnoty mění způsob zpracování tototo pohledu. Důvodem k použití View je především zjednodušení práce s daty ve workflow a pravidlech. Na pohled se lze totiž dívat jako na jednoduchou mapu, která sdružuje 13 Kapitola 3. Datová vrstva informace logicky k sobě náležející. Dalším důvodem je, stejně jako u jiných DTO, bezpečnost. Ke skutečným datům v repository se přistupuje jen z několika tříd v datové vrstvě. Aplikační vrstva smí modifikovat pouze tyto "pohledy", a nemá tak přímý přístup k databázi. Aplikační vrstva má možnost přistupovat k pohledům na identity přes statické metody na třídě Data: metody checkoutView a checkinView. O těchto metodách podrobněji pojednávají další odstavce v této sekci. 3.2. Obecné třídy uchovávající data 3.2.1. DTOModifiable <T> Rozhraní představující datovou strukturu, která uchovává informace o změnách v datech. Je parametrizováno typem T, v němž je zachycena informace o změně. Změnu lze získat veřejnou metodou T getModification(). Jeho potomkem je rozhraní DTO. 3.2.2. DTO Rozhraní, které představuje modifikovatelnou datovou strukturu, v níž jsou data uchovávána v mapě jako dvojice "klíč - hodnota". Dědí od rozhraní DTOModifiable <Map<String, DTOModification>>, je implementováno třídou DTOGroup. Jednotlivá DTO lze vnořovat, hodnotou pro daný klíč může být opět DTO. V takovém případě je změna ve vnořeném DTO chápána jako změna rodičovského DTO. Z jednotlivých instancí DTO je možné sestavovat složitější stromové struktury. Přístup k jednotlivým hodnotám ve stromové struktuře, jíž je dané DTO kořenem, umožňují metody Serializable get(String[] path) a void put(String[] path, Object value). Parametrem path se rozumí cesta po jednotlivých klíčích do zanořených DTO. Zda taková cesta existuje, lze ověřit metodou boolean containsPath(String[] path). V případě, že je zavolána metoda put a zadaná cesta neexistuje, jsou vytvořena nová DTO a chybějící úsek cesty je nově vybudován. Rozhraní DTO je v CzechIdM rodičovským rozhraním pro View, které implementují ostatní view jako například GenericView. 3.2.3. DTOModification Třída reprezentující jednu změnu v DTO pro konkrétní klíč. Obsahuje tři důležité atributy: • Serializable oldValue - hodnota pro daný klíč před modifikací • Serializable newValue - hodnota pro daný klíč po modifikaci • Type type - druh změny, jedná se o výčtový typ, který může nabývat hodnot ADD, REMOVE a CHANGE 3.2.4. DTOGroup Třída implementující DTO pomocí hešovací mapy. Zjišťování modifikací dané DTOGroup probíhá v implementacích následujícím způsobem: 1. Vytvoří se nová prázdná výstupní mapa "klíč - modifikace" 2. Zjistí se modifikace pro konkrétní klíče tohoto DTO a zařadí se do výstupní mapy 14 DTOList<E> 3. Je-li některá z hodnot implementací DTOModifiable, zjistí se její modifikace metodou getModification() 4. Pokud je taková hodnota modifikována (getModification() vrátilo neprázdnou kolekci, neprázdnou mapu nebo jiný objekt, který není null), zařadí se do výstupní mapy pro příslušný klíč modifikaci typu CHANGE 5. Vrátí se výstupní mapa. Změny ve vnořených DTOGroup se tedy nepropagují do rodičovských DTOGroup v okamžiku, kdy k nim dojde, ale zjišťují se teprve v okamžiku, kde je na rodičovské DTOGroup zavolána metoda getModification(). Není proto efektivní volat tuto metodu opakovaně, pokud je pravděpodobné, že od posledního volání nedošlo k žádné změně. Informace o zaznamenaných modifikacích lze odstranit metodou clearModification(). V CzechIdM je DTOGroup rodičovskou třídou GenericView, což je abstraktní třída, od níž dědí ostatní view jako například UserView. 3.2.5. DTOList<E> Rozhraní rozšiřující DTOModifiable<Map<E, Integer>>. Představuje datovou strukturu, v níž jsou data uchovávána ve formě seznamu a je ukládána informace o proběhlých změnách. K metodám z DTOModifiable přidává metody z List a metody List<E> getAddedElements() a List<E> getRemovedElements(), kterými lze získat informace o přidaných a odebraných prvcích. 3.2.6. DTOLinkedList<E> Implementuje DTOList<E> pomocí spojového seznamu. Ve formě DTOLinkedList je v CzechIdM uchováván například seznam rolí dané identity. 3.2.7. View Rozhraní, které reprezentuje obecný pohled na nějakou entitu v CzechIdM. Poskytuje základní obecné informace o daném pohledu ve formě mapy, která udržuje atributy jako dvojice klíč-hodnota. 3.2.8. GenericView Abstraktní třída, která implementuje základní metody z rozhraní View. Dědí od ní velká většina ostatních view. 3.2.9. UserView Třída reprezentující pohled na jednu identitu v CzechIdM. Dědí od třídy GenericView. Atributy o identitě jsou uchovávány jako dvojice "klíč - hodnota", název a popis atributu je v tabulce: name Celé jméno identity. Slouží jako identifikátor. firstName Křestní jméno lastName Příjmení disabled Příznak toho, že je identita zablokována. 15 Kapitola 3. Datová vrstva password Heslo. Po checkoutu je vždy null. Pokud má po checkinu jinou hodnotu, heslo se změní. idmManager Atribut "name" nadřízeného nebo null, pokud uživatel nemá nadřízeného. attributes Mapa entitních extended atributů; klíčem je název atributu, hodnotou je hodnota atributu homeOrganisation Název organizace ve tvaru "top: .... : nadOrganizace:organizace" controledOrganisations Seznam názvů kontrolovaných organizací ve formátu "top: .... : nadOrganizace:organizace" roles Seznam názvů přidělených rolí accounts Mapa účtů. Klíčem je jméno resource, hodnotou mapa schémat na daném resource. Klíčem v mapě schémat je jméno schématu, hodnotou mapa atributů daného schématu. relationAttributes Mapa relačních extended atributů. Pro přesnou strukturu viz Extended atributy - návrh Tabulka 3.1. Atributy UserView 3.2.10. Extended atributy Do takzvaných "extended" atributů jsou v daném View ukládány informace, které souvisejí pouze s konkrétním nasazením CzechIdM, ne s jeho obecnou podobou. Extended atributy v CzechIdM existují dvojího druhu: "entitní" a "relační". Entitní extended atributy se vztahují přímo k jedné instanci konkrétní entity (například tituly daného uživatele), zatímco relační se vztahují ke dvěma instancím entit, které mohou být různé (například vztah identity k roli). V CzechIdM jsou oba druhy extended atributů reprezentovány třídou ExtendedAttribute. 3.2.10.1. Návrh Každý extended atribut je tvořen svým jménem a hodnotou. Tato hodnota musí být objekt implementující rozhraní Serializable. V repository jsou všechny extended atributy uchovávány v jediné tabulce extended_attributes. V ní lze nalézt sloupce popsané v následující tabulce: atr_id Jedinečný, automaticky generovaný identifikátor extended atributu name Název atributu value Hodnota atributu (LOB), může obsahovat libovolný serializovatelný objekt valueAsString Hodnota atributu převedená na řetězec metodou toString(). owner_id EntityId té entity, které atribut náleží related_id Entityid té entity, která je cílovou entitou relace. Pokud je tedy atribut čistě entitní, je owner_id a related_id stejné. Tabulka 3.2. Sloupce tabulky extended_attributes 16 View handlery Zda se jedná o atribut entitní, nebo relační, se rozhoduje na základě porovnání hodnot ve sloupcích "owner_id" a "related_id". Jsou-li stejné, jde o entitní atribut, jinak relační. Do view jsou extended atributy načítány metodou fillExtendedAttributes na třídě GenericViewHandler. Tato metoda nejprve načte z repository všechny atributy, které se týkají daného view, tedy ty, které se shodují alespon v owner_id s view, do kterého je načítáno. Entitní atributy poté uloží do samostatné DTOGroup pod klíč View.EXTENDED_ATTRIBUTES_ENTITY_KEY_NAME. Zařazení relačních atributů je složitější: pod klíčem View.EXTENDED_ATTRIBUTES_RELATION_KEY_NAME je hodnotou další DTOGroup, v níž jsou klíči entityId identifikátory konkrétních instancí entit. Hodnotou pro daný identifikátor je DTOGroup obsahující ve tvaru "klíč-hodnota" extended atributy konkrétní relace. Veškeré změny v extended atributech jsou po zavolání checkInView propagovány strukturou DTOGroup jako změny celého view, a mohou tedy být zpracovány a pozměněny v repository. 3.2.10.2. Třída ExtendedAttribute Třída reprezentující jeden entitní nebo relační extended atribut. Jednotlivé atributy jsou přímo mapovány na sloupce tabulky v repository. Ke zjištění, zda je atribut relační, nebo entitní, slouží metoda boolean isEntityExtAtr(), která vrací true, pokud je atribut entitním atributem. Instance třídy ExtendedAttribute slouží pouze pro ukládání atributů do repository a pro jejich opětovné načítání. V konkrétním view jsou extended atributy uchovávány výše popsaným způsobem pomocí vnořených DTOGroup. K načtení všech extended atributů, které se týkají daného view, slouží metoda List<ExtendedAttribute> getAllExtendedAttributes(EntityManager entityManager) na třídě GenericView. Sloupec valueAsString se dá použít při vyhledávání entit (uživatelů, organizací) pomocí extended atributů, speciálně relací "Začíná na". Řetězec, který uživatel vyhledává, musí být totiž porovnáván s hodnotou typu String. 3.3. View handlery Vytváření a zpracovávání pohledů obstarávají v datové vrstvě takzvané view handler třídy. Jde o implementace rozhraní ViewHandler. Pro každé konkrétní View (například UserView, RoleView, ...) existuje samostatný ViewHandler (UserViewHandler, RoleViewHandler, ...). ViewHandlery dědí od GenericViewHandler, který implementuje podpůrné metody využivané jeho potomky. Interface ViewHandler deklaruje následující metody. Uvedené metody jsou implementovány v konkrétních ViewHandlerech (dědících od GenericViewHandleru). 3.3.1. Metoda ViewHandler.createEmptyView Vytvoří prázdné view, které má správnou strukturu atributů s výchozími hodnotami. Aplikační vrstva volá tuhle metodu pomocí metody "createEmptyView" na třídě "Data". 3.3.2. Metoda ViewHandler.createView Metoda, která vytvoří pohled na základě již existujících dat v repository, případně i koncových systémů. Z aplikační vstvy opět voláno přes metody (checkoutView, getReadOnlyView, ...) třídy "Data". 17 Kapitola 3. Datová vrstva 3.3.3. Metoda ViewHandler.processViewChanges Metoda, která zpracuje dodané view. To znamená, že upraví nebo vytvoří potřebné entity v repository. Také může připravit hodnoty ve view k jejich propagaci na koncové systémy (samotná změna dat na koncových systémech proběhne v provisioningu). 3.4. Třída Data Tahle třída zpřístupňuje aplikační vrstvě většinu funkcí vrstvy datové. Všechny metody jsou statické. 3.4.1. Získání pohledu na existující entitu - metoda Data.checkoutView Aplikační vrstva CzechIdM nepracuje přímo s entitami uloženými v databázi, ale pouze s pohledy (view) na tyto entity. Pohledy jsou v CzechIdM reprezentovány implementacemi rozhraní View. Pro získání pohledu na konkrétní entitu slouží na třídě Data statická metoda checkoutView. Jejím prvním parametrem je typ view, které má být vráceno, druhým parametrem potom identifikátor entity, kterého se view má týkat. Po zavolání checkoutView je příslušná entita uzamčena a není možné na ní provádět změny. Proto je nezbytné po provedení změn na pohledu zámek odstranit buď metodou checkinView, při níž se zpracují i všechny provedené změny, anebo metodou releaseViewLock, která pouze uvolní zámek a změny nezpracuje. Pokud je tedy požadován pohled, na němž nebudou prováděny žádné změny, je výhodnější zavolat metodu, která vrací view pouze pro čtení: getReadOnlyView na třídě Data. Další metodou, která vrací pohled na entitu, je metoda checkoutTrimmedView. Ta však vrací pouze "ořezaný" pohled, který neobsahuje data nacházející se mimo repository. Její zavolání je rychlé, ale výsledek neobsahuje všechny informace. 3.4.1.1. Získání příslušného managementu S různými entitami v CzechIdM mohou pracovat různé implementace rozhraní DataManagement. V úvodu metody je proto na základě typu view získána příslušná implementace (pro UserView je to například UserManagementBean. Obecné metody většiny těchto tříd jsou implementovány již ve společné abstraktní nadtřídě GenericDataManagement. Na získané třídě je poté zavolána metoda retrieveView. Ta je implementována na třídě GenericDataManagement a většina jejích podtříd využívá této obecné implementace. Na začátku metody retrieveView je ověřeno, zda má uživatel práva k získání pohledu. Pokud ano, je pohled získán pomocí třídy implementující rozhraní ViewHandler pro daný typ pohledu (například UserViewHandler, RoleViewHandler,...). Velká část implementací ViewHandler je potomkem obecné abstraktní třídy GenericViewHandler a využívá některé její metody. 3.4.1.2. Metoda createView v rozhraní ViewHandler Metoda createView v rozhraní ViewHandler slouží k vytvoření pohledu na entitu zadanou v prvním parametru. Je implementována v třídách pro konkrétní typy pohledů. Příkladem metody bude metoda na třídě UserViewHandler, které se bude věnovat další odstavec. 3.4.1.3. Vytvoření pohledu na identitu K získání pohledu na identitu slouží uvnitř datové vrstvy třída UserViewHandler. V tomto odstavci bude popsán průběh vytváření pohledu na již existující identitu: 1. Vytvoření prázdného UserView. 18 Získání pohledu pro čtení na existující entitu - metoda Data.getReadOnlyView 2. Načtení domovské organizace a organizací kontrolovaných na základě atributů identity 3. Nastavení atributu hesla v pohledu na null: v databázi jsou uloženy pouze otisky hesla, které nemají pro aplikační vrstvu žádný význam. Bude-li při checkinView v tomto atributu jiné nenulové heslo, proběhne změna hesla. 4. Načtení atributu vedoucího: do atributu je uloženo jméno té identity, která je u identity, na niž je vytvářen pohled, uvedena jako vedoucí. 5. Načtení extended atributů: k načtení extended atributů do View slouží obecná metoda fillExtendedAttributes na třídě GenericViewHandler. Extended atributům je věnována samostatná sekce v kapitole o datové vrstvě. 6. Načtení přiřazených rolí na základě rolí uvedených u identity 7. Načtení informací o účtech. Struktura informací o jednotlivých účtech je blíže popsána v sekci týkající se UserView 3.4.2. Získání pohledu pro čtení na existující entitu - metoda Data.getReadOnlyView Tato metoda má téměř identický průběh jako metoda Data.checkoutView. Rozdíly jsou pouze dva: nastavení příznaku isreadonly na true, což způsobí, že provedené na tomto view nebude možné propagovat zpět do datové vrstvy. Druhým rozdílem je, že identita, na níž je vytvořen pohled tohoto typu, není uzamčena. 3.4.3. Získání ořezaného pohledu na existující entitu - metoda Data.checkoutTrimmedView Tato metoda má podobný průběh jako metoda Data.checkoutView. Výsledné View však neobsahuje všechny informace, ale pouze ty, které byly dostupné přímo z repository. Její volání je tedy výrazně rychlejší, ale výsledné View je ořezané, a lze tedy použít jen v některých situacích. 3.4.4. Zpracování změn ve View - metoda Data.checkinView Aplikační vrstva CzechIdM nepracuje přímo s entitami, které jsou ukládány do repository. Pracuje pouze s pohledy na tyto entity podle návrhového vzoru DTO. V CzechIdM jsou pohledy tvořeny implementacemi rozhraní View. V aplikační vrstvě jsou informace o konkrétní entitě získány statickou metodou Data.checkoutView(View.Type viewType, String id), která navrací pohled na danou entitu (například roli, identitu, organizaci...). Na tomto pohledu mohou být následně prováděny změny: uživateli mohou být například přiřazeny nové role nebo mu může být změněn nadřízený. V okamžiku, kdy je práce s View skončena, musí být změny propagovány do úložiště a případně i na koncové systémy. K tomu slouží statická metoda checkinView(View view) na třídě Data. V následující sekci je podrobně popsán její průběh. 3.4.4.1. Získání příslušného managementu S různými entitami v CzechIdM mohou pracovat různé implementace rozhraní DataManagement. V úvodu metody je proto na základě typu view získána příslušná implementace (pro UserView je to například UserManagementBean. Obecné metody většiny těchto tříd jsou implementovány již ve společné abstraktní nadtřídě GenericDataManagement. Na získané třídě je poté zavolána metoda 19 Kapitola 3. Datová vrstva saveView. Ta je implementována na třídě GenericDataManagement a většina jejích podtříd využívá této obecné implementace. 3.4.4.2. Metoda saveView Metoda saveView se skládá ze tří klíčových částí: Za prvé instrukcí, které mají být provedeny před samotným uložením. Za druhé samotného uložení a nakonec z instrukcí, které mají být provedeny po uložení. Před uložením je na konkrétní implementace DataManagement zavolána metoda beforeSaveView. Obecným chováním na třídě GenericDataManagement před uložením view je spuštění workflow s názvem "{typ view}.before.checkin" (například "user.before.checkin"). V něm mohou být provedeny akce, které jsou specifické pro konkrétní typ view. V již zmíněném workflow "user.before.checkin" probíhá aktualizace účtů. Přesnému průběhu checkinView pro UserView je věnována samostatná sekce. Klíčovou metodou, v níž probíhá zpracování změn, je metoda processViewChanges z rozhraní ViewHandler, v níž jsou podle typu view zaznamenány změny a aktualizována samotná entita a případně i účty na koncových systémech. Po uložení je zavolána metoda afterSaveView. Ta může být využita některými implementacemi rozhraní DataManagement k provedení některých nutných akcí. Třída SchedulerTaskManagement například využívá metodu afterSaveView k přenastavení časovače. Na třídě GenericDataManagement je tato metoda prázdná. 3.4.4.3. Provisioning Po aktualizování některých entit může být zapotřebí aktualizovat data na koncových systémech. Pokud to konkrétní implementace ukládaného View podporuje, proběhne v tomto okamžiku aktualizace pomocí metody doProvisioning na příslušné implementaci DataManagement. 3.4.4.4. Uvolnění zámku Po skončení saveView a provisioningu je metodou releaseViewLock na příslušné implementaci DataManagement uvolněn zámek na hlavní entitě daného view. Informace o entitě nyní mohou upravovat další části aplikace. 3.4.5. Specifika metody Data.checkinView pro parametr typu UserView Metoda checkinView slouží ke zpracování změn, které provedla aplikační vrstva na pohledu na danou identitu, a jejich propagování do repository a případně i na koncové systémy. V této sekci jsou popsána specifika zpracování změn pohledu na identitu v CzechIdM, reprezentovaného třídou UserView. Sekce se zejména zabývá konkrétními implementacemi některých obecných metod, pro pochopení problematiky je tedy vhodné nejprve nahlédnout do sekcí týkajících se obecného postupu při zavolání metody checkinView. 3.4.5.1. Workflow "user.before.checkin" v metodě beforeSaveView Při zpracování změn pohledu na identitu, reprezentovaného instancí třídy UserView, je používána třída UserManagement, která dědí od abstraktní třídy GenericDataManagement a využívá některých jejích obecných metod. Jednou z nich je obecná varianta metody beforeSaveView, v 20 Specifika metody Data.checkinView pro parametr typu UserView níž probíhají akce nezbytné před samotným uložením. Obecným chováním je spuštění workflow s názvem "{typ view}.before.checkin", v našem případě tedy "user.before.checkin". Uvnitř tohoto workflow je provedena jediná akce: pomocí metody Data.updateAccountsFromIdentity jsou aktualizovány atributy na účtech, jejichž vlastníkem je zpracovávaná identita. Aktualizací atributů účtů se zabývá samostatná sekce v kapitole o koncových systémech. 3.4.5.2. Metoda processViewChanges na třídě UserViewHandler Metoda processViewChanges na třídě UserViewHandler zpracovává změny na třídě UserView a zajišťuje jejich správnou propagaci do samotné entity reprezentované třídou Identity a na koncové systémy. Během metody probíhají následující fáze: 1. Zpracování rolí: jsou rozpoznány nově přiřazené role. Pro každou nově přiřazenou roli, která má definovaného schvalovatele, je spuštěn schvalovací proces (název workflow je definován konstantou Constants.ROLE_APPROVE_WF_NAME, zpravidla jako "user.role.approve"). Nově přidané role jsou přidány do seznamu rolí u příslušné identity. 2. Zpracování základních atributů: jména identity, křestního jména, příjmení, e-mailu a příznaku zablokování. Pozměněné atributy jsou přepsány u příslušné identity. 3. Zpracování hesla: nejprve je pomocí pravidla s názvem Constants.CHECK_PASSWORD_RULE_NAME (zpravidla "checkPasswords") zkontrolováno, zda je heslo řádně vyplněno. To znamená, zda jsou splněny následující podmínky: • Je-li vyplněno jedno pole pro zadání hesla, musí být zadáno i druhé. • Jsou-li obě pole pro zadání hesla vyplněna, musí se shodovat. • Nejsou-li pole vyplněna, view nesmí být pohledem na nově vytvořeného uživatele. Je-li kontrola ukončena a nejsou-li nalezeny žádné chyby, dojde ke změně hesla u identity. Staré heslo je uloženo do historie hesel. 4. Zpracování vedoucího: Je ověřeno, zda zadaný vedoucí existuje, a změna, odebrání nebo přidání vedoucího je zalogována do auditního logu. Změna vedoucího je propagována k identitě. 5. Zpracování extended atributů: UserViewHandler využívá obecnou metodu na třídě GenericViewHandler. O extended atributech pojednává samostatná sekce v kapitole o datové vrstvě. 6. Zpracování domovské organizace: změna organizace je zalogována do auditního logu a propagována do identity. 7. Zpracování kontrolovaných organizací: nalezení entit organizací a propagace změn do identity. 8. Zpracování uživatelské konfigurace: zjištěny změny v jazykové variantě, počtu řádků v tabulkách a podobně. Neexistuje-li zatím žádná konfigurace, je vytvořena nová, a změny jsou propagovány do identity 9. Aktualizace účtů na koncových systémech na základě přiřazených rolí pomocí metody actualizeAccountsFromRoles na třídě UserViewAttributesPropagator. Aktualizaci účtů na koncových systémech je věnována samostatná sekce v kapitole o koncových systémech. 21 Kapitola 3. Datová vrstva 10. Ověření, zda nové heslo splňuje bezpečnostní politiku hesel pro všechna schémata, na nichž má být heslo změněno. 11. Aktualizovaná identita je uložena do repository. 3.4.5.3. UserProvisioning Součástí statické metody checkinView na třídě Data je aktualizace dat na koncových systémech u těch pohledů, které podporují provisioning. Třída UserView provisioning podporuje, je reprezentováno třídou UserProvisioning. Po spuštění UserProvisioning proběhne provisioning účtů zvlášť pro každý koncový systém, o to se stará třída OneResourceUserProvisioning. Součástí provisioningu je zejména vytvoření nového účtu, odlinkování účtu, přilinkování účtu a další akce pracující s účtem a ne hodnotami jeho atributů. 3.4.6. Další užitečné metody na třídě Data Potřebuje-li aplikační vrstva přistupovat k datovému podkladu, nečiní tak přímo, ale prostřednictvím třídy Data. Třída Data nabízí aplikační vrstvě celou řadu užitečných statických metod. 3.4.6.1. Data.addRoleWithoutApprovement Tato metoda zajistí přidělení role danému uživateli. Na rozdíl od běžného postupu však není ověřováno, zda má role stanoveného nějakého schvalovatele, pomocí této metody lze přiřadit roli bez schválení. Jelikož některé role opravňují k přístupu na koncové systémy, nebo jsou jimi počítány atributy na účtech, probíhá na konci této metody provisioning účtů. 3.4.6.2. Data.delete Pomocí metody delete lze dosáhnout smazání entity v repository (například role, identity...). Za některých okolností však metoda delete nebude účinkovat, například při pokusu o smazání role, která je přiřazena některému uživateli. Metoda vrací hodnotu boolean podle toho, zda se podařilo entitu úspěšně smazat. 3.4.6.3. Data.getEntityId V CzechIdM má každá entita několik identifikátorů. Obvyklý identifikátor, který se používá například při metodě checkoutView, však není unikátní napříč všemi entitami v CzechIdM, a neidentifikuje proto entitu jednoznačně. Proto byl zaveden druhý identifikátor označovaný entityId, který je unikátní napříč všemi entitami v CzechIdM. Metoda popisovaná v tomto odstavci umožňuje snadno získat entityId, známe-li běžný identifikátor a typ entity. EntityId je používáno především pro relační extended atributy. Právě entityId je klíčem v mapě relačních extended atributů. 3.4.6.4. Data.getLoggedUserName Metoda getLoggedUserName na třídě Data navrací uživatelské jméno právě přihlášeného uživatele. Je užitečná při vytváření workflow; například v situaci, kdy je nutné ověřit, zda má právě přihlášený uživatel právo na akci, která proběhne spuštěním workflow. 3.4.6.5. Data.getSchemaId Každé schéma na koncovém systému má svůj vlastní identifikátor, který je jedinečný napříč všemi schématy v jedné instanci CzechIdM. Schéma lze ovšem také identifikovat dvojicí "název resource", "název schématu". Metoda getSchemaId slouží k převodu dvojice názvů na identifikátor schématu. 22 Criteria 3.4.6.6. Data.listIds Metoda listIds navrací seznam identifikátorů všech entit daného typu, které vyhovují stanoveným kritériím. Může být tedy využita například v situaci, kdy potřebujeme provést akci pouze s těmi uživateli, kteří mají nastavený extended atribut na stanovenou hodnotu. 3.4.6.7. Data.setUserAccountsEnabled V jistých situacích může být užitečné zablokovat uživateli pouze některé účty, nikoli celý uživatelský profil. K tomu slouží metoda popisovaná v tomto odstavci. Její třetí parametr udává, zda mají být účty zablokovány, či odblokovány. 3.4.6.8. Data.listResourceSchemas Vypíše identifikátory všech schémat na koncovém systému. 3.4.6.9. Data.deleteAccount Tato metoda smaže uživatelský účet s daným identifikátorem na daném resource. 3.5. Criteria Třída Criteria stanovuje kritérium výběru. Každá entita v CzechIdM tato kritéria splňuje, nebo nesplňuje. Právě pomocí instance třídy Criteria je možné získat pouze entity s danou vlastností (viz Data.listIds). Na instanci třídy Criteria lze nahlížet jako na klauzuli WHERE v jazyce SQL. Do tohoto filtru lze přidávat podmínky, které jsou spojeny logickou spojkou AND. V některých situacích může být výhodné použít takzvaný "alias", kterým lze porovnávání vztáhnout i na některou entitu, s níž je porovnávaná entita v relaci. Chceme-li proto například stanovit kritéria, která splnují všichni uživatelé, kteří mají extended atribut "valid" nastavený na "true", použijeme následující kód. Vycházíme ze znalosti struktury dat; každá identita je ve vztahu 1:n k extended atributům. Criteria criteria = new Criteria(); criteria.createAlias("extendedAttributes", "attrs"); criteria.add("attrs.name", "valid", Relation.EQ); criteria.addEq("attrs.value", true); List idList = Data.listIds(View.Type.USER, criteria); 23 24 Prezentační vrstva 4.1. Použité technologie Prezentační vrstva je založena na technologiích JavaServer Faces, Facelets a RichFaces. JavaServer Faces je framework usnadňující vývoj uživatelských rozhraní JavaServer aplikací. Použitou verzí JSF je 1.2. Tato verze používá primárně pro zobrazování JSF stránek uživateli technologii JavaServer Pages. V tomto případě však byla použita technologie Facelets, což zjednodušuje a zpřehledňuje samotný vývoj prezentační vrstvy. Podpora Ajaxu je zajištěna použitím knihoven projektu RichFaces. 4.2. Členění uživatelského rozhraní Uživatelské rozhraní je rozděleno na dvě separátní části. První část je určena pro administrátory systému, druhá část pro běžné uživatele. Po vizuální stránce jsou si obě rozhraní podobná. Hlavním důvodem pro toto rozdělení je jednoduchost pro běžné uživatele. Dále je to i bezpečnost. Administrátorské rozhraní nabízí širší možnosti správy CzechIdm, jako je správa uživatelů, rolí, workflow a další, které obyčejný uživatel nepotřebuje. Důležitou součástí jsou jBPM workflow, která jsou spouštěna po každém kliknutí na odkaz, submitnutí tlačítka formuláře atd. 4.3. Třída GenericManagedBean Třída GenericManagedBean slouží prezentační vrstvě k ovládání workflow, která jsou asociována s právě zobrazenou stránkou. V CzechIdM je s kažou stránkou či formulářem svázáno workflow, které běží na pozadí a obstarává přechody mezi stránkami a akce na datech, například třídění údajů záznamů v tabulkách. Pomocí GenericManagedBean je možné získat k těmto workflow přístup přímo z šablony stránky. 4.3.1. Metoda getPageParameters Navrací parametry stránky. Ty mohou sloužit jako zdroj dat pro přidružené workflow. 4.3.2. Metoda processSubmit Pomocí této metody je možné posunout přidružené workflow do dalšího stavu. V parametru metody uvádíme název přechodu k dalšímu stavu. 4.3.3. Metoda processAction Různé varianty této metody slouží k provedení akce v podobě workfow. Název akce je mapován na název workfow. Tato metoda tedy spustí workflow daného názvu a nastaví mu proměnné (podle parametrů předaných této metodě). Pokud je akce spuštěna ze stránky, se kterou je asociované nějaké workflow, bude toto workflow nastaveno jako rodič toho nově spouštěného. Po skončení nově spuštěného workflow dojde tedy k přechodu do dalšího stavu rodičovského workflow. Což je často smyčka zpět na uzel zobrazující stránku, ze kterého byla spuštěna akce. 25 Kapitola 4. Prezentační vrstva 4.3.4. Metoda processActionWithNewWorkflow Funkce této metody je stejná jako u "processAction" s tím rozdílem, že nové workflow není spouštěno jako podworkflow toho aktuálního. 4.4. JSF šablony CzechIdM používá JSF a framework Richfaces, ze kterého používá některé komponenty, například <rich:dataTable> či <rich:tree>. Šablony se nachází v projektu BCV_IdM/WebContent a jsou děleny podle rozhraní na administrátorské a uživatelské. Výhodou těchto šablon je, že pokud některou část používáme na více místech (například menu je použito na každé stránce), můžeme ji definovat na jednom místě a v ostatních šablonách ji jednoduše vkládat. Nedochází tak ke zbytečné duplikaci kódu, a pokud menu měníme, stačí ho změnit na jediném místě a změna se promítne do všech stránek. <ui:define name="submenu"> <ui:include src="submenu.xhtml" /> </ui:define> Příklad 4.1. Do šablony se vkládá jiná šablona submenu.xhtml. Protože má téměř každý formulář v CzechIdM vlastní šablonu, je vhodné vkládání využít. 4.5. Psaní formulářů pomocí JSF a workflow V CzechIdM jsou přechody mezi jednotlivými JSF šablonami řízeny pomocí workflow, která běží v pozadí. Potřebujeme tedy způsob, jak z šablony ovlivnit běh workflow na pozadí a jak mu předat informace. K tomu slouží třída GenericManagedBean, jíž je věnován samostatný oddíl dokumentace. 4.5.1. Spuštění nového workflow Spuštění nového workflow provedeme ze šablony zavoláním jedné z metod třídy GenericManagedBean. V ní uvedeme, jaké workflow má spustit a jaké případné parametry mu má předat. Třída GenericManagedBean se nachází v BCV_IdM/src/eu.bcvsolutions.idm.ui. <s:button action="#{genericBean.processAction}" value="#{messages.admin_users_action_new_organisation}" styleClass="menu_button" rendered="#{s:hasPermission('ORGANISATION', 'CREATE')}"> <f:param name="action" value="organisation.new" /> </s:button> Příklad 4.2. Jak spustit nové workflow. 26 Propojení JSF šablon a workflow Kód v příkladu zobrazí tlačítko pro vytvoření nové organizace. Po kliknutí na tlačítko se spustí metoda processAction, která spustí nové workflow definované pomocí elementu <f:param> s atributem name="action" a value="organisation.new", který udává, které workflow má být spuštěno. Za povšimnutí stojí atribut rendered, který udává, za jaké podmínky se má tlačítko zobrazit. Zde se zobrazí, pokud má aktuálně přihlášený uživatel právo na vytváření organizací. Workflow, které chceme spustit musí být pojmenované a tímto názvem ho poté spouštíme. <process-definition xmlns="urn:jbpm.org:jpdl-3.2" name="organisation.new"> ... stavy ve workflow </process-definition> Příklad 4.3. Každé workflow musí být pojmenované. V ukázce je vidět jak se v elementu <process-definition> pomocí atributu name workflow pojmenuje. Vytváření workflow je v této dokumentaci věnována samostatná kapitola. 4.5.2. Propojení JSF šablon a workflow Často potřebujeme přenášet data z workflow do šablony. Uděláme například ve workflow dotaz na databázi a vybereme všechny uživatele, které potom chceme v šabloně zobrazit v tabulce. K tomu, abychom to mohli udělat, potřebujeme data uložit v kontextu workflow. Důvod je ten, že právě kontext workflow je přístupný ve stránce jim zobrazené. Data uložíme do kontextu namapováním proměnné s daty na název, pod kterým bude hodnota proměnné přístupná v kontextu. Dělá se tak pomocí elementu <variable>. Takto namapovaná proměnná se poté přenese do šablony. Element <variable> obsahuje atribut name, což je jeho název, na který se odkazujeme v šabloně. Dále obsahuje atributy mapped-name a access. Atribut mapped-name slouží k tomu k mapování jmen proměnných mezi skriptem ve workflow a šablonou. Pokud jsou názvy stejné, nemusí se mapped-name používat. V atributu access můžeme nastavit, zda chceme do proměnné z našeho zdrojového kódu ukládat. V takovém případě nastavíme hodnotu write a proměnná je poté dostupná v parametrech stránky, a můžeme ji tedy v šabloně vypisovat. Chceme-li ji pouze načítat, tedy například nějakou hodnotu z formuláře chceme poslat do workflow, aby ji náš kód ve workflow mohl dál zpracovávat, tak použijeme hodnotu read. Možná je i kombinace read,write a to ve chvíli, kdy chceme proměnnou například načíst ze šablony a poté nějakým způsobem změnit její hodnotu ve workflow. Hodnota read funguje jako filtr. Pokud nevytvoříme žádnou proměnnou <variable>, která má přístup read, máme v uzlu všechny proměnné dostupné ke čtení. Nesprávné nastavení atributu access bývá častým zdrojem chyb. <node name="retrieveData"> <description> Získá userView uživatele. </description> <event type="node-enter"> <script> <expression> import eu.bcvsolutions.idm.data.Data; 27 Kapitola 4. Prezentační vrstva import eu.bcvsolutions.idm.app.Application; import eu.bcvsolutions.idm.data.view.View; import eu.bcvsolutions.idm.data.util.Enums; View userView = Data.checkoutView(View.Type.USER, userName); </expression> <variable name="userName" access="read" mapped-name="userName" /> <variable name="userView" access="write" mapped-name="userView" /> </script> </event> <transition to="deleteIdentity"></transition> </node> Příklad 4.4. Pomocí elementu <variable> můžeme ukládat data, která budeme zobrazovat v šabloně a nebo data z šablony číst. V ukázkovém zdrojovém kódu vidíme, kdy načítáme ze šablony proměnnou userName, což je uživatelské jméno uživatele. Tato proměnná je uložená ve <variable> s přístupem read. Poté pomocí této proměnné získáme View userView uživatele, které si uložíme do workflow proměnné userView, abychom userView mohli používat v šabloně a nebo v dalších uzlech workflow. Nyní již máme data uložena v kontextu workflow a můžeme tedy zobrazit stránku, která bude tythel data prezentovat uživateli. Zobrazení stránky má na starosti třída ShowPageAction. Ta očekává, že ji určíme, kterou stránku má zobrazit. Na výběr jsou dvě možnosti. První je, specifikovat stránku přímo konstantou (v příkladě níže) pomocí elementu "pageName". Druhá možnost je, že řekneme, pod jakým názvem v kontextu workflow je cesta ke stránce. K tomu slouží element "pageNameVar". Stránka je zobrazována při události "node-enter" - tedy při vstupu do uzlu. Uzel je typu "state". To znamená, že běh workflow nebude pokračovat, dokud mu někdo neřekne, že tak má udělat. Typicky to bude právě uživatel kliknutím na najaké tlačítko na stránce. Tímto tlačítkem se bude volat metoda "processSubmit" s názvem přechodu, který se má použít. V tomto případě by to mohl být "processSubmit('close')" nebo "processSubmit('reset')". <state name="showPage"> <event type="node-enter"> <action class="eu.bcvsolutions.idm.app.workflow.ShowPageAction"> <pageName>admin/user/view</pageName> </action> </event> <transition name="close" to="end-state1" /> <transition name="reset" to="checkoutView" /> </state> Příklad 4.5. Zobrazení stránky z workflow 4.5.3. Zobrazení dat workflow v JSF stránce Jak zobrazit uložená data z workflow? Data musí být uložena v elementech <variable> a poté je můžeme v JSF šabloně získat z parametrů stránky. To uděláme v šabloně pomocí hodnoty #{pageParameters.variables}. V nich jsou přístupné všechny uložené proměnné. Je vhodné si v JSF šabloně #{pageParameters.variables} uložit do proměnné, a zkrátit tím zápis. 28 Ovlivnění již běžícího workflow <c:set var="vars" value="#{pageParameters.variables}"/> <h:outputText value="#{vars.userView['firstName']} #{vars.userView['lastName']}, #{vars.userView['email']}" /> Příklad 4.6. Je vhodné si page parametry uložit do proměnné, lépe se s nimi pak pracuje. 4.5.4. Ovlivnění již běžícího workflow Předpokládejme, že máme již běžící workflow a chceme ho posunout do dalšího stavu, například pro zobrazení jiné stránky. To provedeme zavoláním metody na třídě GenericManagedBean. <h:commandButton action="#{genericBean.processSubmit('save')}" value="#{messages.cmn_btn_save}" /> Příklad 4.7. Změna stavu workflow. Po kliknutí na tlačítko dojde ke spuštění přechodu <transition> s názvem save, který se postará o uložení. Zde je vidět, proč je důležité přechody ve workflow pojmenovávat. 4.5.5. Internacionalizace K tomu, abychom toho docílili internaciontalizace, je zapotřebí, aby nebyl text v šablonách psán přímo, ale přes objekt messages. Toho dosáhneme použitím #{messages.nazev_textoveho_retezce}. Textové řetězce jsou vypsány v BCV_IdM/src/ eu.bcvsolutions.idm.ui.msgcat.{admin|common|user}, zde jsou soubory .properties. Na texty v nich uvedené odkazujeme pomocí jejich názvu v šabloně. Pro každý jazyk existuje v CzechIdM zvláštní .properties soubor. Nové jazykové mutace lze snadno docílit přeložením příslušného .properties souboru. <h:commandButton action="#{genericBean.processSubmit('save')}" value="#{messages.cmn_btn_save}" /> <h:commandButton action="#{genericBean.processSubmit('reset')}" value="#{messages.admin_cmn_reset_view}" immediate="true" /> <h:commandButton action="#{genericBean.processSubmit('close')}" value="#{messages.cmn_btn_close}" immediate="true" /> Příklad 4.8. TODO: sem dát nadpis 29 Kapitola 4. Prezentační vrstva Obrázek 4.1. Ukázka .properties souboru. Soubor lze prohlédnout v Eclipse v módu Source (zdrojový kód) nebo Properties, kdy pomocí tlačítka Add můžeme přidávat další řetězce. Samozřejmě je možné také řetězce editovat a mazat. 30 Workflow 5.1. Jak vytvořit workflow 5.1.1. Úvod V této sekci se podíváme na workflow v CzechIdM, ukážeme si, kdy a k čemu se v CzechIdM workflow používají a na příkladu se naučíme, jak vytvořit vlastní workflow. Každé jednotlivé workflow definuje nějaký proces v CzechIdM. Může to být například zablokování identity, odstranění identity, vytvoření nové role nebo přiřazení role uživateli. Podobně jsou pomocí workflow implementovány vnitřní funkce CzechIdM: když v záložce "Uživatelé" kliknete na sloupec, podle kterého má být tabulka setříděna, postará se o třídění workflow spuštěné na pozadí formuláře. V CzechIdM používáme technologii JBPM, což je engine napsaný v jazyce Java, který obstarává průběh workflow, časování, ukládání a načítání nedokončených procesů z databáze. V technologii JPBM je workflow definováno pomocí schématu, které se skládá z uzlů a přechodů mezi uzly. V jednom okamžiku se spuštěné workflow nachází v jednom uzlu. Po spuštění je to vždy pevně stanovený počáteční uzel. Každý uzel může obsahovat Java instrukce, které se provedou při vstupu do uzlu. Na základě jejich průběhu a výsledku je uzel opuštěn po některém přechodu do jiného uzlu. Pokud je uzel koncovým uzlem, z něhož nevychází žádné přechody, workflow skončí. Schéma jednoduchého workflow, které vypisuje všechny existující role v CzechIdM, si můžeme prohlédnout na následujícím obrázku: 31 Kapitola 5. Workflow Nahoře na obrázku vidíme počáteční uzel "start-state1". V něm se workflow ocitne vždy po svém spuštění. Po vykonání instrukcí v uzlu "start-state1" se workflow přesune jediným přechodem do uzlu "reset", z něj do uzlu "search" (všimněte si, že přechody jsou orientované) a do uzlu "showPage", v němž je uživateli zobrazena stránka. Z tohoto uzlu vedou přechody dva: na základě chování uživatele workflow rozhodne, zda se vydá po přechodu k uzlu "reset", nebo po přechodu ke koncovému uzlu "end-state1", ve kterém je workflow ukončeno. Naše nové workflow vytvoříme v jazyce jPDl, pomocí kterého zachytíme schéma našeho workflow v běžném xml formátu. Řekněme, že chceme vytvořit workflow, pomocí kterého deaktivujeme zadaného uživatele. Chceme, aby se naše workflow jmenovalo "disableUser", ve " BCV_IdM-ejb/repoObjects/ {identifikaceProjektu}/workflows" tedy vytvořme složku s názvem "disableUser" a v ní vytvořme soubor "processdefinition.xml". Uvnitř tohoto souboru zadefinujeme naše workflow. 5.1.2. Ukázkové workflow Otevřeme si soubor "processdefinition.xml" a nahoru napišme hlavičku xml souboru a úvodní tag, kterým specifikujeme, že se jedná o definici workflow: 32 Ukázkové workflow <?xml version="1.0" encoding="UTF-8"?> <process-definition xmlns="urn:jbpm.org:jpdl-3.2" name="disableUser"> </process-definition> Atribut "name" je důležitý, musí workflow jednoznačně pojmenovat, neboť právě přes něj bude workflow z CzechIdM vyhledáno a spuštěno. Dovnitř do tagu <processdefinition> budeme definovat jednotlivé uzly našeho workflow. Uzlů rozlišujeme několik typů, každému typu přísluší jeden xml tag. V naprosté většině případů vystačíme s následujícími typy: počáteční uzel (tag <start-state>), koncový uzel(tag <endstate>), stav (<state>) a běžný uzel (<node>). Stav se od běžného uzlu liší v jedné podstatné věci: po vstupu do běžného uzlu workflow pokračuje prvním přechodem pryč, zatímco ve stavu se workflow zastaví a čeká na signál z vnějšku, že může pokračovat. V našem souboru tedy vytvořme počáteční stav, koncový stav a tři běžné uzly. Přechody mezi uzly vytvoříme pomocí tagu <transition> <start-state name="start-state"> <transition to="getUserView"/> </start-state> <node name="getUserView"> <transition to="setUserDisabled"/> </node> <node name="setUserDisabled"> <transition to="checkinView"/> </node> <node name="checkinView"> <transition to="end-state"/> </node> <end-state name="end-state"/> Workflow, které jsme takto zadefinovali (a které zatím nic nedělá, protože uzly neobsahují žádné instrukce), zachycuje následující schéma: 33 Kapitola 5. Workflow Nyní se postupně zaměříme na jednotlivé uzly a doplníme do nich příslušné Java instrukce. Předpokládejme, že naše workflow dostane na vstupu parametr "userName", ve kterém bude jméno uživatele, který má být zablokován. V uzlu "getUserView" bychom chtěli ověřit, zda je tento parametr korektně zadán, a načíst pohled na daného uživatele: <node name="getUserView"> <event type="node-enter"> <script> <expression> import eu.bcvsolutions.idm.data.util.StringUtils; import eu.bcvsolutions.idm.app.Application; import eu.bcvsolutions.idm.data.Data; import eu.bcvsolutions.idm.data.view.View; if (StringUtils.isEmpty(userName)) { throw new IllegalArgumentException("Parametr 'userName' nemůže být null nebo prázdný"); } View userView = Data.checkoutView(View.Type.USER, userName); </expression> 34 Ukázkové workflow <variable name="userName" access="read" /> <variable name="userView" access="write"/> </script> </event> <transition to="setUserDisabled"/> </node> Java instrukce jsme do uzlu vložili obalené tagy <event>, <script> a <expression>. Za povšimnutí stoji tagy <variable> po ukončení tagu <expression>. V nich je zapotřebí vyjmenovat všechny proměnné, jejichž doba života přesahuje uzel, tedy pokud existují již z uzlu předchozího (nebo jsou některým z parametrů workflow), nebo se budou ještě používat v dalších uzlech a jejich změny se mají promítnout do vnějšího světa. K tomu slouží tag <variable>; v atributu "name" se uvádí název proměnné, v atributu "access" potom typ přístupu: "read" pouze pro čtení, "write" pro zápis, "read, write" pro čtení i zápis. Důležitou součástí jsou importy, jejich opomenutí bývá častým zdrojem chyb. Na tomto místě je dobré si uvědomit, že zdroj píšeme v prostředí xml, znak "menšítka" tedy musíme psát jako <, podobně znak "většítka" jako > a logickou konjunkci jako && V ukázce si také můžeme prohlédnout jednoduchý způsob, jak získat informace o konkrétní entitě (tedy například uživateli, roli, organizaci...) v CzechIdM. Ve workflow pracujeme s takzvanými pohledy (view) na konkrétní entitu. View můžeme snadno získat statickou metodou checkoutView na třídě Data. Prvním parametrem je typ view, druhým identifikátor entity, v našem případě jméno uživatele. Po zavolání checkoutView je na view daného uživatele vystaven zámek, nesmíme proto zapomenout před ukončením našeho workflow view zase odemknout! K tomu se dostaneme ve třetím uzlu. V prvním uzlu jsme načetli data o uživateli, nyní chceme provést samotnou deaktivaci: <node name="setUserDisabled"> <event type="node-enter"> <script> <expression> import eu.bcvsolutions.idm.app.Application; import eu.bcvsolutions.idm.data.view.View; import eu.bcvsolutions.idm.data.user.UserViewHandler; userView.put(UserViewHandler.DISABLED_ATTRIBUTE,true); Application.logInfo("Uzivatel " + userName + " byl deaktivovan.",new Object[0]); </expression> <variable name="userView" access="read,write"/> <variable name="userName" access="read"/> </script> </event> <transition to="checkinView"/> </node> V tomto uzlu jsme provedli změnu pohledu (přesná struktura UserView a dalších je k nahlédnutí v kapitole o datové vrstvě) a celou akci zalogovali. K logování slouží statické metody log{typ logu} na třídě Application. Logování je vhodné zejména pro účely ladění. Zbývá uložit view, aby se změny propagovali k entitě. To provedeme ve třetím uzlu: <node name="checkinView"> <script> 35 Kapitola 5. Workflow <expression> import eu.bcvsolutions.idm.data.view.View; import eu.bcvsolutions.idm.data.Data; Data.checkinView(userView); </expression> <variable name="userView" access="read,write"/> </script> <transition to="end-state"/> </node> Statickou metodou checkinView na třídě Data jsme propagovali změny v pohledu k samotné entitě, tedy k deaktivovanému uživateli. Tím jsme také uvolnili zámek. Kdybychom chtěli uvolnit zámek, ale změny z nějakého důvodu nepropagovat, použijeme metodu releaseViewLock(View userView). Naše jednoduché workflow je hotovo! Uvnitř workflow můžeme spustit pravidlo (viz sekce "Pravidla") pomocí metody executeRule na třídě Application. Metoda bere jako první parametr název pravidla, jako druhý parametr mapu vstupních parametrů pro pravidlo. Uvnitř workflow je také možné spouštět jiná workflow jako podprocesy, k tomu slouží metoda startWorkflow na třídě Application. Také tato metoda bere jako první parametr název workflow, jako druhý mapu parametrů. Obdobou metody startWorkflow je pro asynchronní spuštění workflow (nečeká se, až workflow skončí) metoda startWorkflowAsynchronously. 5.2. Notifikace pomocí e-mailu Součástí workflow často bývá e-mailová notifikace uživatelů, kterých se proces týká. Je-li například uživatel deaktivován, je vhodné mu zaslat e-mail. K tomu slouží metoda sendEmailToIdentity(String identityName, String templateName, Map<String, Object>) na třídě Application. Jejím prvním parametrem je jméno uživatele, kterému má být email zaslán. Druhým je název šablony, která má být použita, třetím její parametry. CzechIdM zasílá emaily postavené na základě šablon. Každá šablona je samostatný xml dokument, uložený v adresáři " BCV_IdM-ejb/repoObjects/{identifikaceProjektu}/emailTemplates". S parametry lze uvnitř šablony pracovat pomocí notace #{nazev_parametru}. <?xml version="1.0" encoding="UTF-8"?> <emailTemplate xmlns="urn:jbpm.org:bcv_emailTemplate-1.0" name="UserRoleApprove"> <language>cs</language> <subject>Schválení přidání role uživateli #{userFullName}</subject> <text> Dobrý den, žádáme Vás o schválení přidělení role "#{roleName}" uživateli "#{userName}". Popis role: #{roleDescription} </text> </emailTemplate> 5.3. Workflow engine Jako workflow engine je použito jBPM. 36 Předdefinované proměnné Definice procesů jsou uloženy v db v tabulce workflow_definitions. Zde vyskytujici se definice ale ještě nejsou viditelné samotnému jBPM. Aby mohl být proces spustěn, tak musí být deployovan v tabulkách s prefixem JBPM. K tomu slouží metoda WorkflowControler.cleanCache. Ta provede deploy (a tím pádem aktualizaci) všech definic a vymazaní vyrovnavací paměti. Tabulka workflow_definitions je pozůstatek z dřívějška, kdy byla všechna workflow mezi wait stavy držena pouze v paměti. Výjimkou bylo čekání v task uzlu, kdy se stav procesu ukládal do db. Poté ale docházel k chybám. Když se v rámci zpracování tasku spustilo další wf, které mělo být pouze v paměti, tak ve skutečnosti běželo v JbpmContextu a přistupovalo k db. Tam ovšem nebyly požadovaná data. Do budoucna se počítá se zrušením tabulky workflow_definitions. Definice by se pak deployvaly přímo do tabulek JBPM. Bude je ale třeba rozšířít tak, aby tam bylo možné udržovat: • runAs - pod právy jakého uživatele má být wf spouštěno • resultTTL - jak dlouho se má uchovávat výsledek běhu workflow (uchovávání výsledků zatím není implementováno) • sourceCode - zdrojové xmlko • atributy pro audit a zamykání Běžící workfow je v paměti reprezentováno instancí třídy WorkflowInstance. Ta přidává do kontextu jBPM procesu konstanty a další pomocné proměnné a zajišťuje, aby se proces spouštěl pod správnými právy (runAs). Pokud bylo workflow spuštěno jako podworkflow (v konstruktoru je specifikovane rodičovské wf), tak je odpovídajícímu JBPM procesu nastaveno superProcessToken na rootToken procesu rodičovského workflow. Také udržuje proměnné a název aktuálního uzlu, aby nebylo potřeba přistupovat k db při každém požadavku na čtení. Stav procesu je načítán z databáze před každým volaním metody signal pro změnu stavu. Změněný stav je opět uložen do databáze po té, co proces dorazí do čekacího stavu nebo skončí. Vše se děje v rámci metody sendSignal. Další důležitou třídou je ShowPageAction, která implementuje ActionHandler. V procesech se tedy používá jako akce pro zobrazení požadované stránky. Akce je vykonávána tak, že nejdříve jsou vytvořeny PageParameters. Do těch je uložen odkaz na aktuální workflow a také jsou zkopírovány proměnné z kontextu procesu. Pokud nemá dojít pouze k obnovení kontextu, tak je zobrazena požadovaná stránka. Že má dojít pouze k uložení kontext procesu do PageParameters se specifikuje nastavením parametru onlySaveContext na hodnotu "true". 5.3.1. Předdefinované proměnné Tyto proměnné jsou definované přímo ve zdrojácích JBPM. Konkrétně: org.jbpm.graph.action.Script.createInputMap() a org.jbpm.jpdl.el.impl.JbpmVariableResolver.resolveVariable(). Pokud ale uvedeme tyto proměnné v elementu variable, tak tím zrušíme jejich výchozí chování a najdeme v nich pravděpodobně hodnotu null. Tyto proměnné jsou naopak přístupné i v případě, že uvedeme výčet jiných promměných v elementech variable s přístupem pro čtení. Název proměnné Typ executionContext org.jbpm.graph.exe.ExecutionContext token org.jbpm.graph.exe.Token node org.jbpm.graph.def.Node task org.jbpm.taskmgmt.def.Task 37 Kapitola 5. Workflow Název proměnné Typ taskInstance org.jbpm.taskmgmt.exe.TaskInstance Tabulka 5.1. Proměnné přístupné ve skriptu Název proměnné Typ taskInstance org.jbpm.taskmgmt.exe.TaskInstance processInstance org.jbpm.graph.exe.ProcessInstance processDefinition org.jbpm.graph.def.ProcessDefinition token org.jbpm.graph.exe.Token taskMgmtInstance org.jbpm.taskmgmt.exe.TaskMgmtInstance contextInstance org.jbpm.context.exe.ContextInstance Tabulka 5.2. Proměnné přístupné v EL 5.4. Metody související s workflow na třídě Application Workflow je možné spustit také z kódu. Toho docílíme puštěním statických metod na třídě Application. Jde o statické metody public static Object startWorkflow(String name, Map<String, Object> variables), public static Object startWorkflow2(String name, Object... variables), public static void startWorkflowAsynchronously(String name, Map<String, Objectgt; variables), public static void startWorkflowAsynchronously2(String name, Object... variables) Ty spustí workflow dle zadaného jména a předá mu na vstup zadané parametry. Spustíme-li workflow z kódu metodou startWorkflow, potom se vykonávání našeho kódu přeruší, dokud pouštěné workflow nedoběhne. Další možností je takzvané asynchronní spouštění. V takovém případě se workflow z našeho kódu pustí a již se nečeká na výsledek. Další metoda, která souvisí s workflow na tříde Application je metoda public static boolean isWorkflowExists(String name). Ta pouze zjišťuje, zda workflow zadaného jména existuje. 5.5. Výstupní hodnota Občas je potřeba, aby workflow vracelo nějakou výstupní hodnotu. Jak to uvnitř workflow zařídit? Mezi variables přidáme tag <variable name="stateResult" access="write" /> a hodnotu, kterou chceme mít na výstupu vložíme do proměnné stateResult. Toto se používá například u webové služby. 38 Pravidla 6.1. Pravidla Pravidla (rules) zastupují ve světě beanshellu (tedy Javě podobného skriptovacího jazyka, ve kterém vytváříme naše workflow) procedury a funkce. Ukládáme do nich časté bloky instrukcí, které bychom zbytečně znovu a znovu rozepisovali na různých místech v našem workflow. Podobně jako workflow, definujeme i pravidla v xml souborech. Pravidla umístěna v adresáři podle použití v adresáři "BCV_IdM-ejb/repoObjects/czechidem/rules" jsou uložena obecná pravidla bez návaznosti ke konkrétnmu projektu. Všechna pravidla, která jsou specifická pro jedno dané nasazení CzechIdM, jsou pak uložena v adresáři "BCV_IdM-ejb/repoObjects/projspec/rules". 6.2. Ukázkové pravidlo V této sekci si ukážeme, jak napsat své vlastní jednoduché pravidlo. Řekněme, že chceme vytvořit pravidlo, které na základě jména uživatele vrátí jméno jeho přímého nadřízeného ve struktuře CzechIdM, pojmenujeme ho "getManager". V adresáři "BCV_IdM-ejb/repoObjects/czechidm/rules" vytvořme soubor getManager.xml. V něm zadefinujeme naše nové pravidlo. Nejprve vložíme obvyklou xml hlavičku a základní tag, který specifikuje, že se jedná o definici pravidla: <?xml version="1.0" encoding="UTF-8"?> <rule-definition xmlns="urn:jbpm.org:bcv_rule-1.0" name="getManager"> </rule-definition> Atribute "name" je důležitý, musí pravidlo jednoznačně identifikovat, přes toto jméno bude definice pravidla vyhledána. Dovnitř do tagu <rule-definition> vepíšeme samotný kód pravidla. Předpokládáme, že v parametru userName dostaneme jméno uživatele, jehož nadřízený má být vrácen. <?xml version="1.0" encoding="UTF-8"?> <rule-definition xmlns="urn:jbpm.org:bcv_rule-1.0" name="getManager"> import eu.bcvsolutions.idm.data.Data; import eu.bcvsolutions.idm.data.view.View; import eu.bcvsolutions.idm.data.user.UserViewHandler; View userView = Data.checkoutView(View.Type.USER, userName); String manager = userView.get(UserViewHandler.IDMMANAGER_ATTRIBUTE); Data.releaseViewLock(userView); return manager; </rule-definition> Na začátku pravidla nesmíme zapomenout uvést všechny importy, jejich opomenutí bývá častým zdrojem chyb. Uvnitř pravidla můžeme spustit další pravidlo pomocí metody executeRule na třídě 39 Kapitola 6. Pravidla Application. Metoda bere jako první parametr název pravidla, jako druhý parametr mapu vstupních parametrů pro pravidlo. Pravidlo může spustit i sebe samo; rekurze není problém. Uvnitř pravidla je také možné spouštět workflow, k tomu slouží metoda startWorkflow na třídě Application. Také tato metoda bere jako první parametr název workflow, jako druhý mapu parametrů. Obdobou metody startWorkflow je pro asynchronní spuštění workflow (nečeká se, až workflow skončí) metoda startWorkflowAsynchronously. V příkladu pravidla výše si povšimněme metody checkoutView na třídě Data. S její pomocí snadno získáme pohled na informace o konkrétní entitě (například uživateli, roli, organizaci...). Jejím prvním parametrem je typ entity, druhým její identifikátor. V okamžiku, kdy je pohled vytvořen, je identita uzamčena pro ostatní uživatele. Na konci pravidla tedy musíme zámek uvolnit metodou releaseViewLock. Navíc je potřeba dávat si pozor na drobnou chybu obsaženou v beanshellu, která se týká deklarace proměnných. V případě že v průběhu pravidla používáme cyklus (for, while) a v uvnitř tohoto cyklu zadeklarujeme nějakou svou lokální proměnnou, pak často dochází v beanshellu k chybě která nám znemožní tuto proměnnou upravovat (při čtení její hodnoty dostaneme vždy hodnotu s jakou byla proměnná deklarována, bez ohledu co do ní zapíšeme). Ochranou proti této chybě je deklarování proměnné před cyklem. Proměnno pak můžeme v ámci cyklu upravovat, na jeho začátku do ní třeba vložit požadovanou počáteční hodnotu. 6.3. Rule engine Pravidla jsou xml soubory (uložené v db) s jedním elementem, jehož obsahem je skript v BeanShellu. V databázi jsou uloženy v tabulce rules_definitions. Vykonáváni pravidel má na starosti třída RuleControler, přesněji její metoda executeRule. Ta načte zdrojový kód požadovaného pravidla. Dále do contextu pravidla doplní kostanty z pravidla getBusinessConstants a přidá proměnné předané metodě jako parametr. Nakonec spustí interpretr BeanShellu a vrátí výsledek vykon 6.4. Transformační pravidla Pro dané schéma na koncovém systému může administrátor nastavit pravidlo, kterým se bude hodnota atributu v CzechIdM transformovat na hodnotu atributu na koncovém systému. Podobně může nastavit pravidlo, kterým se bude hodnota na koncovém systému transformovat na hodnotu v CzechIdM. Obě pravidla mají stejné vstupní parametry a obě musí navracet transformovanou hodnotu. Název parametru Typ parametru Popis Constants. TRANSFORM_RULE_ ATTR_RESOURCE String Název koncového systému Constants. TRANSFORM_RULE_ ATTR_NAME String Název atributu v CzechIdM Constants. TRANSFORM_RULE_ ATTR_VALUE Object Hodnota atributu, která má být transformována Tabulka 6.1. Parametry, které má k dispozici transformační pravidlo 40 Pravidla pro výpočet nové hodnoty atributu 6.5. Pravidla pro výpočet nové hodnoty atributu Během aktualizace atributů schématu, které vychází z některé přiřazené role, je zapotřebí stanovit novou hodnotu atributu. Existuje několik možností, jednou z nich je výpočet nové hodnoty na základě staré hodnoty pomocí pravidla, které vytvoří sám administrátor. Pravidlo musí vracet novou hodnotu atributu. Název parametru Typ parametru Popis UserView. RULE_VARIABLE_ VIEW_ATTRIBUTE UserView Pohled na uživatele, s jehož účtem je pracováno UserView. RULE_VARIABLE_ ATTR_NAME String Název atributu UserView. RULE_VARIABLE_ ACCOUNT_ATTRS DTO Atributy účtu, který je aktualizován UserView. RULE_VARIABLE_ ATTR_OLD_VALUE Object Předchozí hodnota atributu Tabulka 6.2. Parametry, které má k dispozici pravidlo pro výpočet nové hodnoty atributu 6.6. Korelační pravidla Během rekonciliace je zapotřebí párovat účty. K tomu slouží korelační pravidla, která může administrátor stanovit v administrátorském rozhraní. Pravidlo musí vracet jméno identity, s níž se má účet spárovat. Název parametru Typ parametru Popis Generic Synchronization. WFC_SYNC_TYPE Generic Synchronization. SyncType Typ obecné synchronizace, tedy SyncType.SYNCHRONIZATION nebo SyncType.RECONCILIATION Generic Synchronization. WFC_ACCOUNT_UID String Jedinečný identifikátor účtu Generic Synchronization. WFC_RESOURCE_NAME String Název systému, na němž se nachází účet Generic Synchronization. WFC_RESOURCE_ ATTRIBUTES DTO Atributy schématu, na němž se účet nachází Generic Synchronization. WFC_SCHEMA_NAME String Název schématu, na němž se nachází účet Tabulka 6.3. Parametry, které má k dispozici korelační pravidlo 41 Kapitola 6. Pravidla 6.7. Pravidla pro blokování účtů V administrátorském rozhraní je možné u jednotlivých schémat stanovit, jakým způsobem mají být případně blokovány účty. Administrátor má na výběr ze tří možností, jedna z nich je "BY_RULE". Blokování účtů pravidlem je nejobecnější varianta, která přenechává veškerou zodpovědnost na administrátorovi. CzechIdM neočekává žádnou návratovou hodnotu. Název parametru Typ parametru Popis operationType OperationType Pro blokování je tato hodnota konstantně vždy OperationType.DISABLE resourceName String Název systému, na němž se nachází účet schemaName String Název schématu, na němž se nachází účet schemaAttributes DTO Atributy schématu, na němž je účet mazán accountUid String Jedinečný identifikátor účtu, který má být zablokován identityName String Jméno identity, která je vlastníkem účtu Tabulka 6.4. Parametry, které má k dispozici pravidlo pro blokování účtů 6.8. Pravidla before provisioning a after provisioning Administrátor může ve svém rozhraní nastavit pravidla, která se mají spustit před začátkem provisioning účtů a po jeho skončení. Zvolení konkrétního pravidla může snadno provést přes "Systémy" - zvolení konkrétního systému - "Pokračovat" - "Editovat" u konkrétního schématu. Pravidly before a after provisioning může administrátor specifikovat libovolné akce, které mají být provedeny před nebo po skončení provisioning. Může dokonce provisioning konkrétního účtu zcela zablokovat, pokud pravidlo before provisioning vrátí hodnotu null. Název parametru Typ parametru Popis operationType OperationType Udává typ operace, tedy OperationType. DISABLE (případně také ADD, APPROVE, CHANGE, DENY, DISABLE,. ENABLE, REMOVE, RENAME, SET, UNLINK, WANT_ADD) resourceName String Název systému, na němž se nachází účet, u něhož bude prováděno provisioning schemaName String Název schématu, na němž se nachází účet, u něhož bude prováděno provisioning 42 Pravidla before provisioning a after provisioning Název parametru Typ parametru Popis schemaAttributes DTO Atributy schématu, na němž se nachází účet, u něhož bude prováděno provisioning accountUid String Jedinečný identifikátor účtu, u něhož bude prováděno provisioning identityName String Jméno identity, která je vlastníkem účtu, u něhož bude prováděno provisioning. Tabulka 6.5. Parametry, které mají k dispozici pravidla before a after provisioning 43 44 Logování 7.1. Úvod do logování v CzechIdM Veškeré informace se prvotně zaznamenávají do příslušných tabulek v repository. Chybový výstup může být také směrován přes syslog do libovolného jiného úložiště. Například do jiné databáze nebo do souboru. Ale i v takovém případě dojde nejdříve k zápisu do repository idm a až po té k předání informací syslogu. V repository tedy budou vždy všechny informace. Je to především pro potřeby reportů. Ty jsou vytvářeny vždy z dat vedených v repository. 7.2. Co všechno je logováno? V identity manageru se zaznamenává několik druhů informací, které při provozu vznikají. Ty jsou zapisovány do různých tabulek. Následující tabulka shrnuje o které informace se jedná a kam jsou v rámci repository zapisovány. Typ záznamu Tabulka Chyba, warování, ladící informace, ... log Emaily (odeslané i neodeslané kvůli chybě) email_log Auditní informace tabulky s prefixem "audit_log" Uživatelské požadavky user_requests Tabulka 7.1. Logované informace a kde jsou uloženy Detailně jsou jednotlivé typy záznamů popsány v dalším textu. 7.3. Auditní informace Auditními informacemi se myslí takové, které mohou někdy později sloužit k přezkoumávání událostí, co se v identity manageru staly. Například informace o tom, kdo, co a kdy změnil, vytvořil, odstraníl a podobně. Záznamy tohoto typu jsou umísťovány do více tabulek v repository, které mají prefix "audit_log". Například informace o všem, co se stalo s nějakou identitou nebo jejím účtem, je ukládáno do "audit_log_for_identity". 7.4. Uživatelské požadavky Uživatelské požadavky jsou veškeré "akce", k nimž dá pokyn uživatel skrze nějaké rozhrání identity manageru. Například se může jednat o požadavek na zobrazení seznamu všech uživatelů, který uživatel vyšle kliknutím na patřičný odkaz. Kromě toho, o jaký požadavek se jedná, jsou také zaznamenány informace o uživateli samotném. Atribut Popis id Jednoznačný identifikátor. Jak je generován, je uvedeno níže v textu. turnNumber Číslo, které udává, kolikátý je tohle požadavek daného uživatele v rámci sezení (od přihlášení). userName Uživatelské jméno přihlášeného uživatele. 45 Kapitola 7. Logování Atribut Popis begin Datum a čas, kdy byl požadavek vytvořen (podán). duration Počet milisekund udávající, jak dlouho trvalo zpracování požadavku. ipAddress IP adresa, ze které uživatel daný požadavek poslal. interfaceType Typ rozhraní, ze kterého požadavek pochází. Možnosti jsou: WEB, WS, TEST_NO_GUI servletPath Cesta k servletu. queryString Atributy zadávané v adresní řádce prohlížeče. Tabulka 7.2. Sledované atributy uživatelského požadavku 7.5. Identifikátor požadavku Identifikátor je generován ihned při vytvoření požadavku. Mezi touto akcí a uložením požadavku do databáze je vykonáno velké množství operací. Některé z těchto operací jsou okamžitě zalogovány. Aby se vědělo, na základě jakého požadavku byly operace vykonávány, je ke každému takovému záznamu přidáván právě identifikátor aktuálně zpracovávaného požadavku. To je takového, který byl vytvořen, ale ještě nebyl uložen. Ve skutečnosti požadavek nemusí být zapsán úplně pokaždé do databáze. Pokud totiž v rámci jeho zpracovávání není do repository zapsán žádný záznam, který by se na něho odkazoval, není uložen ani tento požadavek. Pokud bude potřeba, je tuto funkčnost možno změnit v "LoggingUtils.endUserRequest". Z výše uvedeného je zřejmé, že generování identifikátoru nebude moci mít na starosti databáze nýbrž aplikace. Jako (s velkou pravděpodobností) jedinečný identifikátor se používá UUID (Universally Unique Identifier) verze 4 rozšířený o identifikátor aktuálního vlákna a počet milisekund od 1.1.1970. Takovéto identifikátory jsou používáné i na dalších místech aplikace a to vždy prostřednictvím třídy "eu.bcvsolutions.idm.data.UuidBasedId". 7.6. Začátek a konec uživatelského požadavku Pro označení začátku a konce (a provedení potřebných akcí) jsou ve třídě "eu.bcvsolutions.idm.data.logging.LoggingUtils" určeny statické metody "beginUserRequest" a již zmíněná "endUserRequest". To kdy jsou tyto metody volány ovšem záleží na rozhraní, ze kterého požadavek pochází. Nejčastěji používaným je webové rozhraní. To je vytvořeno pomocí JSF frameworku a tudíž může být využito tzv. phase listener, který dokáže reagovat na jednotlivé fáze zpracovávání požadavku. K tomuto účelu slouží třída "eu.bcvsolutions.idm.app.page.PagePhaseObserver" a speciálně její metoda "beforePhaseAction". Její deklarace je: @Observer("org.jboss.seam.beforePhase") public String beforePhaseAction() V těle metody se zjistí, ve které fázi se nacházíme. Pokud půjde o fázi "RESTORE_VIEW", tak zavoláme "LoggingUtils.beginUserRequest". Naopak ve fázi "RENDER_RESPONSE" budeme volat metodu "endUserRequest". 46 Zápis logu 7.7. Zápis logu IdmLogger decorator je implementací Log rozhraní. Primárně se snaží zapisovat do databáze (prostřednictvím DBLoggeru). Zkrácené informace také zapíše do logu, který dekoruje. V případě neúspěšného zápisu jde do takového logu kompletní informace. Zápis do DB logu běží v samostatné transakci. Důvod je ten, že pokud už jednou záznam pošlu do syslogu, nejde tento také rollbackovat. Lepší by ale asi bylo, kdyby se error logy zapisovaly v samostatné transakci, kdežto auditní logy v transakci stejné v jaké beží logovaná operace. Tento rys může být předmětem dalšího vývoje. 7.8. Jak logovat z kódu Kromě logování, které je prováděno přímo z kódu CzechIdM, může být výhodné logovat některé informace ze spouštěných workflow, neboť i ta mohou prostřednictvím třídy Data manipulovat s datovou vrstvou. Druhým důvodem pro logování z workflow může být vypisování stavů workflow pro účely ladění během vývoje. K logování je možné použít statické metody Application.logInfo, Application.logWarn, Application.logInfo, Application.logError, Application.logDebug a Application.logFatal. Pro přístup k auditnímu logu slouží na třídě Application metoda getIoLog. Application.logInfo("Tento retezec bude zalogovan s dulezitosti {0}", new String[] {"info"}); AuditOperationsLogger logger = Application.getIoLog(); //zalogovani do auditniho logu - uzivateli "admin" byl odebran atribut logger.logIdentity(OperationTarget.ATTRIBUTE, OperationType.REMOVE, "admin"); 47 48 Schvalování 8.1. Schvalovací proces Schvalovací proces v CzechIdM je elektronická obdoba procesu běžného třeba na úřadech. Pokud něco chcete, musíte o to požádat. Vaše žádost je poté doručena k vyřízení člověku, který má schvalování na starosti. Ten rozhodne, zda vaší žádost schválí, nebo zamítne. V případě první možnosti je vám následně předáno to, o co jste žádali. Na úřadech (a v mnohých firmách) jsou tyto žádosti v papírové podobě, a tudíž se může snadno stát, že je doručena ke špatnému člověku nebo úplně ztracena. Je-li schvalovací proces řešen emailovou komunikací, hrozí, že ji člověk v záplavě jiných emailů ztratí. Navíc je i tato forma přenosu informací relativně nebezpečná (kdokoliv ji může přečíst). Pokud k výše zmíněnému přičteme i to, že akce a rozhodnutí kolem žádostí nejsou nikde zaznamenávány, je jasné, že tyto způsoby nejsou zdaleka ideální. Lepší způsob nabízí CzechIdM. Výhody jsou především: • Rychlost - vaše žádost je okamžitě doručena schvalovateli. Pokud ten na ni nebude reagovat po definovanou dobu, je možné žádost přeposlat na někoho jiného. • Bezpečnost - informace v žádosti vidí pouze schvalovatel. Emailem jde pouze informace o tom, že žádost byla podána a odkaz na ni. Před jejím zobrazením je ovšem vyžadována autentizace. • Dohledatelnost - podání žádosti, rozhodnutí schvalovatele i to, zda vám bylo "dáno" to, o co jste si žádali, je zaznamenáváno. Je tedy například možné kdykoliv zjistit, kdo, kdy a jak rozhodl ohledně schválení žádosti. 8.1.1. Pohled uživatele Uživatelem je v tomto případě myšlen jak žadatel, tak schvalovatel. Žadatel si po přihlášení do uživatelského rozhraní může například požádat o roli, která ho bude opravňovat k přístupu do nějakého systému. Třeba do systému účetnictví. Jelikož tento systém obsahuje informace, které nemůže vidět kdokoliv, podléhá vytvoření účtu v systému schválení hlavní účetní. Té tedy přijde email, že má schválit přístup. Proto se přihlásí do CzechIdM a zobrazí si detail žádosti. Po té, co klikne na tlačítko schválit, je žadateli okamžitě vytvořen účet. 8.1.2. Pohled administrátora Administátor má na rozdíl od obyčejného uživatele přístup do administračního rozhraní. Díky tomu je schopen provádět mnohem více operací. K těm, které se týkají schvalovacího procesu, patří: • Vytvoření role, jejíž vlastnictví opravňuje ke schvalování. • Přiřazení této admin role nějakému uživateli. Tím se z uživatele stává schvalovatel (který ale ještě nemá nic na schvalování). • Konfiguraci role tak, aby její přiřazení vyžadovalo schválení. U role se definují schvalovatelé. Od té chvíle jim budou chodit ke schválení žádosti o konfigurovanou roli. Nyní si tyto operace popíšeme podrobněji. V CzechIdM není žádná výchozí role, která by definovala schvalovatele. Jeji vytvoření je ale velmi jednoduché. Na záložce "Role" klikneme na tlačítko "Nová admin role". Vyplníme libovolné jméno a na záložce "Oprávnění" vybereme "ROLE" a zaškrtneme "APPROVE". Dále přidáme oprávnění "USER_TASK","APPROVE" a "USER","APPROVE", potřebná 49 Kapitola 8. Schvalování při schvalování. Roli můžeme samozřejmě přidat i další oprávnění, definovat její podrole nebo systémy, na kterých přiřazuje účet. Kliknutím na tlačítko "Uložit" roli vytvoříme. Nyní můžeme vytvořenou roli přiřadit uživateli, ze kterého chceme udělat schvalovatele. V menu na záložce "Uživatelé" vyhledáme tohoto uživatele a klikneme na odkaz editovat. Zobrazí se editační formulář. Přejdeme na záložku "Role a kontrolované organizace" a přidáme námi vytvořenou roli. Také vybereme organizaci, pro kterou budou všechny admin role uživatele platit. Po klepnutí na tlačítko "Uložit" dojde k vytvoření schvalovatele. Zbývá u nějaké role určit schvalovatele. Opět tedy přejdeme na záložku "Role" a vybereme roli, jejíž přiřazení komukoliv budeme chtít nejdříve potvrdit schvalovatelem. Uděláme tak na záložce "Schvalovatelé" v detailu role. Pro vyhledání schvalovatele můžeme použít filtr. Pokud chcete zobrazit všechny, tak nechte vyhledávací pole prázdná a klepněte na tlačítko "Vyhledat". Zobrazí se všichni uživatelé, kteří mají roli s oprávněním schvalovat. Některé z nich vybereme a roli uložíme. Tím je nastavení schvalovatele hotovo. Důležití je ještě to, že pokud definujeme více schvalovatelů u jedné role, tak každému z nich dojde, při pokusu o přiřazení role, požadavek na schválení. Kdo první schválí, ten vyhrává. Ostatním pak takovíto požadavek zmizí. 8.1.3. Pohled vývojáře Schvalovací procesy jsou z části implementovány v Java kódu a z části pomocí workflow. Schvalovací formuláře samozřejmě též využívají i JSF. V následujícím textu popíšeme, jak jsou jednotlivé části provázány a jak spolu komunikují. I když se schvalování může týkat i jiných událostí, my si to, jak je schvalování implementováno, ukážeme na příkladu schválení přiřazení role. Celý proces začíná voláním "Data.checkinView" na "UserView", do jehož uzlu "roles" byla přidána role vyžadující schválení. Takovéto view se dostane přes třídy "Data" a "UserManagementBean" až do objektu třídy "UserViewHandler". Zde se zjistí, že byla přidána role a že má tato role určené schvalovatele. K těmto operacím dochází v metodě "processViewRolesAttribute" a z ní volané "startRoleApproveWfsIfNeeded". Druhá z uvedených metod pak také spouští workflow "user.role.approve", které notifikuje schvalovatele a vytváří schvalovací požadavek. Toto workflow tedy obsahuje uzel typu "task-node". V okamžiku, kdy se běh programu dostane do tohoto uzlu, je vytvořen "task" (schvalovací požadavek, úkol) s následujícími proměnnými (žádná z nich není povinná, ale doporučujeme vyplňovat alespoň "approvers", "approvalInfo", a "pageId" nebo "workflowName"): • approvers - seznam schvalovatelů • roleName - název role, která se schvaluje • userView - view uživatele, kterému má být role přidána. • pageId - id stánky, která se má použít pro zobrazení tohoto úkolu. V případě schvalování rolí to je "include/roleApprove". • workflowName - název workflow, která bude mít na starosti zobrazení uživatelského požadavku. Pokud je nastavena tato proměnná (a není null), tak zastíní i případně nastavenou proměnnou "pageId". Použití nachází v případě složitějšího zpracovávání požadavku, kdy nevystačíme s pouhým zobrazením stránky následovaným rozhodnutím o schválení. • approvalInfo - informace o schvalování, které se použijí pro záznam do audit logu. K tomu dochází po ukončení požadavku ve workflow "userTask.detail". Je možné doplnit položky "targetIdentityName", "newValue", "oldValue", "operationSubject", "detail". 50 Pohled vývojáře Proměnné jsou poté dostupné ve view daného tasku. Tím, že je vytvořen task, dojde k přerušení workfow. Běh programu se vrací do java kódu, kde dochází k uložení stavu workflow do databáze. Dále program pokračuje kódem v "UserViewHandleru", což může znamenat další spuštění workflow pro jinou přidanou roli. Workflow zůstává přerušeno do doby ukončení schvalovacího požadavku (schválení, nebo zamítnutí). Schvalovateli byl ve workflow "user.role.approve" poslán email o tom, že na jeho schválení čeká požadavek o přiřazení role. Přihlásí se do uživatelského rozhraní IdM a přejde na stránku s úkoly. Tím se spouští workflow "ui.userTask.list", které vytáhne z repository všechny neukončené úkoly přihlášeného uživatele. V administračním rozhraní je, pro získání všech úkolů, používáno workflow "userTask.list". To zatím dělá téměř to stejné, co workflow z uživatelského rozhraní. Dá se ale předpokládat, že časem bude jeho funkcionalita rozšířena tak, aby si administrátor s dostatečnými právy mohl vylistovat úkoly i jiných uživatelů. Pak již se tato dvě workflow budou velmi lišit. Nyní je rozdíl pouze ve formulářích, které zobrazují. Jedná se buď o "user/userTask/userTasks" pro uživatelské rozhraní nebo "admin/userTask/userTasks" pro administrační rozhraní. Formuláře jsou opět velmi podobné, ale i u nich to v budoucnu nebude platit. Po kliknutí na název úkolu, je spuštěno workflow "userTask.detail". To si nejdříve obstará kompletní view úkolu s daným id. Poté spustí akci "eu.bcvsolutions.idm.app.workflow.ShowUserTaskPageAction", která z předaného pohledu zjistí, kterou stránku má použít pro zobrazení úkolu. Zjišťuje to na základě proměnné "pageId". Pokud není definována, je použita výchozí stránka "/adminOrUser/userTask/detail.xhtml", která nedělá nic jiného, než že zobrazí všechny proměnné zobrazovaného úkolu v tablce. Pokud je proměnná "pageId" definována, tak je naopak zobrazena stránka, jejiž id je hodnotou proměnné. Ve skutečnosti je ale stránka nejdříve vložena do "/adminOrUser/userTask/customDetail.xhtml" a ta je pak zobrazena. Workflow "userTask.detail" čeká během zobrazování stránky v uzlu "showPage". Z něho vedou následující přechody, jimž mohou odpovídat tlačítka na formuláři: • finishAndSave - ukončí úkol, aniž by došlo ke schválení nebo zamítnutí. • save - uloží změny v úkolu. Úkol ale tímto není ukončen a stále bude zobrazován v seznamu aktivních úkolů. • reset - znovu načte view úkolu. • close - neuloží změny v úkolu, ani ho neukončí. • approve - nastaví proměnnou "___IS_TASK_APPROVED___" na hodnotu "true" a poté ukončí a uloží úkol. • deny - chová se stejně jako přechod "approve" s tím rozdílem, že nastaví hodnotu proměnné na "false". Proměnnou "___IS_TASK_APPROVED___" kontroluje UserTaskManagementBean, který je volán z workflow prostřednictvím "Data.checkinView". Pokud je nastavena, tak podle její hodnoty použije přechod "approved", nebo "denied" z "task-node". Jedná se o uzel ve workflow, které je asociované s právě ukončeným taskem. Tím se běh programu dostává zpět do workflow, které úkol vytvořilo a bylo tedy až do tohoto okamžiku pozastaveno. Pokud je místo "pageId" použito "workflowName", tak v uzlu "userTask.detail" dojde ke spuštění v proměnné uvedeného workflow. Tomu je do kontextu přidána proměnna "taskView" s pohledem na aktuálně zpracovávaný požadavek. Očekává se, že workflow před ukončením nastaví proměnnou "stateResult". Její hodnotou by mělo být taskView. Pokud bude v TaskView atribut nextAction, tak se 51 Kapitola 8. Schvalování jeho hodnota použije pro navigaci do dalšího uzlu workflow "userTask.detail". Hodnota tedy odpovída názvu přechodu, které jsou uvedeny výše v seznamu. 8.1.4. Příklad vytvoření schvalovací úlohy ve workflow Následující kód vytvoří schvalovací požadavek a pozastaví workflow, ve kterém se nachází. Jakmile dojde ke schválení, nebo zamítnutí, běh workflow bude pokračovat buď přechodem do uzlu "addRole", nebo do uzlu "logDisapproval". <task-node name="createUserTask"> <task name="SchvaleniPridaniRole"> <assignment pooled-actors="#{approvers}"></assignment> <controller> <variable name="approvers" access="read"/> <variable name="roleName" access="read" /> <variable name="userName" access="read" /> </controller> <description></description> </task> <transition name="approved" to="addRole"/> <transition name="denied" to="logDisapproval"/> </task-node> 8.2. Delegace schvalovacích procesů Delegace schvalovacích procesů rozšiřuje možnosti schvalovacích procesů popsaných v předchozí kapitole. Tato funkcionalita dává uživatelům možnost dynamicky si volit zástupce na které budou delegovány příchozí žádosti o schválení. Příkladem může být šéf delegující své žádosti na sekretářku (která by v běžné situaci neměla příslušné pravomoce), nebo dočasná delegace mezi kolegy v době dovolené. 8.2.1. Pohled uživatele Možnosti delegací jsou přístupné pouze uživatelům s určitými právy. To proto že uživatel si vybírá své delegáty ze seznamu ostatních uživatelů, a musí mít proto právo si příslušný seznam prohlédnout. V případě že tato práva nemá nezobrazí se mu ani možnost přejít ve webovém rozhraní na formulář delegací. V opačném případě, rozhodne-li se delegace využít, má delegátor možnost specifikovat, na koho chce své žádosti delegovat, a určit od kdy do kdy bude delegace aktivní (v případě že časový interval nespecifikuje je delegace uvažována na neurčito). Po potvrzení své volby je následně všem potenciálním delegátům vytvořena žádost o schválení delegací. Po jejím potvrzení se stává delegace aktivní a všechny žádosti přicházející na delegátora jsou poslány jeho delegátům. Delegát pak má právo potvrdit schválit nebo odmítnout žádost stejně jako původní schvalovatel (delegátor). V případě že žádost má definováno více schvalovatelů probíhá schvalování stejně jako v případě nedelegovaných žádostí,s tím rozdílem že delegátor je v seznamu schvalovatelů naahrazen seznamem všech svých aktivních delegátů. Žádost pak zmizí poté co jí vyřídí jeden ze schvalovatelů. 8.2.2. Pohled vývojáře Formulář webového rozhraní je ovládán pomocí workflow "userTask.delegation". V případě že si uživatel přidá nového delegáta je spuštěno workflow "userTask.delegation.approve". To pošle 52 Pohled vývojáře potenciálnímu delegátovi žádost o schválení delegací. V okamžiku kdy je žádost schválena je přidán do extendet atributu delegates původního žadatele. Samotné delegace jsou pak aplikovány pomocí jednoduchého pravidla "getDelegates". Vstupem tohoto pravidla je LinkedList schvalovatelů (ve stejné podobě v jaké je používán při tvorbě schvalovacího tasku) výstupem je nový list schvalovatelů ve kterém jsou všichni uživatelé s aktivními delegáty nahrazeni seznamem těchto delegátů (a to rekurzivně, tj. delegáti jsou také testováni na přítomnost vlastních delegátů). Pokud by v systému byla vytvořena delegační smyčka (A deleguje na B, B na C a C zpět na A) bude výsledným příjemcem žádosti její původní adresát. 53 54 Plánování úloh 9.1. Scheduler a task executer Workflow a další procesy (neboli úlohy) je možné spouštět asynchronně. K tomu slouží třída TaskControlerBean, která zařadí úlohu do fronty Message Driven Beany TaskExecutorBean. Ta už pak (v jiném vlákně) provádí samotné vykonávání úlohy. Počet současně vykonávaných úloh je závislí na velikosti poolu pro tuto beanu. To se dá nastavit v konfiguraci aplikačního serveru. Úlohy jsou reprezentovány třídami, které implementují rozhraní ApplicationTask. Jsou to například RunWorkflowTask, SynchronisationTask nebo TimerTask. Každá z těchto implementací specifikuje, jak konkretně se má úloha vykonat. Pro příklad uvedeme, jak spustit workflow asynchronně. Jako při většině jiných operací použíjeme jednu z fasád. Pro tento účel je určena třída Application a její statická metoda startWorkflowAsynchronously. Úlohy je možné naplánovat na spustění v konkrétní čas a také nastavit periodu jejich spouštění. Toto se dělá nastavením odpovídajících atributu v SchedulerTaskView a následně jejich uložením prostřednictvím Data.checkinView. Během metody checkinView dojde i k nastavení systémového časovače. Třída SchedulerTaskManagementBean odpovídající za uložení pohledu totiž po jeho uložení volá Scheduler.updateTimerServiceAccordingToTaskAndSetTimerHandle. Tato metoda zařídí vytvoření případně zrušení časovače. Do volitelných informacích časovače uloží id naplánované úlohy a naopak do atributu úlohy uloží timer handle. Vzniká tak obousměrná vazba mezi úlohou a časovačem. 55 56 Koncové systémy 10.1. Definice koncového systému 10.1.1. Pravidla spouštěná před a po operaci na systému U každého schématu lze definovat pravidlo, které se má spustit před nebo po provedení každé operace na systému. Z pravidla je samozřejmě možné spustit nějaké workflow, které například vytvoří schvalovací úlohu. Pravidlu jsou předány následující argumenty: Název Popis operationType O jakou operaci se jedná. Jde o výčtový typ OperationType a možnými hodnotami jsou zatím: ADD, CHANGE, REMOVE, RENAME resourceName Název koncového systému. schemaName Název schématu. schemaAttributes Atributy, které se budou posílat (měnit, nastavovat) nebo které byly poslány do koncového systému. accountUid Identifikátor účtu identityName Jméno IdM uživatele, kterému patří učet, na němž se operace provádí. Tabulka 10.1. Argumenty předávané pravidlům Pravidlo, spouštěné před operací musí vrátit atributy, které se pošlou koncovému systému. Pokud tedy nechce udělat žádnou změnu, tak pouze vrátí ty samé atributy, co dostane v parametru "schemaAttributes". Pokud je vráceno null, tak je operace přerušena. 10.1.2. Nastavení způsobu blokování (a odblokování) účtu Jsou podporovýny dva způsoby blokace účtů. První je změnou hesla na neznámou hodnotu (splňující politiku hesel na systému). Druhou je pak volání pravidla, které nastaví patřičný atribut. Změna hesla je universální - funguje na všech systémech. Nevýhodou ale je, že uživatel musí mít i zablokovaný přístup do IdM. V opačném případě by si totiž heslo mohl změnit. . Vzhledem k tomu, že si IdM neukládá žádná hesla, není možné po odblokování účtu nastavit původní heslo. Uživatel si tak musí heslo změnit z uživatelského rozhraní IdM. Pokud je vybrán druhý způsob blokace účtů, tak IdM pouze zavolá pravidlo, které musí samo nastavit atributy na správné hodnoty tak, aby došlo k blokaci nebo aktivaci účtu. 10.2. Přilinkování uživatelského účtu „Ruční“ přilinkování uživatelského účtu probíhá tak, že IdM nejprve vyhledá všechny účty na koncovém systému. Uživatel poté vybere účet, který chce přiřadit některé z identit v IdM. IdM vyzve uživatele k výběru identity, které chce účet přiřadit. Tato identita musí mít přiřazenou roli, která ji 57 Kapitola 10. Koncové systémy opravňuje k tomu mít účet na daném koncovém systému. Pokud identita tuto roli nemá, vyzve IdM uživatele k tomu, aby některou roli identitě přiřadil. Je možné přilinkovat pouze takový účet na koncovém systému, který má v tabulce účtů (v přehledu, který se zobrazí) vyplněné schéma. Jeden uživatel může mít na koncovém systému pouze jeden účet pro dané schéma. Zobrazení přehledu uživatelských účtů na koncovém systému zajišťuje workflow „resource.accounts.view“. Filtrování podle schemat je zajištěno přímo v průběhu komunikace s koncovým systémem. Filtrování podle začátku jména a podle stavu účtu se provádí přímo v tomto workflow. Tzn., že jsou nejprve načteny všechny účty (případně je proveden filtr na schéma) a poté se ve workflow vyfiltrují účty, které nesplňují vyhledávací podmínky. Přilinkování uživatelského účtu zajišťuje workflow „resource.accounts.link“. Workflow požádá uživatele o vybrání identity a poté zašle požadavek na přilinkování. V případě, že při této akci dojde k chybě, která je způsobená tím, že uživatel nemá přiřazenou potřebnou roli, načte workflow role, které toto oprávnění poskytují a požádá uživatele o vybrání role. Při opětovném pokusu o přilinkování dojde k tzv. checkoutView, nastavení nové role, přidání účtu do seznamu účtů a k tzv. checkinView. Potřebná oprávnění: • RESOURCE (READ, LINK_ACCOUNT) • USER (READ, EDIT, EDIT_ACCOUNT) • ROLE (READ) 10.3. Vypnutí reálných modifikací systému V konfiguraci CzechIdM je možné nastavit, aby se změny na systémech ve skutečnosti neprováděly a namísto toho byly změny pouze zapisovány do souboru. Toho lze docílit nastavením níže uvedených parametrů v konfiguračním souboru (viz kapitolu Konfigurace). • create_real_account_allowed=true • update_real_account_allowed=true • fake_resources_dir=META-INF/fakeResources/ 10.4. Aktualizace hodnot atributů účtů na koncových systémech Při každém zpracování pohledu na identitu nebo roli v metodě checkinView na třídě Data jsou aktualizovány hodnoty atributů na účtech. V zásadě je možné rozdělit aktualizování hodnot atributů na účtech do dvou kategorií: aktualizaci z hlediska role a aktualizaci z hlediska identity. K aktualizaci z hlediska role dochází při přidělení nové role, odebrání role nebo pozměnění role. Aktualizace z hlediska identity je spouštěna při zpracování pohledu na identitu a může upravit atributy účtu, které jsou přímo mapovány na některý atribut identity. Aktualizace z hlediska rolí je naproti tomu spouštěna, pokud došlo k nějaké změně v přidělení rolí, tedy přiřazení nové role, odebrání role. Propagaci hodnot atributů z CzechIdM na koncový systém zajišťuje třída UserViewAttributesPropagator a její metody actualizeAccountsFromRoles a actualizeAccountsFromIdentity. 58 Aktualizace z hlediska identity 10.4.1. Aktualizace z hlediska identity Tento druh aktualizace je zajišťován metodou actualizeAccountsFromIdentity na třídě UserViewAttributesPropagator. K aktualizaci dochází z workflow "user.before.checkin", které je spouštěno z metody beforeSaveView při každém zpracování pohledu metodou checkinView na třídě Data. Všechny mapované atributy jsou podle pohledu přepsány novou hodnotou. 10.4.2. Aktualizace z hlediska rolí Tento druh aktualizace je zajišťován metodou actualizeAccountsFromRoles na třídě UserViewAttributesPropagator. K aktualizaci dochází z metody processViewChanges na třídě UserViewHandler. Role přiřazená uživateli může znamenat vytvoření účtu na některém z koncových systémů. Některé atributy na systému jsou poté propagovány na koncový systém právě na základě přiřazené role. Může se ovšem stát, že se k jednomu koncovému systému vztahuje více rolí, které mají některé atributy společné. V takovém případě musí CzechIdM řešit konflikt: jedna role může doplňovat atribut například konstantou A, zatímco druhá konstantou B nebo pravidlem C. Která hodnota se má v takovém případě použít? V této sekci je popsáno, jak je tento problém v CzechIdM řešen. U každé identity v CzechIdM je veden seznam přiřazených rolí a jsou sledovány jeho změny. V okamžiku, kdy dojde ke změně v seznamu, postupuje se podle následujícího algoritmu: 1. Pro všechny nově odebrané role: • Označ všechny její atributy jako modifikované a vynuluj je. 2. Označ všechny účty na schématech, k nimž se nevztahuje žádná ze stávajících rolí, jako "ke smazání". 3. Pro všechny role, které zůstaly přiřazeny: • Pokud je role mezi nově přiřazenými, nastav příznak isNewAssigned na true. • Pro všechna schémata dané role odstraň příznak ke smazání a projdi všechny atributy daného schématu: • Pokud atribut nemá být propagovaný, pokračuj dalším atributem. • Pokud má být atribut propagován z identity a takový na identitě existuje, aktualizuj ho z identity. • Pokud má být atribut propagován z role: • Pokud atribut na základě zvolené přepisovací strategie nemá být přepsán, pokračuj dalším atributem. Rozhodnutí proběhne na základě zvolené přepisovací strategie takto: • Strategie WRITE_IF_NOT_EXISTS: atribut bude zapsán, pokud pro daný účet atribut zatím vůbec neexistuje. • Strategie OVERWRITE_IF_MODIFIED: atribut bude přepsán, pokud je označen jako modifikovaný, tedy například v případě, kdy byla odebrána jiná role pracující s tímto atributem • Strategie OVERWRITE_FIRST_TIME: atribut bude přepsán, pokud je role nově přiřazena 59 Kapitola 10. Koncové systémy • Strategie OVERWRITE_ALWAYS: atribut bude přepsán • Nyní je na základě role a staré hodnoty atributu vypočítána nová hodnota pro daný atribut. Jsou dvě možnosti, jak může být tato nová hodnota vypočítána: a. Pravidlem (pokud bylo nějaké příslušné nalezeno) ... nová hodnota je vypočítána na základě parametrů, které jsou pravidlu zadány: • parametr userView ... odkaz na view identity, pro niž je výpočet prováděn. Typu UserView • parametr attributeName ... název atributu. Typu String • parametr accountAttributes ... atributy účtu identity na tomto schématu. Typu DTO • parametr oldValue ... hodnota atributu před spuštěním výpočtu. Typu Object Pravidlem lze stanovit novou hodnotu v závislosti na hodnotě původní. Například může být nová hodnota se starou kombinována, anebo mohou být ošetřeny situace, kdy se původní hodnota přepisovat nemá (například pokud stará hodnota není null). b. Konstantou (pokud nebylo nalezeno žádné příslušné pravidlo) ... stará hodnota je vždy přepsána stanovenou konstantou. Pokud má být nová hodnota kombinována s původní hodnotou nebo na ní jakkoli záviset (například se má přepisovat pouze tehdy, pokud je stará hodnota null), je třeba použít pravidlo, ve kterém bude kombinování řešeno. • Stará hodnota atributu je přepsána novou hodnotou. 4. Schémata označená ke smazání jsou smazána. Příklad: Představme si systém, jehož atributy jednak souvisí s rolí "mail" a jednak s rolí "unix". Každá z těchto rolí používá na daném systému svou množinu atributů. Některé z atributů mohou být používány pouze rolí "mail", některé pouze rolí "unix", některé oběma rolemi. Mějme uživatele, kterému není přiřazena ani role "mail", ani role "unix". V okamžiku, kdy je mu přiřazena role "unix", jsou na základě pravidel a konstant pro roli "unix" vypočítány příslušné atributy. V okamžiku, kde je přiřazena také role "mail", prochází se opět všechny atributy ve schématu. Přepočítají se jak atributy související s rolí "mail", tak atributy související s rolí "unix". Řekněme, že obě role používají atribut "a". Atribut se přepočítává postupně na základě všech rolí, s nimiž souvisí. Nejprve se tedy hodnota atributu "a" stanoví konstantou nebo pravidlem role "unix", poté konstantou nebo pravidlem role "mail". Máli výsledná hodnota být kombinací obou hodnot (nikoli jen tou, která odpovídá poslední zpracované roli), musí to být ošetřeno v pravidle u obou rolí. V okamžiku, kdy je uživateli odebrána role "mail", jsou všechny atributy, se kterými role "mail" pracuje, nastaveny na null a tím označeny jako modifikované. Při dalším zpracování role "unix" jsou atributy související s rolí "unix" opět přepočítány, a výsledná hodnota tedy správně odpovídá pouze roli "unix". Kdyby byla role "mail" poslední rolí pro dané schéma, bylo by toto po zpracování všech rolí pro příslušnou identitu odstraněno. 10.4.3. Standardní řešení výpočtu hodnot typu "merge" Častým technickým požadavkem při napojení koncového systému je výpočet hodnoty atributu jako kombinace (slití, "merge") atributů jednotlivých rolí. Typicky se používá v situaci, kdy jsou role na koncovém systému mapovány na role v CzechIdM. Přiřazené role jsou potom jedním atributem koncového systému a tento atribut musí být odvozen ze všech přiřazených rolí v CzechIdM. V našem 60 Standardní řešení výpočtu hodnot typu "merge" příkladu předpokládejme, že je počítaným atributem například atribut "permission" a je počítán z rolí Role1, Role2, ... RoleN, přičemž každá z těchto rolí přispívá do výsledné hodnoty řetězcem "AtrX" (X je číslo role). Výslednou hodnotou pro přiřazené role Role2, Role4 a Role85 by měl být řetězec "Atr1;Atr4;Atr85". Jak takového výsledku docílit? V současnosti nejlepším řešením je zavedení mapovací mezitabulky v business konstantě a jedné výjimečné "přístupové" role. Tato role přiřazuje příslušný koncový systém a zadává na něm atribut "permission" pravidlem "mergePermission" s přepisovací politikou OVERWRITE_ALWAYS. Ostatní role tento atribut nezadávají. V pravidle "mergePermission" je z userView načten seznam všech aktuálně přiřazených rolí. Pro každou přiřazenou roli je v mapovací mezitabulce nalezen řetězec, kterým daná role přispívá do výsledku. Z řetězců je sestavena výsledná hodnota atributu. Výhodou tohoto přístupu je skutečnost, že není nutné pro každou roli z rolí Role1 až RoleN implementovat vlastní pravidlo. Případné obohacení seznamu rolí o další pak znamená jen přidat do mapovací tabulky v business konstantách dvojici "NovaRole - NovyAtribut". 61 62 Identity Connectors 11.1. Definice konektoru Identity Connectors (dále jen konektory) jsou aplikační komponenty, prostřednictvím kterých se spravují uživatelské účty (a nejen ty) na koncových systémech. Tvoří k nim jakousi fasádu, tj. poskytují jednotné rozhraní pro práci s nimi. Koncové systémy jsou v tomto případě všechny aplikace a systémy, které používají vlastní správu uživatelů, např. systémy pro sledování požadavků, HR systémy, mzdové systémy atd. Úkolem konektorů je poskytovat jednotné rozhranní pro připojení se k daným koncovým systémům a propagovat změny na tyto systémy, popř. z těchto systémů. 11.2. Lokální a vzdálený connector server Každý konektor, který má být použit, je deployován do takzvaného connector serveru, což je aplikace, která konektory obsluhuje a zasílá jim data. CzechIdM zpravidla používá lokální connector server, který je součástí CzechIdM a je spouštěn a zastavován spolu s aplikačním serverem. Connector server však může být spouštěn i jako zcela samostatná aplikace odděleně od CzechIdM. Takový connector server nazýváme remote connector server a to, zda má být koncový systém napojen přes lokální nebo vzdálený connector server, je nutné stanovit už v okamžiku definice typu systému. Výhodou použití remote connector serveru může být rozložení zátěže mezi více fyzických strojů nebo Java VM. Některé systémy (například Active Directory) musí být napojeny přes remote connector server, protože konektor, který se pro napojení používá, je napsán v .NET a standardní lokální Java connector server s ním neumí pracovat. Pro úspěšné připojení k remote connector serveru je potřeba zadat host, na kterém server běží, port a heslo. Spuštění Java remote connectoru: java -cp "connector-framework.jar:connector-framework-internal.jar:groovy-all.jar" org.identityconnectors.framework.server.Main -run -properties ConnectorServer.properties To, zda se použije lokální, nebo vzdálený konektor server, je věcí typu resource. Klíčovou třídou, která na základě ResourceType rozhodne, který konektor server použít, je třída ConnectorUtil, konkrétně její metoda getAPIConfiguration. 11.3. Funcionalita konektorů Operace pro navázání spojení • validace konfiguračních údajů konektoru • otestování spojení s koncovým systémem • navázání spojení s koncovým systémem • ukončení spojení s koncovým systémem 63 Kapitola 11. Identity Connectors Provisioning operace • vytvoření nového uživatelského účtu, popř. skupiny, role, organizace apod. • editaci stávajícího uživatelského účtu • vyhledání uživatelského účtu dle zadaného kritéria • vyhledání všech účtů • vyhledání timestampu poslední modifikace sledovaných záznamů • vyhledání záznamů jenž byly modifikovány po zadaném timestampu 11.4. Spouštění externích skriptů na koncových systémech Pomocí konektorů, které tuto možnost podporují (např.: Universal JDBC konektor), je možné volat jednorázové skripty na koncových systémech. To se hodí zejména v situacích, kdy prováděná akce nijak nesouvisí s účtem na koncovém systému. Typickou situací může být správa organizací nebo jednorázové stažení seznamu revokovaných certifikátů. Ke spuštění externí akce slouží na třídě Data metoda runScriptOnResource. Jako vstup dostane název systému, jazyk skriptu, text skriptu a mapu parametrů, které mají být skriptu předány na vstupu. Je důležité si uvědomit, že chování této metody závisí na implementaci konkrétního konektoru. Například Universal JDBC konektor předpokládá, že je-li zadaný jazyk "bsh", je v textu skriptu přímo celé tělo skriptu. Naproti tomu pro "bsh_path" očekává v textu skriptu pouze cestu k souboru se skriptem. 11.5. Struktura tříd Universal JDBC konektoru Na příkladě našeho Universal JDBC konektoru si popíšeme obecnou strukturu Java tříd u konektorů. Základní model je založen na Identity Connector Frameworku a je tedy pro všechny konektory stejný. JDBCConnector • Hlavní třída konektoru, • Zde jsou implementavány všechny provisioning metody (vytvoření účtu, úprava účtu, smazání účtu, získání atributů, vypsání seznamu všech účtů, spuštění synchronizace, navrácení času poslední provedené synchronizace). JDBCConnection • Zajisťuje komunikaci s koncovým systémem, • Implementuje metody pro testování spojení, jeho navázání a následné ukončení. JDBCConfiguration • Intance této třídy obsahují konfigurační údaje konektoru. • Implementuje metodu pro validaci konfiguračních údajů. Výše popsané třídy tvoří jen nutné minimum všech konektorů. Většina z nich má mnohem více tříd, a i stávající tři třídy mají větší rozsah. V další podkapitole se podíváme na jednotlivé třídy blíže. 64 Jak použít Universal JDBC Connector 11.6. Jak použít Universal JDBC Connector Universal JDBC Connector umožňuje napojení libovolné relační databáze pomocí technologie JDBC. Manipulaci s daty v tabulkách obstarávají BeanShell (BSH) skripty, které jsou volány z konektoru a jednotlivým akcím jsou přiřazeny při založení systému v administrátorském rozhraní. Každý skript dostává mezi parametry instanci objektu Connection, jehož prostřednictvím může provádět manipulaci s daty. V této sekci se blíž podíváme na nastavení systému a na jednotlivé skripty, na data, která dostávají skripty na vstupu a na jejich očekávaný výstup. 11.6.1. Konfigurace konektoru Konektor je standardně uložen v balíku eu.bcvsolutions.connector.jdbc-XXX.jar. Název parametru Popis Příklad hodnoty Název systému Výstižný název, pod kterým se systém např. zobrazuje uživatelům. Evidence čtenářů Pouze ke čtení Zaškrtnutím této možnosti je odškrtnuto zajištěno, že na koncovém systému nebude moci Identity Manager nic vytvořit, změnit ani smazat. Authority level Určuje prioritu systému. Hodnota větší než 0 znamená, že půjde o autoritativní zdroj pro IdM; čím vyšší, tím autoritativnější. Poznámka: největší prioritu má IdM repository. 0, nebo 1 pro autoritativní zdroje Password Heslo uživatele, s nímž se IdM přihlašuje k databázi. ****** JDBC Driver Driver pro spojení s databází. • oracle.jdbc.driver.OracleDriver pro Oracle • com.mysql.jdbc.Driver pro MySQL • org.firebirdsql.jdbc.FBDriver pro Firebird Database name Název databáze. readers_repository URL template Template pro vytvoření url pro JDBC konektor - za hodnoty %h, %p a %d se dosadí host, port a název databáze. Umožňuje definovat další vstupní hodnoty, např. kódování. • jdbc:oracle:thin:@%h: %p:%d pro Oracle • jdbc:mysql:// %h:%p/%d? useUnicode=true&characterEncoding pro MySQL • jdbc:firebirdsql:%h/ %p:%d pro Firebird 65 Kapitola 11. Identity Connectors Název parametru Popis Příklad hodnoty Port number Port, na kterém databázový server naslouchá. • 1521 pro Oracle • 3306 pro MySQL • 3051 pro Firebird Username Login uživatele, s nímž se IdM přihlašuje k databázi. root Hostname Adresa databázového serveru localhost Tabulka 11.1. Parametry systému napojeného pomocí Universal JDBC Connector Dále se pro každou akci vyplní název BSH skriptu, který ji zpracovává. Jeden BSH skript přitom může obsluhovat i více akcí. Pokud na koncovém systému nechceme nastavovat synchronizaci, není nutné vytvářet SYNC a GET_LAST_SYNC_TOKEN skripty. SCHEMA skript se momentálně uplatňuje pouze při vytváření schématu pro nový systém, kdy předvyplní atributy schématu. BSH skripty musí být uloženy na stejném serveru, na kterém je deployován konektor. V nastavení parametrů systému je třeba uvést plnou cestu k nim (např. /opt/czechidm/readersScripts/ create.bsh). 11.6.2. Skript CREATE Skript slouží k vytvoření objektu na koncovém systému. Na výstupu vrací identifikátor nově vytvořeného objektu, zpravidla účtu. Činí tak prostřednictvím instance třídy Uid jménem "newUid". 11.6.3. Skript CREATE Název parametru Typ parametru Popis objClass ObjectClass Třída objektu, která je skriptem spravována. Obvykle ObjectClass.ACCOUNT attributes Set<Attribute> Množina atributů, které mají být propagovány na koncový systém options OperationOptions Možnosti operace conn Connection Objekt reprezentující JDBC připojení k databázi uid Uid Současný identifikátor objektu Tabulka 11.2. Parametry na vstupu skriptu CREATE Skript slouží k vytvoření objektu na koncovém systému. Na výstupu vrací identifikátor nově vytvořeného objektu, zpravidla účtu. Činí tak prostřednictvím instance třídy Uid jménem "newUid". /* Tento skript vytvoří účet na koncovém systému napojeném pomocí Universal JDBC konektoru. Zde jde o jednoduchou tabulku "accounts" obsahující sloupce "id", "firstname", "lastname" a "state". 66 Skript CREATE Na vstupu dostane skript od konektoru množinu atributů, ve kterých musí být atributy "jmeno" a "prijmeni". Když se účet korektně vytvoří, tzn. do tabulky se přidá nový záznam, z hodnoty "Id" nového záznamu se zkonstruuje Uid nového účtu. To se dosadí do objektu s názvem "newUid", odkud ho bude umět konektor přečíst. */ import import import import import import import java.sql.Connection; java.sql.Driver; java.sql.DriverManager; java.sql.ResultSet; java.sql.SQLException; java.sql.Statement; java.sql.PreparedStatement; import java.util.*; import org.identityconnectors.framework.common.objects.*; final String DB_ACCOUNTS = "accounts"; final final final final String String String String COLUMN_ID = "id"; COLUMN_FIRST_NAME = "firstname"; COLUMN_LAST_NAME = "lastname"; COLUMN_STATE = "state"; final String STATE_ACTIVE = "active"; final String FIRST_NAME_ATTR = "jmeno"; final String LAST_NAME_ATTR = "prijmeni"; boolean loging = true; // Only ACCOUNT is allowed. if (!objClass.is(ObjectClass.ACCOUNT_NAME)) { throw new IllegalArgumentException("Bad object type. Only ACCOUNT is supported."); } //=============================== Metody skriptu ============================== Uid insertNewAccount(Connection connection, Set attributes) { if (attributes == null) { return; } Attribute attr = null; Iterator it = attributes.iterator(); String firstName = null; String lastName = null; while (it.hasNext()) { attr = (Attribute) it.next(); if (attr.getName().equals(FIRST_NAME_ATTR)) { firstName = attr.getValue().get(0); } if (attr.getName().equals(LAST_NAME_ATTR)) { lastName = attr.getValue().get(0); } } 67 Kapitola 11. Identity Connectors if ( firstName == null || lastName == null ) { throw new RuntimeException("First name and last name must be set."); } String insert = "INSERT INTO `"+DB_ACCOUNTS+"` (`"+COLUMN_FIRST_NAME+"`, `"+COLUMN_LAST_NAME +"`, `"+COLUMN_STATE+"`) " + "VALUES ( ?, ?, ?);"; PreparedStatement insertStm = null; ResultSet rs = null; int lastID = -1; Uid newUid = null; try { connection.setAutoCommit(false); // Vytvorime novy zaznam. insertStm = connection.prepareStatement(insert, java.sql.ResultSet.TYPE_FORWARD_ONLY, java.sql.ResultSet.CONCUR_UPDATABLE); insertStm.setString(1, firstName); insertStm.setString(2, lastName); insertStm.setString(3, STATE_ACTIVE); rs = insertStm.executeQuery(); if (rs.next()) { lastID = rs.getInt(COLUMN_ID); } else { throw new RuntimeException("No ID returned."); } newUid = new Uid(String.valueOf(lastID)); connection.commit(); } catch (SQLException e) { if (connection != null) { try { //Rollback connection.rollback(); } catch (SQLException excep) { throw new RuntimeException(excep.getMessage()); } } logErrors(e.getMessage(), loging); throw new RuntimeException(e.getMessage()); } catch (Exception ex) { logErrors(ex.getMessage(), loging); throw new RuntimeException(ex.getMessage()); } finally { if (insertStm != null) { insertStm.close(); } if (connection != null) { connection.setAutoCommit(true); } } return newUid; } // Pokud je atribut TRUE, tak vypisuje zpravu "error" primo do standardniho chyboveho vystupu. 68 Skript UPDATE void logErrors(String error, boolean log) { if (log) { System.err.println(error); } } //=============================== Konec - Metody skriptu ============================== // Vytvoření nového účtu na koncovém systému a získání jeho Uid. // Proměnné conn a attributes dodává konektor. newUid = insertNewAccount(conn, attributes); Příklad 11.1. Skript create.bsh 11.6.4. Skript UPDATE Název parametru Typ parametru Popis objClass ObjectClass Třída objektu, která je skriptem spravována. Obvykle ObjectClass.ACCOUNT attributes Set<Attribute> Množina atributů, které mají být propagovány na koncový systém options OperationOptions Možnosti operace conn Connection Objekt reprezentující JDBC připojení k databázi uid Uid Současný identifikátor objektu Tabulka 11.3. Parametry na vstupu skriptu UPDATE Skript slouží k aktualizaci objektu na koncovém systému. Na výstupu vrací identifikátor objektu po úpravě. Činí tak prostřednictvím instance třídy Uid jménem "newUid". /* Tento skript aktualizuje účet na koncovém systému napojeném pomocí Universal JDBC konektoru. Zde jde o jednoduchou tabulku "accounts" obsahující sloupce "id", "firstname", "lastname" a "state". Na vstupu dostane skript od konektoru - atribut uid typu Uid - množinu atributů attributes, ve kterých jsou modifikované atributy "jmeno" a "prijmeni". Uid účtu zůstává stejné, takže se pouze dosadí do objektu s názvem "newUid", odkud ho bude umět konektor přečíst. */ import import import import import import import java.sql.Connection; java.sql.Driver; java.sql.DriverManager; java.sql.ResultSet; java.sql.SQLException; java.sql.Statement; java.sql.PreparedStatement; 69 Kapitola 11. Identity Connectors import java.util.*; import org.identityconnectors.framework.common.objects.*; final String DB_ACCOUNTS = "accounts"; final final final final String String String String COLUMN_ID = "id"; COLUMN_FIRST_NAME = "firstname"; COLUMN_LAST_NAME = "lastname"; COLUMN_STATE = "state"; final String FIRST_NAME_ATTR = "jmeno"; final String LAST_NAME_ATTR = "prijmeni"; boolean loging = true; // Only ACCOUNT is allowed. if (!objClass.is(ObjectClass.ACCOUNT_NAME)) { throw new IllegalArgumentException("Bad object type. Only ACCOUNT is supported."); } //=============================== Metody skriptu ============================== Uid updateAccount(Uid uid, Connection connection, Set attributes) { if (uid == null) { throw new RuntimeException("UID is not set."); } if (attributes == null) { return; } Attribute attr = null; Iterator it = attributes.iterator(); String firstName = null; String lastName = null; // Zjištění hodnot modifikovaných atributů while (it.hasNext()) { attr = (Attribute) it.next(); if (attr.getName().equals(FIRST_NAME_ATTR)) { firstName = attr.getValue().get(0); } if (attr.getName().equals(LAST_NAME_ATTR)) { lastName = attr.getValue().get(0); } } String select = "SELECT `"+COLUMN_FIRST_NAME+"`, `"+COLUMN_LAST_NAME+"` FROM `"+DB_ACCOUNTS +"` WHERE `"+COLUMN_ID+"` = ?;"; PreparedStatement prepStmt = null; ResultSet rs = null; int lastID = -1; Uid newUid = null; try { connection.setAutoCommit(false); // Hodnota Uid je obsažena ve sloupci "id", pomoci toho najdeme účet prepStmt = connection.prepareStatement(select, java.sql.ResultSet.TYPE_FORWARD_ONLY, 70 Skript UPDATE java.sql.ResultSet.CONCUR_UPDATABLE); prepStmt.setInt(1, Integer.parseInt(uid.getUidValue())); rs = prepStmt.executeQuery(); if (!rs.next()) { throw new RuntimeException("Account for given UID wasn't found."); } if (firstName != null) { rs.updateString(COLUMN_FIRST_NAME, firstName); } if (lastName != null) { rs.updateString(COLUMN_LAST_NAME, lastName); } rs.updateRow(); //Navratova hodnota (uid se nemeni). newUid = uid; connection.commit(); } catch (SQLException e) { if (connection != null) { try { //Rollback connection.rollback(); } catch (SQLException excep) { throw new RuntimeException(excep.getMessage()); } } logErrors(e.getMessage(), loging); throw new RuntimeException(e.getMessage()); } catch (Exception ex) { logErrors(ex.getMessage(), loging); throw new RuntimeException(ex.getMessage()); } finally { if (prepStmt != null) { prepStmt.close(); } if (connection != null) { connection.setAutoCommit(true); } } return newUid; } // Pokud je atribut TRUE, tak vypisuje zpravu "error" primo do standardniho chyboveho vystupu. void logErrors(String error, boolean log) { if (log) { System.err.println(error); } } //=============================== Konec - Metody skriptu ============================== // Aktualizace účtu na koncovém systému a získání jeho Uid. // Proměnné uid, conn a attributes dodává konektor. 71 Kapitola 11. Identity Connectors newUid = updateAccount(uid, conn, attributes); Příklad 11.2. Skript update.bsh 11.6.5. Skript DELETE Název parametru Typ parametru Popis objClass ObjectClass Třída objektu, která je skriptem spravována. Obvykle ObjectClass.ACCOUNT options OperationOptions Možnosti operace conn Connection Objekt reprezentující JDBC připojení k databázi uid Uid Současný identifikátor objektu Tabulka 11.4. Parametry na vstupu skriptu DELETE Skript slouží ke smazání objektu na koncovém systému. Na výstupu nic nevrací. /* Tento skript obsluhuje smazání účtu na koncovém systému napojeném pomocí Universal JDBC konektoru. Zde jde o jednoduchou tabulku "accounts" obsahující sloupce "id", "firstname", "lastname" a "state". Mazání účtu se provádí vložením hodnoty "deleted" do sloupce "state", žádný řádek se ve skutečnosti neodstraňuje. Na vstupu dostane skript od konektoru atribut uid typu Uid. */ import import import import import import import java.sql.Connection; java.sql.Driver; java.sql.DriverManager; java.sql.ResultSet; java.sql.SQLException; java.sql.Statement; java.sql.PreparedStatement; import java.util.*; import org.identityconnectors.framework.common.objects.*; final String DB_ACCOUNTS = "accounts"; final String COLUMN_ID = "id"; final String COLUMN_STATE = "state"; final String STATE_DELETED = "deleted"; boolean loging = true; // Only ACCOUNT is allowed. if (!objClass.is(ObjectClass.ACCOUNT_NAME)) { throw new IllegalArgumentException("Bad object type. Only ACCOUNT is supported."); } 72 Skript DELETE //=============================== Metody skriptu ============================== // Označí účet s daným uid ke smazání (bez ohledu na jeho aktuální stav). void deleteAccount(Uid uid, Connection connection) { if (uid == null) { throw new RuntimeException("UID is not set."); } PreparedStatement deleteStm = null; String statement = "UPDATE `"+DB_ACCOUNTS+"` SET `"+COLUMN_STATE+"` = ? WHERE `"+COLUMN_ID+"` = ?;"; try { connection.setAutoCommit(false); deleteStm = connection.prepareStatement(statement); deleteStm.setString(1, STATE_DELETED); deleteStm.setInt(2, Integer.parseInt(uid.getUidValue())); deleteStm.executeUpdate(); connection.commit(); } catch (Exception ex) { if (connection != null) { try { //Rollback connection.rollback(); } catch (SQLException excep) { throw new RuntimeException(excep.getMessage()); } } logErrors(e.getMessage(), loging); throw new RuntimeException(e.getMessage()); } catch (Exception ex) { logErrors(ex.getMessage(), loging); throw new RuntimeException(ex.getMessage()); } finally { if (deleteStm != null) { deleteStm.close(); } if (connection != null) { connection.setAutoCommit(true); } } } // Pokud je atribut TRUE, tak vypisuje zpravu "error" primo do standardniho chyboveho vystupu. void logErrors(String error, boolean log) { if (log) { System.err.println(error); } } //=============================== Konec - Metody skriptu ============================== // Smazání účtu na koncovém systému. // Proměnné uid, conn a attributes dodává konektor. deleteAccount(uid, conn, attributes); 73 Kapitola 11. Identity Connectors Příklad 11.3. Skript delete.bsh 11.6.6. Skript LISTALL Název parametru Typ parametru Popis objClass ObjectClass Třída objektu, která je skriptem spravována. Obvykle ObjectClass.ACCOUNT options OperationOptions Možnosti operace conn Connection Objekt reprezentující JDBC připojení k databázi Tabulka 11.5. Parametry na vstupu skriptu LISTALL Skript na výstupu vrací seznam objektů na koncovém systému. Činí tak prostřednictvím instance třídy List<ConnectorObject> jménem "listOfIDs". /* Tento skript vrací seznam účtů na koncovém systému napojeném pomocí Universal JDBC konektoru. Zde jde o jednoduchou tabulku "accounts" obsahující sloupce "id", "firstname", "lastname" a "state". Existující účet je takový, který má ve sloupci "state" hodnotu "active". Seznam účtů se dosadí do objektu s názvem "listOfIDs", odkud ho bude umět konektor přečíst. */ import import import import import import import java.sql.Connection; java.sql.Driver; java.sql.DriverManager; java.sql.ResultSet; java.sql.SQLException; java.sql.Statement; java.sql.PreparedStatement; import java.util.*; import org.identityconnectors.framework.common.objects.*; final String DB_ACCOUNTS = "accounts"; final final final final String String String String COLUMN_ID = "id"; COLUMN_FIRST_NAME = "firstname"; COLUMN_LAST_NAME = "lastname"; COLUMN_STATE = "state"; final String STATE_ACTIVE = "active"; final String FIRST_NAME_ATTR = "jmeno"; final String LAST_NAME_ATTR = "prijmeni"; boolean loging = true; // Only ACCOUNT is allowed. if (!objClass.is(ObjectClass.ACCOUNT_NAME)) { throw new IllegalArgumentException("Bad object type. Only ACCOUNT is supported."); 74 Skript LISTALL } //=============================== Metody skriptu ============================== // Vrátí ConnectorObject pro účet na koncovém systému s daným uid ConnectorObject getUser(String uid, Connection connection) { ConnectorObject connectorObject = null; ConnectorObjectBuilder builder = null; String statementAccount = "SELECT `"+COLUMN_ID+"`, `"+COLUMN_FIRST_NAME+"`, `"+COLUMN_LAST_NAME+"` FROM `"+DB_ACCOUNTS+"` WHERE `"+COLUMN_ID+"` = ?;"; PreparedStatement existStmt = null; ResultSet rs = null; String firstName = null; String lastName = null; Collection attributesCol = null; try { // Nalezení objektu s daným UID existStmt = connection.prepareStatement(statementAccount); existStmt.setInt(1, Integer.parseInt(uid)); rs = existStmt.executeQuery(); if (!rs.next()) { // Ucet neexistuje return null; } builder = new ConnectorObjectBuilder(); attributesCol = new ArrayList(); // Získání hodnot atributů firstName = rs.getString(COLUMN_FIRST_NAME); lastName = rs.getString(COLUMN_LAST_NAME); // Vytvoření objektů typu Attribute se získanými hodnotami a přidání do seznamu attributesCol.add(AttributeBuilder.build(FIRST_NAME_ATTR, firstName)); attributesCol.add(AttributeBuilder.build(LAST_NAME_ATTR, lastName)); builder.addAttributes(attributesCol); // Nastavíme třídu objektu UID, Name a vytvoříme objekt. builder.setObjectClass(ObjectClass.ACCOUNT); builder.setUid(uid); builder.setName(uid); connectorObject = builder.build(); } catch (Exception ex) { logErrors(ex.getMessage(), loging); throw new RuntimeException(ex.getMessage()); } finally { if (existStmt != null) { existStmt.close(); } } return connectorObject; } // Vrátí seznam všech aktivních účtů na koncovém systému 75 Kapitola 11. Identity Connectors List listAllAccounts(Connection connection) { List allObjects = new ArrayList(); ConnectorObject object = null; ConnectorObjectBuilder builder = null; String statementAccount = null; PreparedStatement accountStmt = null; ResultSet rs = null; int id; builder = new ConnectorObjectBuilder(); statementAccount = "SELECT `"+COLUMN_ID+"` FROM `"+DB_ACCOUNTS+"` WHERE `"+COLUMN_STATE+"` = ?;"; accountStmt = connection.prepareStatement(statementAccount); accountStmt.setString(1, STATE_ACTIVE); rs = accountStmt.executeQuery(); while (rs.next()) { id = rs.getInt(COLUMN_ID); object = getUser(String.valueOf(id), connection); allObjects.add(object); } return allObjects; } // Pokud je atribut TRUE, tak vypisuje zpravu "error" primo do standardniho chyboveho vystupu. void logErrors(String error, boolean log) { if (log) { System.err.println(error); } } //=============================== Konec - Metody skriptu ============================== // Vytvoření seznamu všech aktivních účtů. // Proměnnou conn dodává konektor. listOfIDs = listAllAccounts(conn); Příklad 11.4. Skript listall.bsh 11.6.7. Skript SCHEMA Název parametru Typ parametru Popis conn Connection Objekt reprezentující JDBC připojení k databázi Tabulka 11.6. Parametry na vstupu skriptu SCHEMA Skript na výstupu vrací mapu atributů a jejich typů na koncovém systému jako HashMap<String, String>. /* Tento skript vrací mapu atributů a jejich typů na koncovém systému napojeném pomocí Universal JDBC konektoru. 76 Skript GET Zde jde o jednoduchou tabulku "accounts" obsahující sloupce "id", "firstname", "lastname" a "state". */ import java.util.*; final String DB_ACCOUNTS = "accounts"; final final final final String String String String COLUMN_ID = "id"; COLUMN_FIRST_NAME = "firstname"; COLUMN_LAST_NAME = "lastname"; COLUMN_STATE = "state; //=============================== Metody skriptu ============================== HashMap getSchema() { HashMap names = new HashMap(); names.put(COLUMN_ID, "java.lang.Integer"); names.put(COLUMN_FIRST_NAME, "java.lang.String"); names.put(COLUMN_LAST_NAME, "java.lang.String"); names.put(COLUMN_STATE, "java.lang.String"); return names; } //=============================== Konec - Metody skriptu // Vytvoření mapy atributů na koncovém systému HashMap attributeNames = getSchema(); ============================== Příklad 11.5. Skript schema.bsh 11.6.8. Skript GET Název parametru Typ parametru Popis objClass ObjectClass Třída objektu, která je skriptem spravována. Obvykle ObjectClass.ACCOUNT options OperationOptions Možnosti operace conn Connection Objekt reprezentující JDBC připojení k databázi uid Uid Současný identifikátor objektu Tabulka 11.7. Parametry na vstupu skriptu GET Skript na výstupu vrací atributy na koncovém systému prostřednictvím instance třídy ConnectorObject. /* Tento skript vrací účet na koncovém systému napojeném pomocí Universal JDBC konektoru. Zde jde o jednoduchou tabulku "accounts" obsahující sloupce "id", "firstname", "lastname" a "state". Existující účet se nahraje do objektu třídy ConnectorObject s názvem "connectorObject", odkud ho bude umět konektor přečíst. */ 77 Kapitola 11. Identity Connectors import import import import import import import java.sql.Connection; java.sql.Driver; java.sql.DriverManager; java.sql.ResultSet; java.sql.SQLException; java.sql.Statement; java.sql.PreparedStatement; import java.util.*; import org.identityconnectors.framework.common.objects.*; final String DB_ACCOUNTS = "accounts"; final String COLUMN_ID = "id"; final String COLUMN_FIRST_NAME = "firstname"; final String COLUMN_LAST_NAME = "lastname"; final String FIRST_NAME_ATTR = "jmeno"; final String LAST_NAME_ATTR = "prijmeni"; boolean loging = true; // Only ACCOUNT is allowed. if (!objClass.is(ObjectClass.ACCOUNT_NAME)) { throw new IllegalArgumentException("Bad object type. Only ACCOUNT is supported."); } //=============================== Metody skriptu ============================== // Vrátí ConnectorObject pro účet na koncovém systému s daným uid ConnectorObject getUser(String uid, Connection connection) { ConnectorObject connectorObject = null; ConnectorObjectBuilder builder = null; String statementAccount = "SELECT `"+COLUMN_ID+"`, `"+COLUMN_FIRST_NAME+"`, `"+COLUMN_LAST_NAME+"` FROM `"+DB_ACCOUNTS+"` WHERE `"+COLUMN_ID+"` = ?;"; PreparedStatement existStmt = null; ResultSet rs = null; String firstName = null; String lastName = null; Collection attributesCol = null; try { // Nalezení objektu s daným UID existStmt = connection.prepareStatement(statementAccount); existStmt.setInt(1, Integer.parseInt(uid)); rs = existStmt.executeQuery(); if (!rs.next()) { // Ucet neexistuje return null; } builder = new ConnectorObjectBuilder(); attributesCol = new ArrayList(); // Získání hodnot atributů firstName = rs.getString(COLUMN_FIRST_NAME); lastName = rs.getString(COLUMN_LAST_NAME); 78 Skript SYNC // Vytvoření objektů typu Attribute se získanými hodnotami a přidání do seznamu attributesCol.add(AttributeBuilder.build(FIRST_NAME_ATTR, firstName)); attributesCol.add(AttributeBuilder.build(LAST_NAME_ATTR, lastName)); builder.addAttributes(attributesCol); // Nastavíme třídu objektu UID, Name a vytvoříme objekt. builder.setObjectClass(ObjectClass.ACCOUNT); builder.setUid(uid); builder.setName(uid); connectorObject = builder.build(); } catch (Exception ex) { logErrors(ex.getMessage(), loging); throw new RuntimeException(ex.getMessage()); } finally { if (existStmt != null) { existStmt.close(); } } return connectorObject; } // Pokud je atribut TRUE, tak vypisuje zpravu "error" primo do standardniho chyboveho vystupu. void logErrors(String error, boolean log) { if (log) { System.err.println(error); } } //=============================== Konec - Metody skriptu ============================== // Vytvoření objektu typu ConnectorObject s obsahem atributů koncového účtu. // Proměnné uid a conn dodá konektor. connectorObject = getUser(uid, conn); Příklad 11.6. Skript get.bsh 11.6.9. Skript SYNC Název parametru Typ parametru Popis objClass ObjectClass Třída objektu, která je skriptem spravována. Obvykle ObjectClass.ACCOUNT options OperationOptions Možnosti operace conn Connection Objekt reprezentující JDBC připojení k databázi token Token Čas poslední synchronizace Tabulka 11.8. Parametry na vstupu skriptu SYNC Skript na výstupu vrací seznam instancí třídy ConnectorObject, který obsahuje všechny objekty změněné od poslední synchronizace. 79 Kapitola 11. Identity Connectors 11.6.10. Skript GET_LAST_SYNC_TOKEN Název parametru Typ parametru Popis options OperationOptions Možnosti operace conn Connection Objekt reprezentující JDBC připojení k databázi Tabulka 11.9. Parametry na vstupu skriptu GET_LAST_SYNC_TOKEN Skript na výstupu vrací instanci třídy Token, který obsahuje čas poslední synchronizace. 11.7. Database Table konektor Database Table konektor slouží k napojení jedné tabulky libovolné databáze. Standardně je uložen v balíku org.identityconnectors.databasetable-XXX.jar. Příkladem takového systému může být evidence zaměstnanců v personálním systému, kde všechna data čteme a zapisujeme do jedné tabulky (případně jednoho view). Každý řádek tabulky je z pohledu IdM jeden účet na koncovém systému. Název parametru Popis Příklad hodnoty Název systému Výstižný název, pod kterým se systém např. zobrazuje uživatelům. Personální systém Pouze ke čtení Zaškrtnutím této možnosti je odškrtnuto zajištěno, že na koncovém systému nebude moci Identity Manager nic vytvořit, změnit ani smazat. Authority level Určuje prioritu systému. Hodnota větší než 0 znamená, že půjde o autoritativní zdroj pro IdM; čím vyšší, tím autoritativnější. Poznámka: největší prioritu má IdM repository. 0, nebo 1 pro autoritativní zdroje User Password Heslo uživatele, s nímž se IdM přihlašuje k databázi. ****** Table Název napojované tabulky (jeli potřeba, tak včetně názvu schématu). VYMENIK.identity Key Column Název sloupce, který obsahuje jednoznačný identifikátor účtu (accountUid). Pomocí něj se párují účty se řádky tabulky. Typicky je to primární klíč tabulky. Id JDBC Driver Driver pro spojení s databází. • oracle.jdbc.driver.OracleDriver pro Oracle 80 Database Table konektor Název parametru Popis Příklad hodnoty • com.mysql.jdbc.Driver pro MySQL • org.firebirdsql.jdbc.FBDriver pro Firebird Datasource path Něco specifického pro připojení k Oracle. [prázdné] Validate Connection Query Speciální příkaz pro otestování, [prázdné] zda je spojení navázáno. Při neuvedení se použije defaultní testovací příkaz. User Login uživatele, s nímž se IdM přihlašuje k databázi. root Rethrow all SQLExceptions Odškrtnutím lze zajistit, že SQL příkazy, které způsobují SQLException s 0 Error Code, budou odchyceny a potlačeny. zaškrtnuto All native Zaškrtnutí způsobí, že všechny datové typy sloupců tabulky budou vraceny v nativním formátu. odškrtnuto Name Quoting Nastavuje znaky, mezi které se budou vkládat názvy sloupců (např. jednoduché uvozovky, závorky,...) v posílaných SQL příkazech. [prázdné] Password Column Název sloupce, ve kterém se ukládají hesla účtů uživatelů. Nemusí být vyplněn, pokud se hesla nemají ukládat. [prázdné] Port Port, na kterém databázový server naslouchá. • 1521 pro Oracle • 3306 pro MySQL • 3051 pro Firebird Native Timestamps Zaškrtnutím lze zajistit, že se bude datový typ Timestamp vracet jako java.sql.Timestamp. odškrtnuto Enable writing empty string Zaškrtnutí způsobí, že pro odškrtnuto sloupce s omezením not-null se budou prázdné řetězce zapisovat jako prázdné řetězce, a ne NULL (což je defaultní chování). Initial JNDI Properties Prostor pro nastavení počátečního JDBC JNDI [prázdné] 81 Kapitola 11. Identity Connectors Název parametru Popis Příklad hodnoty kontextu, ve formátu "klíč = hodnota". Host Adresa databázového serveru localhost JDBC Connection URL Template pro vytvoření url pro JDBC konektor - za hodnoty %h, %p a %d se dosadí host, port a název databáze. Umožňuje definovat další vstupní hodnoty, např. kódování. • jdbc:oracle:thin:@%h: %p:%d pro Oracle • jdbc:mysql:// %h:%p/%d? useUnicode=true&characterEncoding=UTFpro MySQL • jdbc:firebirdsql:%h/ %p:%d pro Firebird Change Log Column (Sync) Název sloupce, který obsahuje datum poslední změny na řádku/účtu. Používá se při synchronizaci s koncovým systémem. GREATEST_TIMESTAMP Database Název databáze. personal_repository Tabulka 11.10. Parametry systému napojeného pomocí Database Table konektoru 11.8. SSH konektor Univerzální SSH konektor poskytuje možnosti pro připojení se k serveru prostřednictvím protokolu SSH2. Konektor implementuje všechny potřebné metody pro správu identit na koncovém systému. Koncovým systém může být buď přímo daný server, případně se může jednat o externí systém, který na tomto serveru běží. Realizace napojení koncového systému je rozdělena do dvou částí. První část tvoří samotný SSH konektor. Druhou částí jsou skripty na daném serveru, které provádějí požadované operace nad koncovým systémem. Tato architektura umožňuje použití konektoru k připojení k celé škály koncových systémů. Stačí pouze pro každý systém vytvořit potřebné skripty dle daného rozhraní. Atributy a všechny požadované informace jsou mezi konektorem a koncovými skripty předávány ve formátu CSV nebo jako obyčejné textové řetězce. V této sekci je blíže popsáno, jak konektor konfigurovat, jaké operace nad koncovým systémem jsou konektorem podporovány a příklady jejich vstupů a výstupů. 11.8.1. Konfigurace konektoru Konektor je standardně uložen v balíku org.identityconnectors.ssh-XXX.jar. Název parametru Popis Status Název systému Výstižný název, pod kterým se systém např. zobrazuje uživatelům. POVINNÉ Pouze ke čtení Zaškrtnutím této možnosti je zajištěno, že na koncovém systému nebude moci Identity 82 Skript Create Název parametru Popis Status Manager nic vytvořit, změnit ani smazat. Authority level Určuje prioritu systému. Hodnota větší než 0 znamená, že půjde o autoritativní zdroj pro IdM; čím vyšší, tím autoritativnější. Poznámka: největší prioritu má IdM repository. POVINNÉ Password Heslo uživatele, s nímž se IdM přihlašuje na koncový systém. NEPOVINNÉ multi value attributes Výčet multi-value atributů. NEPOVINNÉ Private key Cesta k privátnímu klíči pro přihlášení. NEPOVINNÉ Private key password Heslo k privátnímu klíči. NEPOVINNÉ multi value attributes separator Oddělovač použitý pro oddělení NEPOVINNÉ vícehodnotových atributů. escape mode Escape mode (BACKSLASH nebo DOUBLED). POVINNÉ Port Číslo portu serveru, kde běží SSH démon. POVINNÉ Host Hostname serveru, kde běží SSH démon. POVINNÉ Username Uživatelské jméno pro přihlášení se na koncový systém. POVINNÉ Host key Veřejný klíč serveru (pro kontrolu otisku klíče při přihlaš.). NEPOVINNÉ Tabulka 11.11. Parametry systému napojeného pomocí Univerzálního SSH konektoru Dále se pro každou operaci nad koncovým systémem vyplní úplná cesta skriptu, který ji zpracovává. Cesty k jednotlivým skriptům jsou sice nepovinné, ale pokud volaná metoda cestu ke skriptu potřebuje a daná cesta není nastavena, dojde k vyhození odpovídající výjimky. To je uděláno z toho důvodu, aby se nemusely nastavovat cesty ke skriptům pro metody, které nebudou používány. 11.8.2. Skript Create Tato metoda slouží pro vytváření nových objektů na koncovém systému. V případě úspěšného vytvoření nového objektu metoda navrací jeho jedinečný identifikátor (instance třídy Uid). Vytvářet se mohou jak uživatelské účty (createUser), tak i uživatelské skupiny (createGroup). Vstup Množina atributů, které mají být propagovány na koncový systém. Výstup AccountId nově vytvořené osoby. 83 Kapitola 11. Identity Connectors Příklad vstupu createUser Name;EmailAddress;GECOS;Disabled mucha8;[email protected];alfons;0 Příklad výstupu AccountId user/64 Tabulka 11.12. Vstup a výstup skriptu createUser 11.8.3. Skript Update Tato metoda slouží pro editaci stávajícího objektu. Požadovaný objekt je vybrán dle jeho jedinečného identifikátoru (instance třídy Uid) a následně jsou u něho změněny požadované hodnoty. Metoda závěrem navrací identifikátor změněného objektu (i identifikátor se může měnit). Editovat se mohou jak uživatelské účty (updateUser), tak i uživatelské skupiny (updateGroup). Vstup Změněné hodnoty a AccountId účtu, kde se mají změny provést. Výstup AccountId. Příklad vstupu updateUser AccountId;Disabled user/82;0 Příklad výstupu AccountId user/82 Tabulka 11.13. Vstup a výstup skriptu updateUser 11.8.4. Skript Delete Tato metoda slouží pro odstranění objektu na koncovém systému. Odpovídající objekt je určen dle svého jedinečného identifikátoru. Odstraňovat se mohou jak uživatelské účty (deleteUser), tak i uživatelské skupiny (deleteGroup). Vstup Pouze identifikátor daného účtu (parametr AccountId), který se má smazat. Výstup Nic, pouze návratová hodnota funkce. Příklad vstupu deleteUser AccountId user/102 Příklad výstupu Tabulka 11.14. Vstup a výstup skriptu deleteUser 11.8.5. Skript DisableUser Tato metoda slouží pro zablokování uživatelského účtu. Blokaci účtu lze provést také prostřednictvím funkce updateUser a atributu Disabled. Vstup AccountId účtu, který se má zamknout. Výstup Nic, pouze návratová hodnota funkce. Příklad vstupu disableUser AccountId user/64 Příklad výstupu Tabulka 11.15. Vstup a výstup skriptu disableUser 11.8.6. Skript EnableUser Tato metoda slouží pro odemčení uživatelského účtu. Odblokování účtu lze provést také prostřednictvím funkce updateUser a atributu Disabled. 84 Skript ListObjects Vstup AccountId účtu, který se má odemknout. Výstup Nic, pouze návratová hodnota funkce. Příklad vstupu enableUser AccountId user/64 Příklad výstupu Tabulka 11.16. Vstup a výstup skriptu enableUser 11.8.7. Skript ListObjects Tato metoda slouží pro vypsání AccountId všech evidovaných osob. Vstup Pouze parametry příkazu, žádné hodnoty. Výstup AccountId všech uživatelů koncového systému. Příklad vstupu listObjects objectType Users Příklad výstupu AccountId user/1 user/10 user/25 Tabulka 11.17. Vstup a výstup skriptu listObjects 11.8.8. Skript AttributesSchema Tato metoda navrací defaultní schéma atributů na koncovém systému, se kterými mohou pracovat ostatní funkce. Vstup Pouze název funkce. Výstup Seznam podporovaných atributů (název + typ + status, např. required). Příklad vstupu getAttributesSchema Příklad výstupu "Attribute_Name";"Type";"Flags" "Name";"java.lang.String";"REQUIRED" "EmailAddress";"java.lang.String";"REQUIRED" "Password";"org.identityconnectors.common.security.GuardedS "Comments";"java.lang.String";"" "Signature";"java.lang.String";"" "RealName";"java.lang.String";"" "Lang";"java.lang.String";"" "GECOS";"java.lang.String";"" "NickName";"java.lang.String";"" "Organization";"java.lang.String";"" Tabulka 11.18. Vstup a výstup skriptu attributesSchema 11.8.9. Příklad univerzálního skriptu Všechny operace podporované SSH konektorem mohou být obsluhovány jedním skriptem. Níže je uveden příklad univerzálního skriptu, který byl použit při napojení systému pro správu požadavků Request Tracker pomocí SSH konektoru. #!/bin/bash 85 Kapitola 11. Identity Connectors # Tento skript slouzi pro praci s ucty na Request Trackeru verze 3.6.7, a to prostrednictvim RT konzole. # Autor: BCV solutions s.r.o. ############################################################################ KONFIGURACE # ############################################################################ # Cesta k awk skriptu pro parsovani CSV AWKCSV="/opt/rt_connector_scripts/csv.awk" # Cesta ke skriptu pro spusteni RT konzole. RTC="/data/rt4/bin/rt" # Pouzivane programy (standardni linux distribuce) AWK=/bin/awk PERL="/usr/bin/perl -w" # Konfiguracni informace o RT export RTSERVER="http://servicedesk:8091/" export RTUSER=root export RTPASSWD=password # Konfiguracni udaje k RT databazi (MySQL) RTDBUSER=root RTDBPASSWD=root RTDBHOST=localhost RTDBNAME=rt4 ############################################################################ # Polozky vystupniho CSV atAccountId="AccountId" # V RT odpovida "id". atPassword="password" # V RT odpovida "Password". # Polozky nazvy atributu vracene RT konzoli rtAccountId="id" # V SSH konektoru odpovida "AccountId". rtPassword="Password" # V SSH konektoru odpovida "password". export PATH=/bin:/usr/bin:/sbin:/usr/sbin # Zpracovani chyb Error() { cat - >&2 [ $# -ge 1 ] && exit $1 } Log () { Error } Debug() { cat - >> /data/rt_conector_script.debug } # Zpracovani vstupnich dat, ktere jsou v CSV formatu. zpracovatVstup() { OIFS=$IFS; IFS=\; local radek=0 while read vstup; do Debug <<< $vstup radek=$((radek+1)) if [ ${radek} -eq 1 ]; then IDM_Prikaz=$vstup; fi if [ ${radek} -eq 2 ]; then CSVH=( $vstup ); fi 86 Příklad univerzálního skriptu if [ ${radek} -eq 3 ]; then IFS=$OIFS eval $($AWK -f $AWKCSV <<< ${vstup}) break fi # vic radku uz nebudeme nacitat i kdyby nam je IdM cpalo # mozna by to chtelo kontrolovat a vracet chybu done #DEBUG: echo $IDM_Prikaz >> /ssh/vstup IFS=$OIFS; #zakladni kontrola vstupnich dat - hlavicka nesmi mit min sloupcu nez hodnoty if [ ${#CSVV[*]} -gt ${#CSVH[*]} ]; then Error 1 <<< "ERROR: Spatny format CSV dat $1." fi for i in $(seq 0 $((${#CSVH[*]} - 1))); do eval "CSV_${CSVH[$i]}=\"${CSVV[$i]}\"" done } # Spusteni akce, kterou Identity Manager pozaduje # - pro deaktivaci staci zakomentovat odpovidajici radek spustitAkci() { case "$1" in ("getUser") getUser ;; ("createUser") createUser ;; ("updateUser") updateUser ;; ("deleteUser") deleteUser ;; ("enableUser") enableUser ;; ("disableUser") disableUser ;; # ("createGroup") createGroup ;; # ("updateGroup") updateGroup ;; # ("deleteGroup") deleteGroup ;; ("listObjects") listObjects ;; # ("getGroup") getGroup ;; (*) Error 1 <<< "ERROR: Nepodporovana akce $1." ;; esac } # Spusti prikaz na RT konzoli. runCmd () { COMMAND="$PERL $RTC $@" #Log <<< "Spusten prikaz: $COMMAND" eval "$COMMAND" } # Spousti MySQL dotaz. runMySQLQuery() { #Log <<< "Spusten MySQL dotaz: $1" unset result result=`/usr/bin/mysql --user=$RTDBUSER --password=$RTDBPASSWD --host=$RTDBHOST --database= $RTDBNAME -e "$1"` echo "$result" } # Vraci seznam nastavenych atributu. setAttributes() { unset cName cEmailAddress cPassword cComments cSignature cRealName cLang cGECOS cNickName cOrganization cHomePhone unset cWorkPhone cMobilePhone cPagerPhone cAddress1 cAddress2 cCity cState cZip cCountry cFreeformContactInfo unset cEmailEncoding cWebEncoding cExternalContactInfoId cContactInfoSystem cExternalAuthId cAuthSystem cTimezone cPGPKey 87 Kapitola 11. Identity Connectors if [ "x$CSV_Name" != "x" ]; then cName="Name=\"${CSV_Name}\""; fi if [ "x$CSV_EmailAddress" != "x" ]; then cEmailAddress="EmailAddress= \"${CSV_EmailAddress}\""; fi if [ "x$CSV_Password" != "x" ]; then cPassword="Password=\"${CSV_Password}\""; fi if [ "x$CSV_Comments" != "x" ]; then cComments="Comments=\"${CSV_Comments}\""; fi if [ "x$CSV_Signature" != "x" ]; then cSignature="Signature=\"${CSV_Signature}\""; fi if [ "x$CSV_RealName" != "x" ]; then cRealName="RealName=\"${CSV_RealName}\""; fi if [ "x$CSV_Lang" != "x" ]; then cLang="Lang=\"${CSV_Lang}\""; fi if [ "x$CSV_GECOS" != "x" ]; then cGECOS="GECOS=\"${CSV_GECOS}\""; fi if [ "x$CSV_NickName" != "x" ]; then cNickName="NickName=\"${CSV_NickName}\""; fi if [ "x$CSV_Organization" != "x" ]; then cOrganization="Organization= \"${CSV_Organization}\""; fi if [ "x$CSV_HomePhone" != "x" ]; then cHomePhone="HomePhone=\"${CSV_HomePhone}\""; fi if [ "x$CSV_WorkPhone" != "x" ]; then cWorkPhone="WorkPhone=\"${CSV_WorkPhone}\""; fi if [ "x$CSV_MobilePhone" != "x" ]; then cMobilePhone="MobilePhone=\"${CSV_MobilePhone}\""; fi if [ "x$CSV_PagerPhone" != "x" ]; then cPagerPhone="PagerPhone=\"${CSV_PagerPhone}\""; fi if [ "x$CSV_Address1" != "x" ]; then cAddress1="Address1=\"${CSV_Address1}\""; fi if [ "x$CSV_Address2" != "x" ]; then cAddress2="Address2=\"${CSV_Address2}\""; fi if [ "x$CSV_City" != "x" ]; then cCity="City=\"${CSV_City}\""; fi if [ "x$CSV_State" != "x" ]; then cState="State=\"${CSV_State}\""; fi if [ "x$CSV_Zip" != "x" ]; then cZip="Zip=\"${CSV_Zip}\""; fi if [ "x$CSV_Country" != "x" ]; then cCountry="Country=\"${CSV_Country}\""; fi if [ "x$CSV_FreeformContactInfo" != "x" ]; then cFreeformContactInfo="FreeformContactInfo= \"${CSV_FreeformContactInfo}\""; fi if [ "x$CSV_EmailEncoding" != "x" ]; then cEmailEncoding="EmailEncoding= \"${CSV_EmailEncoding}\""; fi if [ "x$CSV_WebEncoding" != "x" ]; then cWebEncoding="WebEncoding=\"${CSV_WebEncoding}\""; fi if [ "x$CSV_ExternalContactInfoId" != "x" ]; then cExternalContactInfoId="ExternalContactInfoId=\"${CSV_ExternalContactInfoId}\""; fi if [ "x$CSV_ContactInfoSystem" != "x" ]; then cContactInfoSystem="ContactInfoSystem= \"${CSV_ContactInfoSystem}\""; fi if [ "x$CSV_ExternalAuthId" != "x" ]; then cExternalAuthId="ExternalAuthId= \"${CSV_ExternalAuthId}\""; fi if [ "x$CSV_AuthSystem" != "x" ]; then cAuthSystem="AuthSystem=\"${CSV_AuthSystem}\""; fi if [ "x$CSV_Timezone" != "x" ]; then cTimezone="Timezone=\"${CSV_Timezone}\""; fi if [ "x$CSV_PGPKey" != "x" ]; then cPGPKey="PGPKey=\"${CSV_PGPKey}\""; fi list="$cName $cEmailAddress $cPassword $cComments $cSignature $cRealName $cLang $cGECOS $cNickName $cOrganization $cHomePhone $cWorkPhone $cMobilePhone $cPagerPhone $cAddress1 $cAddress2 $cCity $cState $cZip $cCountry $cFreeformContactInfo $cEmailEncoding $cWebEncoding $cExternalContactInfoId $cContactInfoSystem $cExternalAuthId $cAuthSystem $cTimezone $cPGPKey" echo $list } # Funkce navraci vsechny identifikatory uzivatelskych uctu nebo skupin. # Aktualne: Pouze uzivatelske ucty. # STATUS: OK listObjects() { # Musi byt uveden typ pozadovaneho objektu if [ "x${CSV_objectType}" == "x" ]; then Error 100 <<< "ERROR: Nebyl zadan typ pozadovanych objektu." fi case "$CSV_objectType" in ("Users") result=$(runMySQLQuery "SELECT id FROM Users;") echo "$atAccountId" #Odstranime hlavicku, tj. prvni radek vysledku, a pridame identifikator "user". #result=`echo "$result" | sed 's/id//;s/\ /\nuser\//g' | sed '1d'` result=`echo "$result" | sed '1d;s/^/user\//g'` 88 Příklad univerzálního skriptu echo "$result" ;; ("Group") Error 101 <<< "ERROR: Operace se skupinami nejsou zatim podporovany." exit 0; ;; (*) Error 102 <<< "ERROR: Nepodporovany typ objektu $CSV_objectType." ;; esac } # Navraci udaje pro uzivatele s danym jmenem (vyhledavani dle id (id) nebo uzivatelskeho jmena (Name)) # STATUS: OK getUser() { IFS=": " # vyhledani udaju o danem uzivateli if [ "x$CSV_AccountId" != "x" ]; then userId=$CSV_AccountId elif [ "x$CSV_Name" != "x" ]; then userId=$CSV_Name else Error 110 <<< "ERROR: Nebylo zadano accountId ani Name." fi param="show -t user $userId" output=<( runCmd $param if [ $? -ne 0 ]; then Error 111 <<< "ERROR: Uzivatel $userId nebyl nalezen." fi ) local i=1 while read NAME VALUE; do if [ "$i" -eq 1 ] && [ "$NAME" != "id" ]; then # Neni asi lepsi moznost, jak detekovat to, ze uzivatel s danym jmenem neexistuje. Error 111 <<< "ERROR: Uzivatel $userId nebyl nalezen." fi if [ "$NAME" == "$rtAccountId" ]; then CSV_Header="$CSV_Header;$atAccountId" # Kvuli konektoru, tam je stanoveno, ze bude "AccountId". id="$VALUE" elif [ "$NAME" == "$rtPassword" ]; then # Heslo se nevraci, jako VALUE by se vratil obsah adresare, ve kterem je tento skript. CSV_Header="$CSV_Header;$atPassword" # Kvuli konektoru, tam je stanoveno, ze bude "password". VALUE="" else CSV_Header="$CSV_Header;$NAME" fi # Eskejpovani - DOUBLED, tj. " misto "" VALUE=`echo $VALUE | sed 's/"/""/'` CSV_Values="$CSV_Values;\"$VALUE\"" i=$((i+1)) done < $output # Zjistime status uzivatelskeho uctu (zamknuty nebo odemknuty). id=`echo "$id" | sed 's/user\///'` disabled=$(runMySQLQuery "SELECT Disabled FROM Principals WHERE id=$id;") disabled=`echo $disabled | sed '1d'` 89 Kapitola 11. Identity Connectors CSV_Header="$CSV_Header;Disabled" CSV_Values="$CSV_Values;$disabled" # Odstranime stredniky na zacatcich. CSV_Header=`echo "$CSV_Header" | sed 's/;//'` CSV_Values=`echo "$CSV_Values" | sed 's/;//'` echo "$CSV_Header" echo "$CSV_Values" Debug <<< "getUser CSV_Header: ${CSV_Header}" Debug <<< "getUser CSV_Values: ${CSV_Values}" } # Vytvari zaznam pro noveho uzivatele. # STATUS: OK createUser() { if [ "x${CSV_Name}" == "x" ]; then Error 120 <<< "ERROR: Nebylo zadano uzivatelske jmeno (Name)." fi list=$(setAttributes) param="create -t user set $list" IFS="" # Spusteni prikazu a kontrola, zda byl uzivatel skutecne vytvoren. Debug <<< "createUser param=${param}" read output < <(runCmd $param) text=`echo $output | sed 's/[0-9][0-9]*//'` # Pokud nebyl uzivatelsky ucet zalozen, tak vypiseme odpovidajici chybu. if [ "$text" != "# User created." ]; then Error 121 <<< "ERROR: $output" fi # Uzivatelsky ucet byl vytvoren, navratime user ID. id="user/`echo $output | sed 's/^[#a-zA-Z ]*//;s/[ a-zA-Z.]*$//'`" echo "$atAccountId" echo "$id" # Zamknout ucet? if [ "x$CSV_Disabled" == "x1" ]; then # Zamkneme ucet x=$(disableUser "$id") fi # Pokud nebylo stanoveno, ze bude zamcen, tak nic nedelame, tj. defaultne odemknut. } # Meni zaznam pro daneho uzivatele. # #STATUS: OK updateUser() { if [ "x$CSV_AccountId" != "x" ]; then user=$(getUser $CSV_AccountId) retCode=$? else Error 130 <<< "ERROR: Nebylo zadano accountId." fi # Uzivatel s danym AccountId nebo Name neexistuje. if [ ${retCode} -ne 0 ]; then Error 131 <<< "ERROR: Uzivatel $CSV_AccountId neexistuje."; fi list=$(setAttributes) 90 Příklad univerzálního skriptu Debug <<< "updateUser list=${list}" Debug <<< "updateUser CSV_Disabled=${CSV_Disabled}" # Meni se nektere hodnoty atributu? if [ "x$list" != "x" ]; then IFS="" param="edit -t user $CSV_AccountId set $list" # Spusteni prikazu a kontrola, zda byl uzivatelsky ucet skutecne zmenen. read output < <(runCmd $param) text=`echo $output | sed 's/[0-9][0-9]*//'` # Pokud nebyl uzivatelsky ucet zmenen, tak vypiseme odpovidajici chybu. if [ "$text" != "# User updated." ]; then Error 132 <<< "ERROR: $output" fi fi if [ "x$CSV_Disabled" == "x1" ]; then # Zamkneme ucet x=$(disableUser $CSV_AccountId) elif [ "x$CSV_Disabled" == "x0" ]; then # Odemkneme ucet x=$(enableUser $CSV_AccountId) fi echo "$atAccountId" echo "$CSV_AccountId" } # Zablokovava uzivatelsky ucet pro dane user id. # STATUS: OK disableUser() { if [ "x$1" != "x" ]; then val=$1 elif [ "x${CSV_AccountId}" != "x" ]; then val=$CSV_AccountId else Error 140 <<< "ERROR: Nebylo zadano accountId." fi # Parse user id. Potrebujeme pouze ciselnou hodnotu. id=`echo $val | sed 's/user\///'` runMySQLQuery "UPDATE Principals SET Disabled=1 WHERE id=$id;" } # Odblokovava uzivatelsky ucet pro dane user id. # STATUS: OK enableUser() { if [ "x$1" != "x" ]; then val=$1 elif [ "x${CSV_AccountId}" != "x" ]; then val=$CSV_AccountId else Error 150 <<< "ERROR: Nebylo zadano accountId." fi # Parse user id. Potrebujeme pouze ciselnou hodnotu. id=`echo $val | sed 's/user\///'` runMySQLQuery "UPDATE Principals SET Disabled=0 WHERE id=$id;" 91 Kapitola 11. Identity Connectors } deleteUser() { #Error 160 <<< "ERROR: Nepodporovana operace." CSV_Disabled="1" CSV_Name="$(date +%s)_USER_DELETED_${CSV_AccountId}" CSV_EmailAddress="$(date +%s)_USER_DELETED_${CSV_AccountId}" updateUser } Debug <<< "----- $(date) -----" zpracovatVstup spustitAkci "${IDM_Prikaz}" Debug <<< "***** $(date) *****" Příklad 11.7. Univerzální skript pro všechny operace SSH konektoru 92 Typické procesy správy životního cyklu identit Tato kapitola pojednává o typických procesech správy identit, které zajišťuje CzechIdM. Jednotlivé procesy lze brát jako standardy při návrhu procesů pro nové zákazníky. Všechny níže popsané procesy byly již implementovány v rámci předchozích projektů. Na následující obrázku je diagram popisující vztahy mezi jednotlivými procesy. Stereotyp "CALL" označuje vazbu, kde jeden proces (vychází z něho šipka) volá ve svém těle jiný proces (ten, ke kterému směřuje šipka). 12.1. Závislosti mezi procesy 93 Kapitola 12. Typické procesy správy životního cyklu identit 12.2. Proces "Příchod zaměstnance" 12.2.1. Souhrn Garant procesu Personalista, vedoucí. Základní povinnosti garantů procesu Personalista: Zavedení nového zaměstnance do personálního systému. Vedoucí: Vybrání rolí. Vymezení platnosti procesu Všechny osoby typu zaměstnanec. Hlavní vstupy Informace v personálním systému; vybrané role vedoucím. Hlavní výstupy Založení účtů na systémech připojených k CzechIdM. Založení identity zaměstnance v CzechIdM. Zdroje informací Personalista, vedoucí. Měřitelná kritéria Rychlost založení účtů na připojených systémech. Tabulka 12.1. Příchod zaměstnance 12.2.2. Popis procesu "Příchod zaměstnance" Proces je spouštěn v rámci synchronizace a je iniciován novým záznamem v personálním systému. První akcí je načtení dat tohoto záznamu z personalistiky do IdM. Dle organizačního zařazení zaměstnance se mu přidělí výchozí role, které se přidělují automaticky bez schvalování nebo potvrzování vedoucím. Pokud čas nástupu nastal již v minulosti, tak se ihned vytvoří aktivovaná uživatelská identita v IdM (zároveň s účty na koncových systémech definovaných výchozími rolemi). Jestliže je nástup až v budoucnosti, tak se také vytvoří identita a výchozí uživatelské účty, ale zablokují se. V obou případech se dále vygeneruje uživatelův email, který se zapíše do personálního systému (v personálním systému je zablokována možnost email měnit). Dále se vygeneruje informativní email, který obsahuje informace o přidělených oprávněních na konkrétní koncové systémy, vygenerovaná výchozí hesla, případně další informativní údaje potřebné pro nově příchozího zaměstnance. Tento email se poté odešle vedoucímu zaměstnance (pokud má zaměstnanec přiřazeno více pracovních pozic, tak se odešle vedoucímu hlavní pracovní role, tj. s nejnižším pořadovým číslem). Dále jsou všichni vedoucí nového zaměstnance vyzváni pro přidělení případných dalších rolí. To je již však součástí navazujícího procesu "Přidat oprávnění uživateli". Tento proces může být také spuštěn z procesu "Změna popisných dat uživatele", a to v případě nástupu zaměstnance, který již ve společnosti dříve pracoval. Záznamy o zaměstnancích v personálním systému totiž zůstávají i po jejich odchodu ze společnosti (u záznamu je pouze nastaven příznak "aktivni" na FALSE). V případě opětovného nástupu zaměstnance se tedy v personálním systému nevytváří nový záznam pro zaměstnance, ale nastaví se pouze u stávajícího záznamu příznak "aktivni" na TRUE. Stejně jako personální systém, tak i CzechIdM nemaže po odchodu zaměstnance jeho identitu. V případě opětovného nástupu zaměstnance tedy CzechIdM při synchronizaci dle příznaku "aktivni" zjistí, že nastupuje zaměstnanec, který již ve společnosti dříve pracoval, a aktivuje příslušnou identitu (pokud příslušná identita neexistuje, tak vytvoří novou). Ostatní kroky procesu "Příchod zaměstnance" jsou již stejné jako při standardním nástupu nového zaměstnance. Jen není potřeba generovat pro zaměstnance emailovou adresu, protože ta je již byla vygenerována při jeho prvním nástupu. 94 Diagram 12.2.3. Diagram 95 Kapitola 12. Typické procesy správy životního cyklu identit 12.3. Proces "Přidat oprávnění uživateli" 12.3.1. Souhrn Garant procesu Vedoucí, admin IdM, help-desk, schvalovatel. Základní povinnosti garantů procesu Vedoucí, admin IdM, help-desk: Vybrat uživatele (případně jeho pracovní pozici), vybrat mu role a potvrdit přidělení rolí. Schvalovatel: Schválení požadované role (v případě, že má daná role schvalovatele). Vymezení platnosti procesu Všichni uživatelé (zaměstnanci i externisté). Hlavní vstupy Ruční vyžádání role pro daného uživatele. Hlavní výstupy Přiřazení rolí uživateli (přiřazení oprávnění). Zdroje informací Vedoucí, admin IdM, help-desk, schvalovatel. Měřitelná kritéria Rychlost založení účtů na koncových zařízeních definovaných požadovanými rolemi. Tabulka 12.2. Přidat oprávnění uživateli 12.3.2. Popis procesu "Přidat oprávnění uživateli" Tento proces popisuje způsob, jakým budou uživatelům přidělovány role definující oprávnění na koncové systémy a na adresáře souborového systému. Přidělení oprávnění mohou pro uživatele požadovat jeho vedoucí, pracovníci help-desku, administrátoři IdM a dokonce sám uživatel si může požádat o přidělení oprávnění. Pokud oprávnění pro zaměstnance požaduje někdo jiný než jeho vedoucí, tak je nutné, aby žádost o přiřazení oprávnění vedoucí potvrdil. Oprávnění se přidělují na úrovni pracovních pozic zaměstnance. Při žádosti se tedy musí vybrat pracovní pozice zaměstnance, pro kterou se žádá o dané oprávnění. Tím se určí odpovídající vedoucí, na kterého jde potvrzení pro přidělení oprávnění (dle příslušnosti pracovní pozice do organizačního subjektu). To je nutné z toho důvodu, že zaměstnanec může mít obecně více pracovních pozic. Na rozdíl od zaměstnanců, externisté nemají pracovní pozice. Nemají tedy ani vedoucí. Proto u externistů potvrzuje žádosti administrátoři IdM. Pokud má navíc role definující dané oprávnění přiřazeného schvalovatele (může jich mít i více), tak je nutné schválení alespoň jednoho ze schvalovatelů. Po schválení role schvalovatelem je již možné přiřadit uživateli (ať již zaměstnanci či externistovi) dané oprávnění. O přidělení oprávnění je uživatel notifikován emailem. Pokud je daným uživatelem osoba typu zaměstnance, tak je o přiřazení oprávnění zaměstnanci notifikován vedoucí dané pracovní pozice, pro kterou je oprávnění přiřazeno. V případě externistů jsou notifikováni administrátoři IdM. Tento proces může být také spuštěn automaticky z procesů "Příchod zaměstnance" a "Změna pracovní pozice". V tomto případě se přeskakuje fáze vybírání identity (případně pracovní pozice), ale rovnou se přistupuje k výběru rolí pro přidání oprávnění. 96 Diagram 12.3.3. Diagram 12.4. Proces "Odebrat oprávnění uživateli" 12.4.1. Souhrn Garant procesu Vedoucí, admin IdM, help-desk. Základní povinnosti garantů procesu Vedoucí, admin IdM, help-desk: Vybrat uživatele (případně jeho pracovní pozici), vybrat role pro odstranění, potvrdit odstranění rolí. Vymezení platnosti procesu Všichni uživatelé (zaměstnanci i externisté). Hlavní vstupy Vybrané role pro odstranění u daného uživatele. 97 Kapitola 12. Typické procesy správy životního cyklu identit Hlavní výstupy Odebrané role u uživatele (odebraná oprávnění). Zdroje informací Vedoucí, admin IdM, help-desk. Měřitelná kritéria Rychlost odebrání oprávnění na příslušných účtech koncových systémů (případně zrušení daných účtů). Tabulka 12.3. Odebrat oprávnění uživateli 12.4.2. Popis procesu "Odebrat oprávnění uživateli" Tento proces popisuje odebírání oprávnění uživatelům. Jak již bylo řečeno, oprávnění na koncové systémy a na adresáře souborového serveru jsou reprezentovány pomocí rolí. Pro odebrání přiděleného oprávnění uživateli je potřeba mu odebrat příslušnou roli. Odebírat oprávnění (role) mohou vedoucí (pouze pro své podřízené), pracovníci help-desku (pro všechny uživatele) a administrátoři IdM. Prvním krokem procesu je výběr identity uživatele, kterému se mají odstranit některé role. Pokud se jedná o zaměstnance, tak se dále vybere pracovní pozice, pro kterou se mají odstranit některé role. V případě externistů se pokračuje dále (žádné pracovní pozice nemají). Následuje výběr rolí, které se mají odstranit, a potvrzení jejich odstranění. Poté se role již odstraní. Pokud se odstraní poslední role definující přístup na některý z koncových systémů, tak se odstraní i uživatelův účet na daném systému. Posledním krokem je notifikace uživatele o odstranění některých práv. V případě, že daným uživatelem je osoba typy zaměstnanec, tak je notifikován vedoucí pracovní pozice, pro kterou jsou odebírána oprávnění. Pokud je uživatelem externista, tak jsou notifikováni administrátoři IdM. I tento proces může být spuštěn z jiného procesu, konkrétně z procesu "Změna pracovní pozice". V tom případě se přeskakují fáze, ve které se vybírá identita uživatele (případně i pracovní pozice). Pokračuje se tedy výběrem rolí k odstranění. 98 Diagram 12.4.3. Diagram 12.5. Proces "Povolení systémů" 12.5.1. Souhrn Garant procesu Vedoucí, administrátor IdM, help-desk. Základní povinnosti garantů procesu Vedoucí, administrátor IdM, help-desk: Vybrání zablokované identity uživatele a potvrzení jejího odblokování (spolu se všemi zablokovanými účty uživatele na koncových systémech). Vymezení platnosti procesu Všichni uživatelé (zaměstnanci i externisté). Hlavní vstupy Vybraná zablokovaná identita uživatele. Hlavní výstupy Odblokování identity uživatele a odblokování všech účtů uživatele na koncových systémech. Zdroje informací Vedoucí, administrátor IdM, help-desk. Měřitelná kritéria Rychlost odblokování identity a všech účtů uživatele na koncových systémech. Tabulka 12.4. Povolení systémů 99 Kapitola 12. Typické procesy správy životního cyklu identit 12.5.2. Popis procesu "Povolení systémů" Tento proces slouží k odblokování zablokované identity uživatele a k odblokování účtů uživatele na koncových systémech připojených k CzechIdM. Odblokovat zablokované účty mohou pouze vedoucí (mohou odblokovat pouze účty svých podřízených), pracovníci help-desku a administrátoři IdM. Pracovníci help-desku a administrátoři IdM mohou odblokovat účty libovolnému uživateli. Po odblokování účtů a identity je daný uživatel o této skutečnosti notifikován. V případě, že daným uživatelem je zaměstnanec, tak jsou notifikováni všichni jeho vedoucí. V případě externisty jsou notifikováni administrátoři IdM. Tento proces může být inicializován také z procesů "Provedení časované události" a "Vyjmutí z karantény". Z procesu "Provedení časované události" se volá v tom případě, když nastalo datum nástupu zaměstnance. Datum nástupu zaměstnance může být totiž nastaveno na budoucí datum. V tom případě se v rámci procesu "Příchod zaměstnance" vytvoří zaměstnancova identita a účty na systémech definovaných výchozími rolemi a rolemi specifikovanými vedoucím (vedoucími) daného zaměstnance. Identita a účty se však vytvoří zablokované. Zároveň se také vytvoří časovač, který expiruje k datu nástupu daného zaměstnance. Když nastane datum nástupu zaměstnance (expirace časovače), tak proces "Provedení časované události" odblokuje identitu a všechny zaměstnancovy účty právě pomocí procesu "Povolení systémů". Z procesu "Vyjmutí z karantény" je tento proces volán proto, aby se opětovně aktivovaly přístupy k účtům a k identitě zaměstnance, jehož účty byly v karanténě. Proces "Vyjmutí z karantény" se volá například v tom případě, když účty zaměstnance jsou již v karanténě a zároveň se posune datum odchodu zaměstnance na budoucí datum. 12.5.3. Diagram 100 Proces "Zakázání systémů" 12.6. Proces "Zakázání systémů" 12.6.1. Souhrn Garant procesu Vedoucí, administrátor IdM, help-desk. Základní povinnosti garantů procesu Vedoucí, administrátor IdM, help-desk: Vybrání identity uživatele a potvrzení její zablokování (spolu se všemi účty uživatele na koncových systémech). Vymezení platnosti procesu Všichni uživatelé (zaměstnanci i externisté). Hlavní vstupy Vybraná identita uživatele. Hlavní výstupy Zablokování identity uživatele a všech jeho účtů na koncových systémech. Zdroje informací Vedoucí, administrátor IdM, help-desk. Měřitelná kritéria Rychlost zablokování identity a všech účtů uživatele na koncových systémech. Tabulka 12.5. Zakázání systémů 12.6.2. Popis procesu "Zakázání systémů" Proces "Zakázání systémů" slouží k zablokování identity a účtů na koncových systémech pro daného uživatele. Zablokovat identitu uživatele a jeho účty na koncových systémech mohou vedoucí (pouze u svých podřízených), pracovníci help-desku (u všech uživatelů) a administrátoři CzechIdM. O zablokování identity a účtů jsou v případě zaměstnance notifikováni jeho vedoucí, v případě externistů jsou notifikováni administrátoři IdM. Tento proces může být spuštěn také z procesů "Vyjmutí z evidenčního počtu" a "Ukončení PPV". 101 Kapitola 12. Typické procesy správy životního cyklu identit 12.6.3. Diagram 12.7. Proces "Změna popisných dat uživatele" 12.7.1. Souhrn Garant procesu Personalista, vedoucí, help-desk, administrátor IdM. Základní povinnosti garantů procesu Personalista: Aktualizace údajů zaměstnance v personálním systému. Vedoucí, help-desk, administrátor IdM: Ruční aktualizace popisných dat v CzechIdM. Vymezení platnosti procesu Všichni uživatelé (zaměstnanci i externisté). Hlavní vstupy Změněný záznam zaměstnance v personálním systému. Změněná data u uživatele přímo v CzechIdM. Hlavní výstupy Aktualizace identity uživatele a propagace změněných hodnot na účty uživatele na koncových systémech. Zdroje informací Personální systém, vedoucí, help-desk, administrátor IdM. Měřitelná kritéria Rychlost propagace změněných atributů na koncové systémy, případně rychlost založení účtů na koncových systémech. Tabulka 12.6. Změna popisných dat uživatele 102 Popis procesu "Změna popisných dat uživatele" 12.7.2. Popis procesu "Změna popisných dat uživatele" Proces je spouštěn během synchronizace v okamžiku, kdy CzechIdM zjistí změnu záznamu v personálním systému oproti repository CzechIdM. Změny mohou být také prováděny přímo v CzechIdM. Podle druhu provedené změny se potom spustí odpovídající proces. Pokud dojde u záznamu zaměstnance v personálním systému ke změně příznaku "aktivni" z FALSE na TRUE, tak se spustí proces "Příchod zaměstnance". Tato situace nastane, pokud do společnosti nastupuje zaměstnanec, který zde již pracoval. Záznamy v personálním systému se totiž nemažou, takže při opětovném nástupu se v personálním systému nevytváří nový záznam zaměstnance. Pouze se záznam zaměstnance aktivuje nastavením příznaku "aktivni" na TRUE. Pokud neexistuje odpovídající identita v CzechIdM, tak ji proces "Příchod zaměstnance" vytvoří. Dále proces "Příchod zaměstnance" pokračuje standardně, až na to, že se negeneruje emailová adresa (ta je již v personalistice uložena z předchozího pracovního poměru). V případě změny obyčejných popisných atributů je spuštěn proces "Změna uživatelských atributů". Tento proces nastavuje atributům v CzechIdM nové hodnoty a zajišťuje propagaci nových hodnot na účty uživatele na koncových systémech. Pokud navíc dojde ke změně příjmení, tak se vygeneruje emailový alias, který bude přesměrovávat poštu do původní schránky. Pokud došlo v personálním systému ke změně u pracovních pozic, tak je automaticky spuštěn proces "Změna pracovní pozice". Změnou může být přidání pracovní pozice zaměstnanci nebo odebrání pracovní pozice zaměstnanci. Dalším možným procesem spustitelným z tohoto procesu je proces "Vyjmutí z evidenčního počtu". Ten je spuštěn v tom případě, kdy u záznamu zaměstnance v personálním systému je nastaven příznak "vyjmuti_z_evidencniho_poctu" na TRUE. Z tohoto procesu může být spuštěn také proces "Vyjmutí z karantény". Tento proces se spouští v tom případě, kdy zaměstnancova identita a jeho účty jsou v karanténě a zároveň se mu posune datum odchodu na budoucí datum. V tom případě je potřeba zablokované účty a identitu do daného data povolit, a právě k tomu slouží proces "Vyjmutí z karantény". Posledním procesem, který může být spuštěn z procesu "Změna popisných dat uživatele" je proces "Ukončení PPV". Ten slouží k nastavení karantény na zaměstnancovu identity a účty na koncových systémech. Pokud bude nastaveno datum odchodu zaměstnance na aktuální datum nebo datum v minulosti, tak se nebude vytvářet časovač, který by spustil proces "Ukončení PPV" k datu odchodu zaměstnance, ale proces "Ukončení PPV" se spustí rovnou z tohoto procesu. Hlavní výhodou je to, že se identita a účty dostanou do karantény rychleji (není potřeba vytvářet časovač). 103 Kapitola 12. Typické procesy správy životního cyklu identit 12.7.3. Diagram 12.8. Proces "Změna uživatelských atributů" 12.8.1. Souhrn Garant procesu Personalista, help-desk, administrátor IdM. Základní povinnosti garantů procesu Personalista: Změna uživatelských atributů v personálním systému. Help-desk: Změna hesla 104 Popis procesu "Změna uživatelských atributů" uživatele v CzechIdM. Administrátor IdM: Změna atributů přímo v CzechIdM. Vymezení platnosti procesu Všichni uživatelé (zaměstnanci i externisté). Hlavní vstupy Změna uživatelských atributů oproti aktuálnímu stavu. Hlavní výstupy Aktualizované atributy u identity a u účtů na koncových systémech. Zdroje informací Personální systém, help-desk a administrátor IdM. Měřitelná kritéria Rychlost propagace změněných atributů na účty na koncových systémech. Tabulka 12.7. Změna uživatelských atributů 12.8.2. Popis procesu "Změna uživatelských atributů" Tento proces provádí změnu atributů u identity uživatele dle aktuálních dat v personálním systému. Změny mohou být také provedeny ručně v CzechIdM. Tento proces potom nastaví odpovídajícím atributům nové hodnoty a ty také propaguje na uživatelovy účty na koncových systémech. 12.8.3. Diagram 12.9. Proces "Změna pracovní pozice" 12.9.1. Souhrn Garant procesu Personalista, vedoucí. Základní povinnosti garantů procesu Personalista: Přidat nebo odebrat pracovní pozici zaměstnanci. Přesunout org. subjekt s danou pracovní pozicí. Vedoucí: Přiřazení případných dalších rolí při přidání pracovní pozice. Vybrání a odstranění rolí při odstranění pracovní pozice. 105 Kapitola 12. Typické procesy správy životního cyklu identit Vymezení platnosti procesu Všechny osoby typu zaměstnanec. Hlavní vstupy Informace v personálním systému. Vedoucím vybrané role. Hlavní výstupy Aktualizace pracovních pozic zaměstnance. Zdroje informací Personalista, vedoucí. Měřitelná kritéria Rychlost propagace změněných oprávnění zaměstnance na jeho účty na koncových systémech. Tabulka 12.8. Změna pracovní pozice 12.9.2. Popis procesu "Změna pracovní pozice" Tento proces je spouštěn z procesu "Změna popisných dat uživatele" v případě, že dojde ke změně pracovních pozic u zaměstnance. Tento proces provedené změny vyhodnotí a propaguje dále. Pokud je zaměstnanci přiřazena další pracovní pozice, tak mu jsou automaticky přidělena výchozí práva a automaticky se mu vytvoří účty na koncových systémech, kam má nově dle oprávnění přístup. Dále je spuštěn proces "Přidat oprávnění uživateli", aby mohl vedoucí danému zaměstnanci přiřadit další případná oprávnění. Pokud je zaměstnanci odebrána pracovní pozice, tak se spustí proces "Odebrat oprávnění uživateli", kde vedoucí dané pracovní pozice (dle org. subjektu) specifikuje oprávnění (role), které se mají zaměstnanci odebrat. Poslední možností je přesun pracovní pozice. To je situace, kdy je v personálním systému přesunut organizační subjekt, který obsahuje danou pracovní pozici. V tom případě se spustí proces "Přesun pracovní pozice". 106 Diagram 12.9.3. Diagram 12.10. Proces "Vyjmutí z evidenčního počtu" 12.10.1. Souhrn Garant procesu Vedoucí hlavní pracovní pozice. Základní povinnosti garantů procesu Vedoucí hlavní pracovní pozice: Vymezení platnosti procesu Všechny osoby typu zaměstnanec. Hlavní vstupy Informace v personálním systému; Vedoucím nebo administrátorem IdM vybrané role. Hlavní výstupy Aktualizace účtů na systémech připojených k IdM. Zdroje informací Personální systém, vedoucí a admin IdMs. Měřitelná kritéria Rychlost aktualizace účtů na připojených systémech. Případně rychlost založení či zrušení účtů. Tabulka 12.9. Vyjmutí z evidenčního počtu 12.10.2. Popis procesu "Vyjmutí z evidenčního počtu" Tento proces je spouštěn pouze z procesu "Změna popisných dat uživatele". Slouží k tomu, aby se zablokovaly účty zaměstnance, pokud má být zaměstnanec delší dobu nepřítomen, například z 107 Kapitola 12. Typické procesy správy životního cyklu identit důvodu dlouhodobé nemoci či mateřské dovolené. V personálním systému personalista nastaví u záznamu daného zaměstnance příznak "vyjmuti_z_evidencniho_poctu" na TRUE. Při synchronizaci CzechIdM s personálním systémem je tato změna zjištěna v rámci procesu "Změna popisných dat uživatele", kde dojde ke spuštění procesu "Vyjmutí z evidenčního počtu". Tento proces vyhledá všechny role definující oprávnění zaměstnance na účty na koncových systémech. Vedoucí hlavní pracovní pozice poté rozhodne, které účty se mají pomocí procesu "Zakázání systémů" zablokovat. Nakonec jsou vedoucí všech pracovních pozic zaměstnance notifikováni o zablokování daných účtů zaměstnance. Při návratu zaměstnance do společnosti je potřeba v personálním systému zrušit příznak "vyjmuti_z_evidencniho_poctu" a povolit zaměstnanci zablokované účty. 12.10.3. Diagram 12.11. Proces "Přesun pracovní pozice" 12.11.1. Souhrn Garant procesu Vedoucí. Základní povinnosti garantů procesu Vedoucí: Výběr rolí, které se odstraní u přesouvané pracovní pozice. Vymezení platnosti procesu Všichni zaměstnanci. Hlavní vstupy Informace v personálním systému o přesouvané pracovní pozici. Vedoucím vybrané role pro odstranění u dané pracovní pozice. Hlavní výstupy Aktualizovaná oprávnění zaměstnance, jenž má přidělenou danou pracovní pozici. 108 Popis procesu "Přesun pracovní pozice" Zdroje informací Personální systém, vedoucí. Měřitelná kritéria Rychlost změny oprávnění u dané pracovní pozice. Tabulka 12.10. Přesun pracovní pozice 12.11.2. Popis procesu "Přesun pracovní pozice" Tento proces je volán z procesu "Změna pracovní pozice". Nejdříve se určí role, které se dané pracovní pozici přiřadí automaticky. Role, které přesunem organizačního subjektu přestanou u dané pozice platit, nemohou být přímo odstraněny. Jejich odstranění musí být potvrzeno vedoucím přesouvaného org. subjektu (vedoucím dané pracovní pozice). Vedoucí tedy vybere role, které se u dané pracovní pozice odstraní a potvrdí jejich smazání. Po odstranění vybraných rolí se propagují změny na koncové systémy daného zaměstnance. Nakonec je příslušný zaměstnanec a jeho vedoucí notifikován o změně organizačního subjektu a přidělených práv. 12.11.3. Diagram 109 Kapitola 12. Typické procesy správy životního cyklu identit 12.12. Proces "Provedení časované události" 12.12.1. Souhrn Garant procesu Žádný. Základní povinnosti garantů procesu Nic. Vymezení platnosti procesu Tento proces pracuje pouze s uživateli typu zaměstnanec. Hlavní vstupy Vypršená časovaná událost. Hlavní výstupy Spuštění odpovídajícího navazujícího procesu. Zdroje informací Personální systém. Měřitelná kritéria Rychlost spuštění navazujícího procesu. Tabulka 12.11. Provedení časované události 12.12.2. Popis procesu "Provedení časované události" Jedná se o interní proces systému CzechIdM. Tento proces reaguje na nastalou časovanou událost. Nejdříve zjistí, jaká událost nastala. V úvahu připadají 3 možnosti: • Nastalo datum ukončení PPV pro některého ze zaměstnanců - v tomto případě je spuštěn proces "Ukončení PPV", jehož úkolem je dále nastavení karantény na účty odcházejícího zaměstnance atd. • Vypršela karanténa na identitu - v případě, že časovaná událost značí to, že vypršela karanténa zaměstnanecké identity, tak se dále spustí proces "Smazání identity". Ten smaže identity zaměstnance a všechny jeho účty na koncových systémech. • Nastalo datum nástupu - v případě, že nastalo datum nástupu zaměstnance, tak se spustí proces "Povolení systémů", který odblokuje již vytvořené účty pro daného zaměstnance (účty se vytváří již při příchodu zaměstnance do společnosti). 110 Diagram 12.12.3. Diagram 12.13. Proces "Ukončení PPV" 12.13.1. Souhrn Garant procesu Žádný. Základní povinnosti garantů procesu Nic. Vymezení platnosti procesu Všechny osoby typu zaměstnanec. Hlavní vstupy Informace v personálním systému. Datum ukončení PPV u daného zaměstnance. Hlavní výstupy Zablokování identity a všech účtů zaměstnance. Zdroje informací Personální systém. Měřitelná kritéria Rychlost zablokování všech účtů zaměstnance. Tabulka 12.12. Ukončení PPV 12.13.2. Popis procesu "Ukončení PPV" Tento proces se vždy spouští pouze z procesů "Provedení časované události" nebo "Změna popisných dat uživatele", a týká se pouze osob typu zaměstnanec. Žádný z uživatelů CzechIdM nemůže tento proces inicializovat přímo. Nejdříve se nastaví příznak vložení do karantény u identity daného zaměstnance a poté se zablokují všechny zaměstnancovi účty na koncových systémech. Závěrem se všem vedoucím daného zaměstnance odešle informativní email s informacemi o tom, že zaměstnancovi účty byly zablokovány. 111 Kapitola 12. Typické procesy správy životního cyklu identit 12.13.3. Diagram 12.14. Proces "Smazání uživatele" 12.14.1. Souhrn Garant procesu Administrátor IdM. Základní povinnosti garantů procesu Administrátor IdM: Vybrání uživatele, jehož identita a účty na koncových systémech budou smazány. Vymezení platnosti procesu Všichni uživatelé (zaměstnanci i externisté). Hlavní vstupy Výběr uživatele pro smazání. Hlavní výstupy Smazána identita uživatele, smazány účty uživatele na koncových systémech. Zdroje informací Personální systém, administrátor IdM. Měřitelná kritéria Rychlost smazání identity a účtů na koncových systémech. Tabulka 12.13. Smazání uživatele 12.14.2. Popis procesu "Smazání uživatele" Tento proces může být spuštěn v rámci procesu "Provedení časované události" nebo přímou akcí administrátora IdM v CzechIdM. Prvním krokem je vybrání uživatele, jehož identita a účty se mají smazat. Pokud je tento proces spouštěn z procesu "Provedení časované události", tak se tento krok přeskakuje, tj. uživatel je vybrán již dříve. V následujícím kroku se vyhledají všechny uživatelovi role, které mu definují účty na koncových systémech. Dále se všechny tyto účty smažou a spolu s nimi i identita uživatele v CzechIdM. Smazání účtů uživatele je trvalé, tj.bez použití zálohy není možné obnovit stav před smazáním. U identit uživatelů je však situace jiná. Identity se mažou pouze příznakem (označení, že identita je smazána, ale ve skutečnosti je stále v repository CzechIdM). To je nutné z důvodu možného nástupu zaměstnance, který již ve společnosti dříve pracoval. V tomto případě se nebude vytvářet nová identita, ale aktivuje se ta dřívější (zaměstnanec tedy bude mít stejný 112 Diagram email, co měl dříve), viz. proces "Příchod zaměstnance". Posledním krokem je notifikace o smazání identity a účtů daného uživatele. V případě, že uživatelem je zaměstnanec, tak jsou notifikováni jeho vedoucí, v případě externistů jsou notifikováni administrátoři CzechIdM. 12.14.3. Diagram 12.15. Proces "Vyjmutí z karantény" 12.15.1. Souhrn Garant procesu Žádný. Základní povinnosti garantů procesu Nic. Vymezení platnosti procesu Všechny osoby typu zaměstnanec. Hlavní vstupy Vybraná identita v karanténě, odstraněno (posunuto) datum ukončení PPV. Hlavní výstupy Odblokování identity a účtů zaměstnance. Zdroje informací Personální systém. Měřitelná kritéria Rychlost odblokování všech účtů zaměstnance a jeho identity v CzechIdM. Tabulka 12.14. Vyjmutí z karantény 113 Kapitola 12. Typické procesy správy životního cyklu identit 12.15.2. Popis procesu "Vyjmutí z karantény" Proces "Vyjmutí z karantény" může být spuštěn pouze z procesu "Změna popisných dat uživatele", a to v tom případě, když je zaměstnancova identita v karanténě a zároveň je odstraněno (nebo posunuto do budoucna) datum odchodu zaměstnance. V tom případě se odblokuje identita zaměstnance a všechny jeho účty na koncových systémech. Poté jsou o vyjmutí zaměstnance z karantény notifikováni vedoucí pracovních pozic zaměstnance. 12.15.3. Diagram 12.16. Obecné řešení karantény Na třídě Data jsou metody quarantineBegin a quarantineEnd. Těmito metodami je možné vložit identitu do karantény nebo ji z karantény vyjmout. Karanténa v tomto případě znamená pouze nastavení příznaku, že je identita v karanténě, a data, kdy byla do karantény vložena. Administrátor může v administrátorském rozhraní nastavit do pravidelně spouštěných úloh workflow quarantine.process. To projde všechny identity v CzechIdM a zjistí, zda nejsou v karanténě déle, než je stanovenž čas. Délka karantény může být stanovena na jiný počet dnů u každé organizace (přes možnost "editovat" v seznamu organizací) Není-li u organizace délka karantény explicitně uvedena, použije se jako defaultní hodnota číslo uvedené v business konstantě DEFAULT_QUARANTINE_LENGTH. 114 Konfigurace Běh CzechIdM je možné konfigurovat nastavením některých parametrů. Jejich nastavení lze provést v konfiguračním souboru META-INF/idm_configuration.properties. V této sekci se podíváme na význam jednotlivých parametrů. 13.1. Přehled konfiguračních parametrů sso_use Parametr sso_use udává, zda má být při přihlašování uživatele do webového rozhraní brán zřetel na případné jednotné přihlášení přes SSO. Nabývá hodnot true a false. Je-li nastaven na true, je při přihlašování nejprve hledán parametr sso_uid v hlavičce HTTP požadavku. sso_uid Název parametru v hlavičce HTTP požadavku. Slouží pro přihlašování pomocí SSO. create_real_account_allowed Zda mají být nové účty skutečně vytvářeny na koncových systémech. Je-li nastaven na false, jsou nově vytvořené účty pouze zapisovány do souboru v adresáři uvedeném v parametru fake_resources_dir. update_real_account_allowed Zda mají být nové účty skutečně aktualizovány na koncových systémech. Je-li nastaven na false, jsou aktualizace účtů pouze zapisovány do souboru v adresáři uvedeném v parametru fake_resources_dir. delete_real_account_allowed Zda mají být nové účty skutečně mazány z koncových systémů. Je-li nastaven na false, jsou mazané účty pouze zapisovány do souboru v adresáři uvedeném v parametru fake_resources_dir. fake_resources_dir Adresář, do něhož jsou zapisovány akce, které by byly provedeny na koncovém systému, je-li některý z parametrů create_real_account_allowed, update_real_account_allowed, delete_real_account_allowed nastaven na false. auth_resource Název koncového systému, vůči němuž má být prováděna autentizace. Jedná se o název systému z pohledu CzechIdM auth_schema Schéma na koncovém systému, vůči němuž má být prováděna autentizace. auth_id_attribute Atribut obsahující identifikátor účtu na koncovém systému, vůči němuž má být prováděna autentizace. 115 Kapitola 13. Konfigurace auth_also_with_repository Atribut říkající, zda se při neúspěšném přihlášení přes koncový systém má CzechIdM pokusit uživatele přihlásit proti své repository. Tabulka 13.1. Konfigurační parametry CzechIdM Properties auth_resource, auth_schema a auth_id_attribute umožňují nastavit systém, vůči němuž má při přihlašování probíhat autentizace. Pokud tyto properties nejsou nastaveny (nebo nejsou nastaveny všechny), probíhá autentizace obvyklým způsobem vůči heši hesla v repository. 116 Webová služba Účelem tohoto modulu je poskytování webové služby pro spouštění workflow v CzechIdM. Webová služba poskytuje metody pro spouštění workflow dle specifikace systému CzechIdM. Název spouštěného workflow a jeho parametry jsou těmto metodám předávány jako jejich parametry. Webová služba umožňuje synchronní i asynchronní spuštění workflow. V případě synchronního spuštění navrátí příslušná metoda odpovídající výstup. Metody implementované webovou službou mohou spouštět pouze autentizovaní uživatelé s požadovaným oprávněním. Prvním krokem je přihlášení klienta. Klient se musí prokázat svým uživatelským jménem a heslem. Teprve přihlášený klient může spouštět workflow. Spustit workflow se však povede pouze těm uživatelům, kteří mají pro tuto akci odpovídající oprávnění. Workflow může být spuštěno dvojím způsobem, a to synchronně a asynchronně. Synchronně spuštěné workflow čeká na dokončení prováděného kódu a následně vrací klientovi výstup. Oproti tomu asynchronní způsob na dokončení workflow nečeká a klienta pouze informuje o případných chybách vzniklých během spuštění workflow. 14.1. Návrhový diagram tříd Stěžejní třídou je třída WFLauncherBean. Jedná se o EJB stateless session beanu, která je zároveň anotována jako webová služba. Tato třída implementuje rozhraní WFLauncherSEI, což je koncové rozhraní dané webové služby, ve kterém jsou deklarovány metody webové služby. Zde konkrétně se jedná o následujících šest metod: • login – metoda k přihlašování klientů k webové službě • logout – metoda k odhlašování klientů • launchWorkflowAsynchronously – metoda pro asynchronní spouštění workflow • launchWorkflowSynchronously – metoda pro synchronní spouštění workflow. • launchWorkflowAsynchronouslyStringVars – alterntivní metoda pro asynchronní spouštění workflow • launchWorkflowSynchronouslyStringVars – alterntivní metoda pro synchronní spouštění workflow 14.1.1. Metoda login Metodě jsou předány jako parametry uživatelské jméno a heslo klienta. Pomocí instance třídy Identity se získá instance třídy Credentials, ve které se nastaví předávané uživatelské jméno a heslo. Dále je potřeba určit, že se klient přihlašuje prostřednictvím webové služby, aby se serverová aplikace nepokoušela po úspěšném přihlášení zobrazit klientovi úvodní WWW obrazovku. Nyní se již na instanci třídy Identity zavolá metoda login, která již provede přihlášení. O úspěšnosti přihlášení je klient informován v návratové hodnotě. 14.1.2. Metoda logout Tato metoda nemá žádné parametry. Po jejím zavolání je nad instancí třídy Identity zavolána metoda logout, která provede odhlášení klienta. O úspěšnosti odhlášení je klient informován v návratové hodnotě této metody. 117 Kapitola 14. Webová služba 14.1.3. Metoda launchWorkflowAsynchronously Parametry této metody jsou název workflow, který má být spuštěn, a jeho případné parametry. Název je metodě předán jako klasický řetězec. Parametrem pro workflow může být libovolný objekt (potomek třídy java.lang.Object). Třída java.lang.Object však nemůže být uvedena jako atribut webové metody, protože to není datový typ podporovaný JAXB. Tento problém je řešen tím způsobem, že se použije uživatelsky definovaná třída InputWS. Tato třída transformuje libovolné objekty do bajtového pole a naopak, přičemž bajtové pole je již podporovaným typem JAXB. Prvním krokem metody je kontrola, zda je klient přihlášen. To se provádí prostřednictvím instance třídy Identity. Následně se kontroluje, zda má přihlášený klient oprávnění pro spouštění workflow. Pokud požadované oprávnění klient má, tak se spustí požadované workflow a případně se mu předají parametry (zrekonstruované z instance třídy InputWS). Jelikož se workflow spouští v této metodě asynchronně, tak metoda skončí, případně předá klientovi prostřednictvím instance třídy OutputWS jako návratovou hodnotu libovolný objekt. Třída OutputWS provádí opět transformace mezi objekty a bajtovým polem, ale na rozdíl od třídy InputWS zapouzdřuje dvě členské proměnné, a to standardní výstup stdOut a chybový výstup errOut. 14.1.4. Metoda launchWorkflowSynchronously Provádění této metody je velmi podobné provádění předchozí metody. Rozdílem zde je hlavně to, že se workflow spouští synchronně. To znamená, že se čeká na jeho dokončení a výstup (ať již standardní, tak i chybový) se předá klientovi opět prostřednictvím instance třídy OutputWS. 14.1.5. Metoda launchWorkflowAsynchronouslyStringVars Tato metoda je velmi podobná metodě launchWorkflowAsynchronously. Liší se pouze ve způusobu předávání parametrů. Tato metoda totiž nepřijímá parametry v podobě instance třídy OutputWS, ale jako obyčejný String. V něm funkce očekává parametry pro workflow uložené v podobě XML. Funkce tento XML String rozparsuje a spustí workflow se správnými parametry. Podoba XML je přesně daná a je potřeba ji dodržet. Každý parametr je reprezentován jedním tagem. Název tagu je název parametru a uvnitř něj je hodnota proměnné (typu String). Dohromady jsou pak parametry obaleny tagem <root></root>. Skrze tuto funkci tedy narozdíl od launchWorkflowAsynchronously nelze poslat jakýkoliv Objekt, ale pouze String. Jiné datové typy je potřeba přeložit na String (na straně klienta) a zpět ve workflow, které o tom musí vědět. Smyslem této alternativní metody je umožnit snadnou komunikaci s webovou službou i klientům napsaným v jiném jazyce než java, kteří nemohou vytvořit instanci třídy InputWS. <root> <userName>FrantaWindows</userName> <resource>VFN AD</resource> <password>noveHeslo</password> <changeInIdM>true</changeInIdM> </root> Příklad 14.1. Příklad správné podoby druhého parametru (typ String) 14.1.6. Metoda launchWorkflowSynchronouslyStringVars Synchronní verze předchozí metody. Rozdíl je analogicky stejný jako mezi launchWorkflowAsynchronously a launchWorkflowSynchronously. 118 Asynchronní volání metod JAX-WS 14.2. Asynchronní volání metod JAX-WS JAX-WS podporuje asynchronní volání metod. To může být provedeno jak pro statický stub (statické klienty), tak i za použití Dispatch API (dynamické proxy). Podporované jsou oba mechanismy získávání návratových hodnot (polling i callback). V případě polling přístupu klient zavolá na serveru asynchronní metodu a server klientovi ihned vrátí tzv. zástupný objekt, přičemž server dále vykonává volanou metodu. Klient se prostřednictvím zástupného objektu dotazuje, zda již server poslal návratovou hodnotu, kterou si poté prostřednictvím tohoto objektu vyzvedne. Vzorový kód pro „polling“ klienta je uveden níže. CreditRatingService svc = ...; Response<Score> response = svc.getCreditScoreAsync(customerFred); while (!response.isDone()) { // do something while we wait } // no cast needed, thanks to generics Score score = response.get(); Příklad 14.2. polling klienta Druhým mechanismem je callback přístup. Zde klient poskytuje handler pro příjetí a následné zpracování návratové hodnoty. Callback mechanismus tedy vyžaduje dodatečné vstup v podobě implementace daného handleru. Tuto implementaci má na starost programátor klientské aplikace. Vzorový kód zahrnující zavolání webové metody a implementaci handleru je uveden níže. CreditRatingService svc = ...; Future<?> invocation = svc.getCreditScoreAsync(customerFred, new AsyncHandler<Score>() { public void handleResponse ( Response<Score> response) { Score score = response.get(); // do work here... } } ); while(!invocation.isDone()){ // do something while we wait } Příklad 14.3. Volání webové metody a implementace handleru 14.3. Zamezení logování WARN [StatelessBeanContext] EJBTHREE-1337 Při volání webových metod se do logu na serveru zapisuje následující varování: 119 Kapitola 14. Webová služba WARN [StatelessBeanContext] EJBTHREE-1337: do not get WebServiceContext property from stateless bean context, it should already have been injected Jedná se o interní chybu aplikačního serveru JBoss. Jde o to, že JBossWS používá zastaralé API, které bylo označeno jako deprecated. Z hlediska koncového vývojáře se nejedná o žádný problém, vše pracuje tak, jak má. Nepříjemností je hlavně to, že toto varování zvyšuje velikost logu, což je nepříjemné a znesnadňuje to jeho případný debugging. Zabránění výpisu tohoto varování je jednoduché (stejně jako u jakéhokoliv jiného). Stačí v souboru $JBOSS_HOME/server/default/conf/ jboss-log4j.xml provést následující změnu. <!-- Pro tridu StatelessBeanContext logovat az uroven ERROR --> <category name="org.jboss.ejb3.stateless.StatelessBeanContext"> <priority value="ERROR" /> </category> Pokud je použita jiná konfigurace než defaultní, je samozřejmě potřeba náležitě upravit cestu ke konfiguračnímu XML souboru. 120 Password filter Password filter neboli filtr hesel je DLL knihovna pro Windows, která umožňuje implementovat libovolnou politiku měnění hesel na operačních systémech Microsoft Windows. Při požadavku na změnu hesla operační systém postupně zavolá všechny nainstalované password filtery (může jich být více) s požadavkem na změnu hesla. Password filter podle politiky, kterou implementuje, rozhodne, jestli změnu schválí nebo ne. Pokud je změna hesla schválena, operační systém změní heslo a znovu zavolá password filter s potvrzením, že došlo ke změněně. My používáme password filter typicky na MS Active Directory pro ověření hesla politikou definovanou v CzechIdM, které password filter zavolá z MSAD prostřednictvím webové služby a následně pro potvrzení změny, po kterém CzechIdM změní hesla daného uživatele ve všech koncových systémech na něj napojených, čímž je realizována synchronizace hesel. Proces synchronizace hesel po krocích: 1. Windows posílá požadavek na změnu hesla do CzechIdM. 2. CzechIdM ověří heslo podle svojí politiky hesel a pošle Windows odpověď. 3. Ve Windows se mění heslo. 4. Windows posílá CzechIdM informaci, že heslo bylo úspěšně změněno. 5. CzechIdM mění hesla ve všech koncových systémech. 15.1. Instalace password filteru na Windows 1. Zkopírujte PasswordFilterCzechIdM.dll a adresář PasswordFilterCzechIdM do systémového adresáře. Při standardní instalaci je to C:/Windows/System32. 2. Otevřete editor registrů (regedit.exe) a najděte klíč HKEY_LOCAL_MACHINE\SYSTEM \CurrentControlSet\Control\Lsa. Otevřete jeho multi-string value “Notification Packages” a doplňte do ní PasswordFilterCzechIdM. (bez koncovky .dll) Stávající hodnoty nemažte. 3. Najděte a otevřete Local Security Policy. (např. Control panel (-> Performance and Maintenance) > Administrative Tools (-> Local Security Policy) ) V Account Policies -> Password Policy nastavte položku “Password must meet complexity requirements” na Enabled. 4. Restartujte systém. 15.2. Konfigurace password filteru Adresář PasswordFilterCzechIdM je úložiště pro konfigurační soubor PasswordFilterCzechIdM.ini, SSL certifikát v PEM formátu a logovací soubor FilterLog.txt. Název atributu Popis url Cílová adresa, na které běží webová služba CzechIdM. cert Cesta k SSL certifikátu pro autentizaci serveru. checkWorkflow Název workflow v CzechIdM pro kontrolu politiky hesel. changeWorkflow Název workflow v CzechIdM pro změnu hesel na koncových systémech. 121 Kapitola 15. Password filter Název atributu Popis resource Identifikátor koncového systému. Nepovinný údaj. CzechIdM s ním může naložit jak chce, například vynechat tento koncový systém ze seznamu systmů, ve kterých změní heslo. changeInIdM Přepínač určující, jestli se má heslo změnit i v CzechIdM. Hodnoty true/ false. loginName Přihlašovací jméno uživatele CzechIdM s oprávněním měnit hesla ostatním uživatelům (admin). loginPassword Heslo uživatele loginName pro přihlášení do CzechIdM přes webovou službu. sslSkipHostCheck Přepínač určující, jestli se má přeskočit kontrola SSL certifikátu serveru poskytujícího webovou službu proti doménovému jménu, pod kterým běží. Tato možnost existuje pro testování protokolu https na localhostu. Hodnoty true/false. Tabulka 15.1. Atributy nastavitelné v konfiguračním souboru 15.3. Vytvoření password filteru password filter je DLL knihovna napsaná v jazyce C++. K dispozici máme projekt pro Microsoft Visual Studio 2010, ve kterém jsou naimplementované potřebné funkce včetně knihoven pro web service komunikaci a SSL. Projekt je připraven k buildu, je však třeba dát pozor na konflikt mezi 32 a 64 bitovým systémem a přeložit projekt pro příslušnou platformu. 15.4. Komunikace password filteru s CzechIdM přes webovou službu Password filter funguje jako klient webové služby. Server je CzechIdM. Ke komunikaci s webovou službou password filter použvá opensource C++ kihovnu gSOAP http:// gsoap2.sourceforge.net/. Tato knihovna je používána pod licencí GNU GPL v2, což znamená, že výsledný software musí být opensource a volně distribuovatelný a modifikovatelný. (toto se týká pouze password filteru, ne CzechIdM) Zdrojové kódy funkcí obstarávajících komunikaci s webovou službou jsou vygenerovány z WSDL souboru popisujícího webovou službu CzechIdM pomocí utilit wsdl2h a soapcpp2, které jsou také součástí gSOAP. Za předpokladu, že se nebude měnit webová služba CzechIdM (nebo alespoň část využívaná password filterem) nebude třeba tento krok opakovat. Kdyby to přece jen potřeba bylo, zde je návod pro windows: 1. Vytvořte si pracovní adresář a zkopírujte do něj soubory wsdl2h.exe, soapcpp2.exe a WSDL popisovač webové služby wsdl.wsdl. wsdl2h.exe a soapcpp2.exe najdete buť v adresáři projektu pod gSoap/bin, nebo si je můžete stáhnout z http://gsoap2.sourceforge.net/ 2. V příkazové řádce (cmd.exe) přejděte do pracovího adresáře a spusťte přkazy: a. wsdl2h.exe -o wsdl.h wsdl.wsdl b. soapcpp.exe -IC:\gSoap\gsoap-2.8\gsoap\import wsdl.h 3. Vygenerované soubory zkopírujte do domovského adresáře projektu password filteru. 122 Synchronizace hesel na straně CzechIdM 4. V hlavičkovém souboru stdsoap2.h doplňte řádek “#define WITH_COOKIES”,pokud tam není. 5. V hlavičkovém souboru stdsoap2.h doplňte řádek “#define WITH_OPENSSL”,pokud tam není. Vygenerované zdrojové kódy jsou následně využívány v projektu password filteru. Vygenerované funkce v nich se odkazují na knihovny z balíčku gSOAP, které je proto potřeba mít k dispozici pro build projektu. (jsou součástí projektu) 15.5. Synchronizace hesel na straně CzechIdM Na straně CzechIdM zajišťují proces synchronizace hesel 2 workflow: ws.password.check a ws.password.change. ws.password.check pouze kontroluje, zda je změna hesla v souladu s politikou hesel, ale nic nemění. Naproti tomu ws.password.change také provede kontrolu politiky a poté i samotnou změnu hesel ve všech koncových systémech. Obě workflow přijímají stejné vstupní argumenty: • userName – název uživatelského účtu ve Windows (obecně jiný než v CzechIdM!) • password – nové heslo • resource – identifikátor koncového systému Windows. Pokud nebudeme připojovat více domén k jednomu IdM, tak se asi obejdeme bez jeho použití. Je tam pro jistotu. • changeInIdM – true/false přepínač, jestli se má změnit heslo i v IdM Pro rychlé nalezení identity podle userName z Windows používame extended atribut, jehož název je pro větší flexibilitu uložen v BusinessConstants pod konstantou WINDOWS_USERNAME_ATTRIBUTE_NAME. Obě workflow vracejí jako výstupní hodnotu true nebo false, podle toho, zda kontrola respektive změna hesla proběhla v pořádku, nebo ne. Všechny proměnné jsou typu String. (včetně true/false, vstupní i výstupní) 15.6. Zabezpečení přes SSL V projektu se využívá knihovna OpenSSL. Autentizace: Server (CzechIdM) se musí autentizovat certifikátem, aby nemohlo dojít ke zfalšování serveru a odeslání hesla falešnému serveru. Naopak klient (Windows) se autentizovat nemusí, protože server neposílá žádné citlivé údaje a navíc přihlásit se do CzechIdM a změnit si heslo stejně může uživatel odkudkoliv skrz webové rozhraní. Na serveru (=CzechIdM) musí být uložen privátní šifrovací klíč. Úložiště může být například $JBOSS_HOME/server/default/conf/ssl. Dále musí být povolen https konektor v konfiguračním souboru $JBOSS_HOME/server/default/deploy/jbossweb.sar/server.xml, ve kterém musí být navíc uvedena cesta ke zmíněnému úložišti a heslo k němu. Příkazy pro vytvoření šifrovacích klíčů (v typickém případě není potřeba klíče vytvářet, protože je dodá klient): $ mkdir -p $JBOSS_HOME/server/default/conf/ssl 123 Kapitola 15. Password filter $ $JAVA_HOME/bin/keytool -genkey -alias czechidm -keyalg RSA -keystore $JBOSS_HOME/server/ default/conf/ssl/czechidm.keystore $ $JAVA_HOME/bin/keytool -export -alias czechidm -keystore $JBOSS_HOME/server/default/conf/ ssl/czechidm.keystore -file $JBOSS_HOME/server/default/conf/ssl/czechidm.cer $ openssl x509 -inform der -in $JBOSS_HOME/server/default/conf/ssl/czechidm.cer -outform pem out $JBOSS_HOME/server/default/conf/ssl/czechidm.pem Příklad 15.1. Vytvoření SSL klíčů 15.7. Rizika a bezpečnostní opatření • Únik citlivých informací: Posílané heslo odchytí třetí osoba (falešný server). Opatření: https protokol s nainstalovaným certifikátem. • V systémovém adresáři Active Directory jsou uloženy přihlašovací údaje administrátorského účtu CzechIdM, SSL certifikát a samotný password filter. Do systémového adresáře proto nesmí mít přístup nepovolané osoby, které by mohly tyto data ukrást nebo zaměnit. • Před ukončením programu smazat(přepsat) paměť obsahující heslo. • Kompatibilita s verzí Windows. Vstupní paramtery (jméno, heslo) jsou typu PUNICODE_STRING, který je definován v hlavičkových souborech Windows (např. C:\Program Files\Microsoft SDKs \Windows\v7.0A\Include\LsaLookup.h) a ty by se správně měly includovat. Jenže se tlučou s vygenerovanými headery gSoapu. Proto je typ PUNICODE_STRING natvrdo definován uvnitř filtru. Neměl by to být problém, ale je lepší o tom vědět a mít tak možnost to dohledat. • Zacyklení: Pokud by CzechIdM ve fázi změny hesel na koncových systémech změnilo heslo i na MS AD, znovu se aktivuje password filter a může vzniknout nekonečný cyklus (pokud mu nezabrání nějaká politika, což by měla). Opatření: AD se při posílání zprávy identifikuje, a CzechIdM ho při měnění hesel vynechá. 15.8. Užitečné informace pro vývoj a testování Vedle samotného Visual Studio projektu PasswordFilterCzechIdM jsou v adresáři czechidm/Ralization/ PasswordFilter ještě Visual Studio projekty gSoapTest a dllExecutor. gSoapTest je konzolová aplikace pro testování komunikace mezi Windows a CzechIdM pomocí C++ knihovny gSoa. dllExecutor je konzolová aplikace, která umí spouštět funkce z dll knihovny, čímž umožňuje testování filtru bez neustálých reinstalací (úprav registrů a restartů). Když Windows volají password filter, vloží za uživatelské jméno jeden unicodový znak (nevím proč). Ten my nechceme a proto password filter poslední znak uřezává. dllExecutor tohle ale nedělá a tak je potřeba při jeho používání počítat se zkrácením zadaného uživatelského jména o 2 znaky. (převod wchar->char) gsoapTest: Funkcím gSoapu je možné zadat v parametru target místo url prázdný řetězec “”. gSoap pak svůj dotaz místo odesílaní vypíše do konzole. Nastavení project properties: Projekty gSoapTest a PasswordFilterCzechIdM musí mít v Configuration Properties -> VC++ Directories -> Include directories nastavené adresáře obsahující zdrojové soubory gSOAPu a statické knihovny OpenSSL. Statické knihovny OpenSSL ssleay32.lib a libeay32.lib je nutné explicitně uvést v Linker->Input->Additional dependencies. Nakonec je zajímavá konvence volání funkcí v C/C++ -> Advanced -> Calling convention. Jde o nastavení způsobu, jakým si funkce mezi sebou předávají parametry a výstupní hodnoty. (pořadí parametrů, místo uložení zásobník/ 124 Užitečné informace pro vývoj a testování registry atd.) Exportované funkce DLL knihovny používají konvenci __stdcall (/Gz), ta ale není kompatibilní s funkcemi ve statických knihovnách OpenSSL, což způsobí chybu při linkování. Proto je potřeba nastavit Calling convention na __cdecl(/Gd). Pak se vnitřní funkce správně přeloží podle __cdecl a exportované podle __stdcall (__stdcall v kódu přepisuje implicitní Calling convention). 125 126 Testování 16.1. Testování kódu CzechIdM je testováno pomocí Unit (jednotkových) testů. Jsou to testy, které testují určitou část kódu (např. metodu), nikoli funkcionalitu. Testy jsou umístěny ve zvláštním projektu. Pro testování je použit framework TestNG (více na www.testng.org), pro analýzu pokrytí testy potom nástroj Eclemma (více na www.eclemma.org), který lze instalovat do Eclipse IDE. Testy se mohou spouštět buď pro každou testovací třídu zvlášť, nebo se spustí pro všechny testovací třídy najednou. Třídy, které se mají být otestovány, jsou uvedené v konfiguračním souboru testng.xml. <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd"> <suite name="Suite" parallel="none"> <test name="All" preserve-order="false"> <classes> <class name="JmenoTridy1Test"/> <class name="JmenoTridyXTest"/> </classes> </test> </suite> Příklad 16.1. Soubor testng.xml Mezi tagy <classes> jsou uvedeny jednotlivé třídy, které definují test. Třídy, které nejsou obsaženy v testng.xml, nebudou otestovány, pře V OrganisationManagementBeanTest je parametr alwaysRun použit, aby se naše vytvořená organizace vždy smazala. Zabráníme tím "zašpinění" databáze daty z předchozích neprošlých testů, která by mohla ovlivnit testy následující. stože se nachází v projektu. Atribut preserve-order určuje, zda je při testování nutné zachovat pořadí testů tak, jak jsou uvedeny v seznamu classes, anebo může být pořadí testů náhodné. Součástí projektů CzechIdM je sada testovacích tříd, které testují jak beany, tak statické metody tříd z IdM. Jsou napsány testy například na OrganisationManagamentBean, RuleManagamentBean, třídu PolicyUtils či jsou otestovány třídy pro zobrazení stránky, které se volají z workflow. Obrázek 16.1. Pokrytí CzechIdm testy Jak začít testovat: • Nejprve musíme zvážit, které metody kterých tříd chceme otestovat. 127 Kapitola 16. Testování • Poté musíme napsat testovací třídu, do ní testovací metody a samotné testy obsahující příkaz assert. • Nakonec spustíme test 16.2. Jak napsat jednoduchý test Pro vytvoření jednoduchého testu stačí vytvořit v testovacím projektu třídu a do ní testovací metody, které mají anotaci @Test. Tyto metody pak testují správné chování testované metody pomocí jednoduchých příkazů assert (například můžeme testovat správnou výstupní hodnotu). Anotace @Test může mít několik parametrů. S jejich pomocí můžeme testovací metody dělit do skupin, vynechávat metody z testování, určovat pořadí, v jakém se provedou či reagovat na výjimky. Třídu bychom neměli zapomenout vložit do konfiguračního souboru testng.xml. public class PolicyUtilTest { /** * Otestuje, že text neobsahuje větší počet písmen, než bylo zadáno. */ @Test public void checkMaxAlpha() { Policy policy = new Policy(); policy.setMaxAlpha(3); Assert.assertTrue(PolicyUtil.isTextValid("58_sd", policy, new Identity(), new ArrayList<byte[]>())); Assert.assertTrue(PolicyUtil.isTextValid("čD", policy, new Identity(), new ArrayList<byte[]>())); Assert.assertTrue(PolicyUtil.isTextValid("3č67979 . § á", policy, new Identity(), new ArrayList<byte[]>())); Assert.assertFalse(PolicyUtil.isTextValid(".:fEa664s", policy, new Identity(), new ArrayList<byte[]>())); } /** * Otestuje správný počet písmen. */ @Test public void getNumberOfAlphaTest() { Assert.assertEquals(PolicyUtil.getNumberOfAlpha("abc"), 3); Assert.assertEquals(PolicyUtil.getNumberOfAlpha("a5B8c"), 3); Assert.assertEquals(PolicyUtil.getNumberOfAlpha("2čžýŽ"), 4); Assert.assertEquals(PolicyUtil.getNumberOfAlpha("0156 4_-)(§"), 0); Assert.assertEquals(PolicyUtil.getNumberOfAlpha("01"), 0); } } Příklad 16.2. Dvě testovací metody z PolicyUtilTest Na příkladu je vidět, jak jednoduše otestovat metody z PolicyUtil a to pomocí metod assert z TestNG. Metod pro testování existuje více a to například assertEquals, která testuje rovnost obou parametrů, assertTrue testuje, zda je výraz roven true či assertNull testuje, zda je objekt roven null. Pokud všechny assert příkazy v testovací metodě projdou, metoda úspěšně prošla testem. Pokud některý z nich neprojde, metoda v testu neobstála. 128 Práce s výjimkami 16.3. Práce s výjimkami V jistých situacích chceme testovat, zda nám testovaná metoda vyhazující výjimku tuto výjimku za dané situace opravdu vyhodí. K anotaci @Test přidáme parametr expectedExceptions, v němž uvedeme třídu očekávané výjimky. public class PolicyUtils { public static void validatePolicy(PolicyView a, PolicyView b) { Integer minValue = null; Integer maxValue = null; String nameA = (String) a.get(PolicyViewHandler.NAME_ATTRIBUTE); String nameB = (String) b.get(PolicyViewHandler.NAME_ATTRIBUTE); minValue = (Integer) a.get(PolicyViewHandler.MIN_ALPHA_ATTRIBUTE); maxValue = (Integer) a.get(PolicyViewHandler.MAX_ALPHA_ATTRIBUTE); validate(minValue, maxValue, nameA, nameB); } private static void validate(Integer min, Integer max, String nameA, String nameB) { if (min == null || max == null) { return; } if (min > max) { throw new IllegalArgumentException(String.format("Could not make intersection from policies: %s and %s", nameA, nameB)); } } } Příklad 16.3. Metoda ze třídy PolicyUtils v jisté situaci skončí výjimkou public class PolicyUtilsTest { @Test(expectedExceptions = IllegalArgumentException.class) public void validatePolicyTest() { PolicyView a = new PolicyView(); PolicyView b = new PolicyView(); a.put(PolicyViewHandler.NAME_ATTRIBUTE , "Nazev1"); b.put(PolicyViewHandler.NAME_ATTRIBUTE , "Nazev2"); a.put(PolicyViewHandler.MIN_ALPHA_ATTRIBUTE, new Integer(1)); a.put(PolicyViewHandler.MAX_ALPHA_ATTRIBUTE, new Integer(10)); a.put(PolicyViewHandler.MIN_LENGTH_ATTRIBUTE, new Integer(2)); a.put(PolicyViewHandler.MAX_LENGTH_ATTRIBUTE, new Integer(3)); a.put(PolicyViewHandler.MIN_LOWERCASE_ATTRIBUTE, new Integer(8)); a.put(PolicyViewHandler.MAX_LOWERCASE_ATTRIBUTE, new Integer(5)); a.put(PolicyViewHandler.MIN_NUMERIC_ATTRIBUTE, new Integer(2)); a.put(PolicyViewHandler.MAX_NUMERIC_ATTRIBUTE, new Integer(6)); a.put(PolicyViewHandler.MIN_SPECIAL_ATTRIBUTE, new Integer(3)); a.put(PolicyViewHandler.MAX_SPECIAL_ATTRIBUTE, new Integer(7)); 129 Kapitola 16. Testování a.put(PolicyViewHandler.MIN_UPPERCASE_ATTRIBUTE, new Integer(1)); a.put(PolicyViewHandler.MAX_UPPERCASE_ATTRIBUTE, new Integer(8)); PolicyUtils.validatePolicy(a, b); } } Příklad 16.4. Testovací třída PolicyUtilsTest pro třídu PolicyUtils obsahuje metodu validatePolicyTest, která očekává výjimku IllegalArgumentException. Metoda validate vyhazuje výjimku v případě, že je min větší než max. Podíváme-li se na naši testovací třídu a na její metodu validatePolicyTest, vidíme, že testuje metodu validatePolicy z PolicyUtils. V určitou chvíli tedy může dojít k výjimce a to se stane ve chvíli, kdy minimální hodnotě přiřadíme číslo větší, než je hodnota maximální. Pro účel testu zadáme právě tyto parametry. Nakonec je zapotřebí přidat do anotace @Test parametr expectedExceptions, v němž uvedeme naši očekávanou výjimku. V našem případě IllegalArgumentException. Kdybychom očekávanou výjimku neuvedli, test by neprošel. 16.4. Když záleží na pořadí V jistých situacích může být výhodné, aby se testovací metody spustily v pevně daném pořadí. Mějme například testovací metodu A, testovací metodu B a testovací metodu C. Předpokládejme, že potřebujeme, aby se testovací metoda B vždy spustila, až skončí testovací metoda A, a aby se spustila testovací metoda C až v okamžiku, kdy skončí testovací metody A a B. K tomu nám slouží parametr dependsOnMethods u anotace @Test, pomocí něhož specifikujeme, na kterých předchozích testech konkrétní test závisí. Metody závislé na pořadí často využijeme u testování takzvaných CRUD ("Create-Retrieve-Update-Delete") operací. Je zjevné, že testovat úspěšné smazání objektu má smysl až poté, co byl objekt úspěšně vytvořen. public class OrganisationManagementBeanTest { @Test public void organisationSimpleCreateAndRead() throws Exception { //Kód testování vytvořené nové organizace //a nastavení potřebných parametrů. } @Test(dependsOnMethods = {"organisationSimpleCreateAndRead"}) public void organisationSimpleUpdate() throws Exception { //Kód testování updatu organizace. } @Test(dependsOnMethods = {"organisationSimpleUpdate"}, alwaysRun = true) public void organisationSimpleDelete() throws Exception { //Kód testování smazání organizace. } } Příklad 16.5. Testovací třída OrganisationManagementBeanTest testuje CRUD operace. Ve třídě OrganisationManagementBeanTest se nacházejí tři metody, které testují CRUD operace. Potřebujeme, aby se nejdříve vytvořila organizace, abychom ji mohli později upravovat a nakonec 130 Psaní testů EJB session bean smazat. Povšimněme si druhého parametru alwaysRun, který můžeme nastavit na true nebo false. Hodnotou true říkáme, že pokud nějaká metoda závisí na metodě, která v testu neobstála, bude se přesto pokračovat v testování. Defaultně je tento parametr nastaven na false. Pokud tedy metoda A, na které testovaná metoda B závisí, testem neprojde, nebude se metoda B testovat. V OrganisationManagementBeanTest je parametr alwaysRun použit, aby se naše vytvořená organizace vždy smazala. Zabráníme tím "zašpinění" databáze daty z předchozích neprošlých testů, která by mohla ovlivnit testy následující. 16.5. Psaní testů EJB session bean Beany se testují specifickým způsobem. Testovací třídu je třeba vytvořit jako potomka třídy SeamTest. Testovací metoda v sobě má příkaz new ComponentTest(), ve kterém je implementován samotný test. Reference na EJB interface je získávána pomocí třídy Component. @Stateless @Name("UserEJB") public class UserEJBBean implements UserEJB { @PersistenceContext private EntityManager em; public boolean insert(String firstname, String lastname) { System.out.println("insert ..."); try { User user = new User(firstname, lastname, 50000); em.persist(user); return true; } catch (Exception e) { log.error("Error - save to DB: " + e); return false; } } } Příklad 16.6. EJB, která se bude testovat. public class UserEJBTest extends SeamTest { @Test public void test_insert() throws Exception { new ComponentTest() { @Override protected void testComponents() throws Exception { UserEJB dao = (UserEJB) Component.getInstance(UserEJBBean.class); boolean result = dao.insert("Jan", "Novak"); assertEquals(true, result); } }.run(); } } Příklad 16.7. Testovací třída. 131 Kapitola 16. Testování 16.6. Psaní testů Seam bean @Name("testSeamBean1") public class SeamTestBeanWithAnotation { private Integer b = 1; public String printHello(){ System.out.println("Hello Seam Bean from component with anotation!"); return null; } public Integer add(int c){ return b + c; } public Integer getB(){ return b; } public void setB(Integer b){ this.b = b; } } Příklad 16.8. Seam bean, která se bude testovat. @Test public void test_beanWithAnotation() throws Exception { new FacesRequest("/userEJB.xhtml") { @Override protected void invokeApplication() { setValue("#{testSeamBean1.b}", new Integer(22)); Integer result = (Integer) invokeMethod("#{testSeamBean1.getB()}"); assertEquals(22, result.intValue()); SeamTestBeanWithAnotation seamTestBean = (SeamTestBeanWithAnotation) Component.getInstance(SeamTestBeanWithAnotation.class); assertEquals(new Integer(22), seamTestBean.getB()); } }.run(); } Příklad 16.9. Testovací metoda. Na začátku je ukázka, jak lze nastavit hodnotu do proměnné. Následuje zavolání metody. Nakonec je možné získat referenci na beanu a dále s ní pracovat. 16.7. Jak testovat workflow CzechIdM obsahuje několik testovacích metod, které testují jednoduchá workflow. Tato worflow mohou obsahovat ActionHandler třídy, např. metodu ShowPageAction, které chceme otestovat. 132 Pokrytí testy @Test public void workflowShowPageTest1() throws Exception { new ComponentTest() { @Override protected void testComponents() throws Exception { InputStream input; ProcessDefinition processDefinition; ProcessInstance processInstance; ContextInstance contextInstance; Assert.assertTrue(TestUtils.login()); try { input = new FileInputStream(WORKFLOW_SHOW_PAGE_TEST_1_PATH); processDefinition = ProcessDefinition.parseXmlInputStream(input); processInstance = new ProcessInstance(processDefinition); contextInstance = processInstance.getContextInstance(); Token token = processInstance.getRootToken(); //Testuje, zda se nachází v počátečním uzlu. Assert.assertEquals("start-state3", token.getNode().getName()); contextInstance.setVariable("userName", USER_NAME_TEST); processInstance.signal(); //Testuje správně nastavenou proměnnou userName, zda View není prázdné a zda je ve stavu showPage Assert.assertEquals("admin", contextInstance.getVariable("userName")); Assert.assertNotNull(contextInstance.getVariable("userView")); Assert.assertEquals("showPage", token.getNode().getName()); processInstance.signal(); //Testuje zda se nachází v koncovém uzlu Assert.assertEquals("end-state1", token.getNode().getName()); } catch (FileNotFoundException e) { e.printStackTrace(); } finally { TestUtils.logout(); } } }.run(); } Příklad 16.10. Metoda testuje workflow. Na začátku naší testovací metody musíme worflow načíst a pak manuálně přecházet mezi stavy a testovat, zda se správně nastavily proměnné a zda jsme ve správném stavu. 16.8. Pokrytí testy Při testování kódu je zapotřebí měřit mimo počet úspěšných testů také to, jaké je pokrytí testy. Pokrytím testy rozumíme poměr mezi počtem řádků otestovaného a neotestovaného kódu. Dobrým nástrojem pro toto měření je Eclemma, jehož prostřednictvím se spustí testování kódu. Jakmile testy proběhnou, spustí nástroj detailní statistiku, z níž lze vyčíst, kolik čeho bylo otestováno a 133 Kapitola 16. Testování jaké je celkové pokrytí. Nástroj nám také vyznačí, které části kódu byly otestovány a které ne. Zelenou barvou se označí řádky, které jsou otestované, červenou jsou neotestované řádky a žlutou barvou jsou řádky, které jsou otestované tak napůl. Například podmínky, kdy jen část je splněná. Cílem je mít co nejvíce zelených řádek a tím tedy vyšší pokrytí testy. Obrázek 16.2. Pokryté části kódu - vyznačeno zelenou barvou Obrázek 16.3. Nepokryté části kódu - vyznačeno červenou barvou 134 Nástroje 17.1. IdM Uploader IdM Uploader je jednoduchá Java aplikace, s jejíž pomocí může vývojář svá nově vytvořená nebo pozměněná workflow, pravidla nebo e-mailové šablony nahrát do repository. S databází komunikuje pomocí JDBC a obsluhuje tabulky "workflows_definitions", "rules_definitions" a "email_templates". Po spuštění projde všechny adresáře na stanovené cestě a nahraje obsah jejich .xml souborů do repository. Cestu, na níž IdM Uploader hledá definice workflow, definice pravidel a e-mailové šablony, lze stanovit v souboru conf.properties v adresáři eu.bcvsolutions.wfuploader. Kromě těchto cest je v souboru možné definovat port, na kterém repository běží, host, databázi, uživatelské jméno a heslo pro přístup a případné další parametry spojení. Před načtením každého workflow, pravidla nebo e-mailové šablony je .xml souboru zpracován pomocí parseru SAX. Jelikož by workflow, pravidla a e-mailové šablony do repository takto vložené neměly coby entity v CzechIdM přiřazen unikátní identifikátor entityId, který v CzechIdM běžně přiřazuje metoda generateEntityIdAndSetOwnerIdToAttributes() na třídě IdmEntity, je entityId generováno ve stejném formátu přímo uploaderem. 17.2. IdMKeyGenerator IdMKeyGenerator je Java aplikace, které slouží ke generování klíčů pro šifrovaná spojení s koncovými systémy. V současnosti je spojení zpravidla realizováno pomocí blokové šifry AES, klíčovou třídou je proto třída AESKeyGenerator, která vytváří klíč pro AES. Kdyby bylo AES v budoucnu nahrazeno jiným kryptografickým standardem, může být IdMKeyGenerator snadno upraven. Vygenerovaný klíč je uložen buď do souboru uvedeného v prvním parametru při spuštění, anebo (není-li parametr uveden) do souboru "keyAES". 17.3. JBPMSyntaxCheck Jednoduchá Java aplikace sloužící k off-line kontrole syntaxe pravidel a workflow. Před spuštěním je nutné nastavit dvě properties: path - cestu k adresáři, který má být zkontrolován, a businessConstants - cestu k adresáři, ve kterém se nachází soubor s business konstantami getBusinessConstants.xml. Aplikace po svém spuštění prochází všechny xml soubory v zadaném podstromě a hlásí: • Chyby v xml, například neuzavřené tagy • Chyby v syntaxi BeanShellu, například chybějící středníky, závorky a z hlediska gramatiky nesmyslné konstrukce • Chybějící importy - vychází z předpokladu, že třídy jsou pojmenovány s velkým písmenem na začátku a nejsou složeny výhradně z velkých písmen (takové řetězce považuje za konstanty). Jelikož některé třídy není potřeba importovat, protože jsou v balíku java.lang nebo java.util, jsou tyto třídy přímo vyjmenovány jako konstanty ve třídě Main. Případné rozšíření seznamu těchto obvyklých tříd je úkolem pro každého vývojáře CzechIdM. 135 Kapitola 17. Nástroje • Volání neexistujících metod na známých třídách. • Přístup k neexistujícím atributům na známých třídách. • Nedeklarované konstanty. • Nebezpečné zapisování do proměnných deklarovaných v BeanShellu uvnitř bloku. JBPMSyntaxCheck ještě není hotový a nelze ho považovat za seriózní testovací nástroj. Jeho cílem je pouze usnadnit vývoj programátorům, kteří chtějí syntaktickou chybu odhalit dřív než po deployi na server. Vychází z myšlenky, že když si není chybou jistý, nic nehlásí a předpokládá svou vlastní chybu. Hlásí-li tedy chybu, je chyba v kódu pravidla nebo workflow. Nehlásí-li chybu, je to dobrý začátek, ale stále je možné, že pravidlo nebo workflow chybu obsahuje. 136 Příloha A. Revision History Revize 0.0-1 Thu Nov 29 2012 Zdeněk Burda [email protected] Vytvoření nového formátu dokumentu a migrace ze starého. 137 138 Rejstřík F feedback informace pro hlášení chyb tohoto dokumentu, ix 139 140
Podobné dokumenty
Jak na CzechIdM - Úvod pro programátory systému CzechIdM
6. Příklady obvyklých akcí
6.1. Získání atributů uživatele ............................................................................................
6.2. Změna atributu uživatele ..................
O soutěži Antivirový program avast! Free Antivirus 6
Použití produktu: Drobo B800i je samostatné externí úložiště dat s intuitivní obsluhou a bez nároků na znalosti diskových polí.
Produkt je připojen pomocí rozhraní iSCSI a určen především pro použi...