Jak na CzechIdM - Úvod pro programátory systému CzechIdM
Transkript
CzechIdM 1.1 Jak na CzechIdM Úvod pro programátory systému CzechIdM Vojtěch Matocha Jak na CzechIdM CzechIdM 1.1 Jak na CzechIdM Úvod pro programátory systému CzechIdM Autor Vojtěch Matocha [email protected] Dokument, který si právě čteš, by ti měl pomoci zorientovat se v CzechIdM. Najdeš v něm jednoduchý přehled toho, co CzechIdM dělá, a naučíš se to nejdůležitější, abys ses mohl sám zapojit do vývoje. Nenajdeš tu vyčerpávající popisy, ty by měly být v kompletní programátorské dokumentaci. Pokud něčemu nebudeš rozumět, nebo ti nějaká informace bude chybět, obrať se na nejbližšího zkušenějšího kolegu, určitě ti pomůže. V této kapitole se obecně seznámíš s důležitými pojmy, v dalších se na ně podíváme o něco podrobněji. Hodně štěstí. 1. Úvod 1.1. 1.2. 1.3. 1.4. 1.5. 1.6. 1.7. O CzechIdM ................................................................................................................ Kde CzechIdM uchovává data ...................................................................................... Jak CzechIdM pracuje s daty ........................................................................................ Role ............................................................................................................................ Konektory .................................................................................................................... Workflow a pravidla ...................................................................................................... Souborový systém ........................................................................................................ 1 1 1 1 2 2 2 3 2. View 2.1. 2.2. 2.3. 2.4. Pohled na objekt .......................................................................................................... Struktura UserView ..................................................................................................... Extended atributy ......................................................................................................... Trimmed View .............................................................................................................. 5 5 6 7 7 3. Koncové systémy 9 3.1. Konektory .................................................................................................................... 9 3.2. Účty ............................................................................................................................. 9 3.3. Schémata .................................................................................................................... 9 3.4. Tok dat ........................................................................................................................ 9 3.5. Synchronizace a rekonciliace ...................................................................................... 11 4. Workflow 4.1. High-level pohled na věc ............................................................................................ 4.2. Jak psát workflow ....................................................................................................... 4.3. IdmUploader - jak nahrát workflow do repository .......................................................... 13 13 13 15 5. Pravidla 5.1. K čemu slouží pravidla ............................................................................................... 5.2. Na co si dát pozor ..................................................................................................... 5.3. Jak pravidlo ladit ........................................................................................................ 17 17 17 18 6. Příklady obvyklých akcí 6.1. Získání atributů uživatele ............................................................................................ 6.2. Změna atributu uživatele ............................................................................................. 6.3. Přiřazení nebo odebrání role ....................................................................................... 6.4. Zablokování nebo odblokování .................................................................................... 6.5. Změna hesla .............................................................................................................. 6.6. Zaslání e-mailu .......................................................................................................... 6.7. Vytvoření schvalovacího požadavku ............................................................................ 6.8. Zobrazení stránky z workflow ...................................................................................... 6.9. Logování .................................................................................................................... 6.10. Seznam uživatelů, rolí, systémů, ... ............................................................................ 6.11. Rekonciliační workflow .............................................................................................. 6.12. Transformační pravidlo .............................................................................................. 6.13. Pravidlo pro vyplnění hodnoty atributu na koncovém systému ...................................... 6.14. Korelační pravidlo ..................................................................................................... 6.15. Spuštění pravidla ...................................................................................................... 6.16. Spuštění workflow .................................................................................................... 19 19 20 20 21 21 22 22 23 23 24 24 26 26 27 28 28 7. Tipy a triky 7.1. O čem bude řeč ......................................................................................................... 7.2. Git ............................................................................................................................. 7.3. JBoss ........................................................................................................................ 7.4. Repository .................................................................................................................. 7.5. SSH ........................................................................................................................... 29 29 29 29 30 30 iii Jak na CzechIdM A. Revision History 33 Rejstřík 35 iv Úvod 1.1. O CzechIdM CzechIdM je náš vlastní Identity Management. Tedy program, který spravuje uživatelské účty napříč informačním systémem zákazníka. Informační systém zákazníka se obvykle skládá z mnoha pro CzechIdM takzvaných koncových systémů, tedy systémů, do kterých CzechIdM zapisuje. Jsou to třeba: webový portál, mailserver nebo systém pro evidenci služebních cest. Druhým typem systému je pro CzechIdM takzvaný autoritativní zdroj informací. Z něj CzechIdM čte, zpravidla to bývá personální systém, ve kterém jsou evidováni všichni zaměstnanci. Co tedy CzechIdM dělá? Sleduje změny v autoritativním zdroji a informace šíří na koncové systémy. Když se například žena provdá, změní se jí příjmení. Paní na personálním oddělení jí ho přepíše v personálním systému. CzechIdM si toho všimne a změní jí příjmení ve všech ostatních koncových systémech, kde se s příjmením pracuje. Spravuje tedy atributy účtů na jednotlivých systémech. Pokud máš za sebou studium na univerzitě, určitě jsi takových účtů měl spoustu: účet v počítačové učebně, účet v menze, účet v knihovně, účet ve studijním systému, účet v centrálním autentizačním LDAPu... CzechIdM toho samozřejmě umí mnohem víc. Třeba automaticky zakládat účty nově příchozím zaměstnancům na základě útvaru, do kterého jsou zařazeni. Měnit hesla. Spravovat oprávnění. Zkrátka a dobře: CzechIdM z některých systémů čte, a co se dozví, může zapsat do těch ostatních. CzechIdM ale není jenom pumpa, která pumpuje data zprava doleva. CzechIdM je systém sám o sobě, do kterého se uživatel může přihlásit. Buď se může přihlásit jako běžný smrtelník do uživatelského rozhraní a z něj si třeba změnit heslo do ostatních napojených systémů, anebo (pokud má příslušná oprávnění) se může přihlásit do rozhraní administrátorského, ze kterého může provádět některé zodpovědnější akce - zakládat účty, mazat účty, napojovat další systémy a podobně. 1.2. Kde CzechIdM uchovává data CzechIdM si o uživatelích a systémech musí leccos pamatovat. Pracuje proto nad databází. Té říkáme repository a každý objekt v CzechIdM v ní má svůj záznam. Obvykle používáme databázi MySQL, ale není to pravidlem. Schéma, ve kterém se repository nachází, se jmenuje bcv_idm_repository. V jednotlivých tabulkách jsou pak záznamy různých objektů, například v tabulce identities najdeš základní sadu informací o uživatelích. Pro práci s repository používá CzechIdM technologii Hibernate. Pokud ses s ní ještě nesetkal, funguje zjednodušeně tak, že objekt v jazyce Java mapuje na jeden řádek v tabulce. Třídy, jejichž objekty které takto mapujeme, označujeme jako entity. 1.3. Jak CzechIdM pracuje s daty Když je potřeba nějak upravit záznam pro nějaký objekt, nepracujeme s ním "napřímo". Kolem repository je vytvořená takzvaná datová vrstva, třídy v jazyce Java, které repository obsluhují. Najdeš je v balíčcích eu.bcvsolutions.idm.data.*. Datová vrstva předává zbytku tříd v CzechIdM takzvané pohledy na objekty, neboli view. Na tomto view se provedou změny a vrátí se datové vrstvě. Ta si změny prohlédne, zkontroluje, a pokud je vše v pořádku, zapíše je do objektu, a tím do repository. K samotným datům tak přistupujeme jen z úzkého okruhu tříd, který je dobře otestovaný a mění se jen zřídka. Kdyby tedy programátor udělal nějakou chybu z nepozornosti v kódu, který se mění často, datová vrstva si toho všimne, zakřičí a změnu na datech neprovede. Získání view od datové vrstvy se říká checkout, předání pozměněného view zpět datové vrstvě checkin. 1 Kapitola 1. Úvod 1.4. Role Dalším důležitým pojmem v CzechIdM je role. Role zpravidla znamená nějaký soubor oprávnění týkající se nějakého napojeného systému. Tento balík oprávnění dostane každý uživatel, kterému je role v CzechIdM přiřazena. Například role "Administrátor Portálu" může přiřazovat práva editovat stránky ve webovém portálu. Role "Uživatel mailserveru" zase může znamenat, že daný člověk má mít zřízený mailový účet. Některým rolím říkáme admin role. Ty se nevztahují k žádnému napojenému systému, ale přímo k CzechIdM. Například role "admin" je admin role pro superuživatele, tedy uživatele s neomezenými právy v CzechIdM - může v CzechIdM v administrátorském rozhraní editovat ostatní uživatele, přiřazovat ostatní role, napojovat další systémy, zkrátka všechno, co se dělat dá. Posledním typem role jsou takzvané business role, které zastřešují několik běžných rolí. Business role "Vedoucí útvaru" tak pod sebou může zahrnovat běžné role "Administrátor Portálu" a "Uživatel mailserveru". Kdo takovou roli dostane, jako kdyby dostal obě dvě běžné role. Pomocí business rolí si každý zákazník může zadefinovat svůj strom oprávnění. Role taky souvisí s účty na napojených systémech. Ke každému vztahu identita - účet musí v CzechIdM existovat role, která říká, že identita takový účet má mít. Potom říkáme, že role přiřazuje účet na systému. 1.5. Konektory Každý koncový systém je jiný a napojuje se jinak. A CzechIdM je vždycky ta strana, která se musí přizpůsobit. Jinak se napojuje tabulka v databázi, jinak adresářová struktura LDAPu a jinak třeba systém, který se ovládá přes terminálovou konzoli. Proto používáme konektory. Můžeš si je představit jako jakousi redukci, která se na jedné straně nasadí na CzechIdM a na druhé straně na napojovaný systém, aby data mohla proudit z jedné strany na druhou. Z hlediska technologie jsou konektory speciální třídy v jazyce Java. Některé jsme sehnali jako open-source na internetu, některé jsme si napsali sami. Nejčastěji se asi setkáš s DatabaseTable konektorem, který umí spravovat jednoduchou tabulku, s Universal JDBC konektorem, kterým se napojují komplikovanější relační databáze a Universal SSH konektorem, kterým se připojujeme k unixovým serverům, na kterých pak spouštíme shellovské skripty. 1.6. Workflow a pravidla Kromě základních tříd napsaných v Javě je CzechIdM tvořeno takzvanými workflow a pravidly. Jedno konkrétní workflow je spustitelný kód, který většinou odpovídá nějakému skutečnému procesu v prostředí zákazníka. Třeba odchodu zaměstnance. Když zaměstnanec odchází, některé účty na koncových systémech mají být smazány, některé zablokovány (protože se uchovávají navždy) a některé mají být převedeny na jiného zaměstnance. Workflow, které pro takového zákazníka napíšeme, situaci modeluje. V CzechIdM je pomocí workflow řešena velká část funkčnosti. Proč? Vysvětlím v následujícím odstavci. Workflow nejsou napsané v Javě, ale v jazyce jPDL, pomocí kterého se definují procesy (nikoli procesy na úrovni operačního systému, ale skutečné postupy, které má zákazník sepsané v nějaké vyhlášce). Vytvořená definice workflow má podobu xml dokumentu. Krátká ukázka: Příklad 1.1. Ukázka definice workflow … 2 Souborový systém <task-node name="createUserTask" end-tasks="true"> <event type="node-enter"> <script> <expression> import java.util.HashMap; import java.util.Date; import eu.bcvsolutions.idm.data.util.Enums.OperationTarget; import eu.bcvsolutions.idm.app.Application; HashMap approvalInfo = new HashMap(); //informace, ktere se pouziji ve schvalovacim formulari approvalInfo.put("operationTarget", OperationTarget.ROLE); approvalInfo.put("targetIdentityName", userView.getId()); ... </expression> </script> </event> <transition name="approved" </task-node> to="addRole" /> … V ukázce sis určitě všiml, že tam kdesi uvnitř xml začíná něco, co se na první pohled velmi podobá Javě. Java to ale není, je to skriptovací jazyk BeanShell. Oproti Javě má jednu výhodu: před spuštěním není potřeba dělat build a generovat bytecode. Vyhodnocuje se až za běhu, řádek po řádku. Proto ho můžeme v běžícím CzechIdM snadno měnit za chodu, aniž bychom CzechIdM museli vypnout. Definice workflow jsou totiž objekty v repository podobně jako uživatelé nebo role. Text, který vidíš v ukázce, je u definice workflow celý v jednom sloupečku. Chceme-li změnit definici workflow, stačí změnit její text. Není totiž závislý na zbytku kódu. O workflow se toho ještě dozvíš víc v dalších kapitolách. Pravidla jsou workflow velice podobná. Nemodelují ale jeden proces, spíš mají význam jedné procedury, jak ji znáš z běžných programovacích jazyků. Používají se ze stejných důvodů: aby se programátor neupsal k smrti a kód zůstal čitelný. Pravidla jsou, stejně jako workflow, objekty v repository, držené v textové podobě. Psát se je naučíš brzy. 1.7. Souborový systém Pokud se ti už podařilo zprovoznit vývojové prostředí a stáhl sis aktuální verzi CzechIdM z gitu, projdeme spolu souborový systém, aby ses v něm lépe orientoval. Nevypisuji všechno, jen to, co by se ti mohlo hodit a co by tě mělo zajímat. Tabulka 1.1. Souborový systém ./Documentation Veškerá dokumentace CzechIdM i konkrétního nasazení u zákazníka. ./Documentation/czechidm Dokumentace té části, která je nezávislá na konkrétním nasazení u zákazníka. ./Realization/BCV_IdM-ejb/Connectors Zdrojové kódy konektorů. ./Documentation/czechidm/JavaDoc Obvyklý javadoc sestavený z komentářů v kódu. 3 Kapitola 1. Úvod ./Documentation/czechidm/Analyza Návrh CzechIdM, než se začalo vytvářet. Pokud nejsi na CzechIdM expert, nedoporučuji číst, není to úplně zábavné. ./Documentation/czechidm/ProgrammersDoc Programátorská dokumentace. Dokument podobný tomuto, jen výrazně podrobnější, hodí se prostudovat! ./Documentation/czechidm/Dokumentace_* Adresáře obsahující dokumentace jednotlivých konektorů, které jsme si napsali, a skutečné realizace. ./Documentation/czechidm/Uzivatelska_prirucka Příručka jak používat uživatelské rozhraní pro běžného uživatele. ./Documentation/projspec Dokumentace konkrétního nasazení u zákazníka. Pro každého zákazníka je tedy odlišná. ./Realization Veškeré zdrojové kódy CzechIdM. ./Realization/BCV_IdM/WebContent Prezentační vrstva CzechIdM, jednotlivé stránky a formuláře. ./Realization/BCV_IdM/src Message katalogy a jejich jazykové mutace. ./Realization/BCV_IdM-ejb/ejbModule Jádro CzechIdM, třídy v jazyce Java ./Realization/BCV_IdM-ejb/repoObjects Definice workflow, pravidel a emailových šablon. Dělí se na obecné a specifické pro daný projekt. ./Realization/BCV_IdM-ejb/Connectors Zdrojové kódy konektorů. ./Realization/BCV_IdM-ejb/SQLScripts SQL skripty obsluhující repository. "initial_import.sql" se používá pro počáteční naplnění prázdné repository. 4 View 2.1. Pohled na objekt Jak jsem zmínil v úvodní kapitole, základními objekty v CzechIdM jsou takzvané pohledy neboli view. Když budeš chtít v kódu pracovat s nějakou identitou, rolí, systémem, schvalovacím požadavkem nebo nějakou jinou instancí entity (to jsou ty třídy objektů uchovávaných v repository), nikdy s nimi nebudeš pracovat přímo. Když budeš psát kód workflow nebo pravidla, nikdy bys tam neměl přímo použít třídu Identity, Role, Resource ani žádnou jinou entitu z balíčku eu.bcvsolutions.idm.data.entity. Přímo k těmto objektům má přístup výhradně datová vrstva kódu, tedy několik málo tříd, které pracují s repository. Ve zbytku kódu se s objekty v repository pracuje nepřímo, prostřednictvím view, které datová vrstva poskytuje. View je interface jazyka Java, v CzechIdM má celou řadu implementací: UserView je pohled na identitu, RoleView pohled na roli, ResourceView pohled na definici koncového systému, OrganisationView pohled na organizaci a tak dále. Z hlediska struktury není View nic jiného než mapa, v CzechIdM je ostatně vždy implementováno jako potomek HashMap<String, Object>. Každé view je tedy jakýmsi výpisem informací o nějakém objektu v repository. Získáš ho pomocí metody checkoutView na třídě Data. Abys mohl mít jistotu, že data ve tvém view jsou aktuální, je objekt v repository dočasně uzamčen pro ostatní přístupy. Můžeš ve view něco změnit, něco přidat, něco odebrat a pak ho pomocí metody checkinView na třídě Data předat zpátky datové vrstvě. Teprve při checkinu skutečně dojde ke změnám, které jsi provedl, a objekt v repository se odemkne. Jak to vypadá v praxi: Příklad 2.1. Ukázka práce s view uživatele View userView = null; String userName = "novakjan"; String firstName = "noveKrestniJmeno"; try { //Ziskani view pro cteni i pro zapis, identita je uzamcena. //Prvni parametr je typ view (my chceme view na identitu), druhy //je identifikator objektu - v pripade identity jeji jmeno. //Objekt, ktery dostaneme, je instanci UserView, my s nim //pracujeme pres spolecny interface View. userView = Data.checkoutView(View.Type.USER, userName); //Zmena krestniho jmena uzivatele userView.put("firstName", firstName); //V tomto okamziku se zmena ulozi do repository a de facto probehne. //Navic se odemkne identita. Data.checkinView(userView); } catch (Exception e) { //Doslo k nejake chybe, zmeny se neprovedou, ale //musime odemknout identitu pro dalsi pouziti Data.releaseViewLock(userView); } 5 Kapitola 2. View 2.2. Struktura UserView Nejčastěji pracujeme s identitami, v následujícím odstavci se proto detailně seznámíme se strukturou UserView. Ostatní view, třeba RoleView, vypadají podobně a podrobně si strukturu můžeš nastudovat v javadoc přímo u těchto tříd. Tabulka 2.1. Atributy UserView Klíč Hodnota name Celé jméno identity. Slouží jako identifikátor. firstName Křestní jméno lastName Příjmení email E-mail disabled zda je identita zablokována (boolean). 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 extended atributů; viz níže 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ů, viz níže. relationAttributes Mapa relačních extended atributů, viz programátorskou dokumentaci Pod klíčem "accounts" se skrývá přehled všech atributů, které lze o dané identitě přečíst na některém napojeném systému. Přímo pod klíčem "accounts" je další mapa, která jako klíče obsahuje názvy systémů, na kterých má identita účet. Pod každým z těchto klíčů je další mapa, která má coby klíče názvy schémat, kde má identita účet. Pod názvem schématu je pak poslední mapa, která obsahuje coby klíče názvy atributů a coby hodnoty konkrétní atributy účtu dané identity. Příklad 2.2. Získání hodnoty atributu z koncového systému //Ziskani view jen pro cteni. //Oproti checkoutu je to veliky rozdil - nejde provest checkin //a identita se neuzamkne. String userName = "novakjan"; UserView userView = Data.getReadOnlyView(View.Type.USER, userName); //nacteni konkretniho atributu "loginName" z koncoveho systemu //"Portal" a schemau "default" String[] path = {"accounts", "Portal", "default", "loginName"}; String loginName = userView.get(path); 6 Extended atributy 2.3. Extended atributy Některé atributy (třeba křestní jméno nebo příjmení), mají identity u všech zákazníků. Tím ovšem společné atributy často končí. U některých zákazníků totiž může CzechIdM pracovat s telefonním číslem, u některých s rodným číslem, u jiných zase s bydlištěm nebo s fotkou. Takovým nestandardním atributům říkáme extended atributy a ve view je najdeš pohromadě pod klíčem "attributes". Řekněme, že chceme z view přečíst extended atribut "city": Příklad 2.3. Získání hodnoty extended atributu //Ziskani view jen pro cteni. String userName = "novakjan"; UserView userView = Data.getReadOnlyView(View.Type.USER, userName); //mapa extended atributu Map extAttributes = (Map) userView.get("attributes"); String city = extAttributes.get("city"); //sikovnejsi zpusob pro totez: String[] path = {"attributes", "city"}; city = userView.get(path); Extended atributy mohou mít hodnotu libovolného serializovatelného objektu: kromě řetězce nebo čísla, klidně obrázku nebo mp3 písničky (i když to není zrovna obvyklé). Při checkinu jsou extended atributy uloženy do vlastní tabulky v repository, do tabulky extended_attributes. Jsou tedy samostatnou entitou podobně jako identity nebo role. Až doposud jsme se o extended atributech bavili jenom v souvislosti s identitami. Extended atributy se ale mohou vztahovat k jakékoli entitě v repository. Extended atributy můžeš používat u rolí (a často to tak děláme), u systémů, u organizací nebo dokonce u jiných extended atributů! 2.4. Trimmed View Kromě běžného View získaného pomocí Data.checkoutView nebo read-only View získaného pomocí Data.getReadOnlyView existují ještě ořezaná ("trimmed") view. Taková view používáme výhradně pro identity, u ostatních entit nemají valného smyslu. Při běžném checkoutu se načítají data i z koncových systémů, což může trvat. V kódu ale často pracujeme jenom s daty, která máme v repository, a hodnoty atributů na koncových systémech nás nezajímají. V takovém případě používáme Data.checkoutTrimmedView, případě Data.getReadOnlyTrimmedView View, které dostaneme, nebude obsahovat klíč "accounts" a při checkinu se změny nepropagují na koncové systémy, ale jen do repository CzechIdM. Jinak se nic nemění. 7 8 Koncové systémy 3.1. Konektory Konektor je třída v jazyce Java, jejímž prostřednictvím CzechIdM komunikuje s koncovým systémem. Všechny konektory implementují společný interface, z pohledu CzechIdM se tedy všechny koncové systémy chovají stejně a mají stejné rozhraní. Představuj si konektor jako nástavec nad koncovým systémem. CzechIdM předává data konektoru, ten obstarává veškerou další komunikaci s koncovým systémem. Jednotlivé instance konektorů nejsou součástí CzechIdM, přibalují se k němu pouze ve formě jar balíků, které se načítají při spuštění aplikačního serveru. Některé konektory jsme vyvinuli sami (třeba konektor pro systémy Alfresco nebo CommuniGate), jiné jsou open-source ke stažení na internetu (například DatabaseTable konektor, který umožňuje pracovat s jednou tabulkou v relační databázi). V administrátorském rozhraní se můžeš setkat s pojmem typ systému. Není to nic jiného než synonymum pro typ konektoru, kterým CzechIdM se systémem komunikuje - pro každý systém se používá nějaký konkrétní typ konektoru. Když klikneš v administrátorském rozhraní v záložce "Systémy" na "Nový systém", nejdřív musíš zvolit typ systému, tedy typ konektoru, který chceš používat. V závislosti na zvoleném konektoru se pak zobrazí formulář pro další specifické informace. 3.2. Účty Účet na koncovém systému je z pohledu CzechIdM vždy mapa atributů - názvů a hodnot. Tyto atributy CzechIdM může číst nebo do nich může zapisovat. Abstrakci od skutečného systému k mapě atributů zajišťuje konektor a CzechIdM díky tomu může pracovat se všemi koncovými systémy stejným způsobem. Některé atributy jsou důležitější než jiné. Prvním takovým je identifikátor účtu. Ten CzechIdM používá pro označení celého účtu a při udržování vztahu mezi identitou a jejím účtem. Druhým je heslo. To se do systému zapisuje pouze při změně, zpravidla se nečte a jeho hodnota se nikam neloguje. Každá identita může mít na jednom koncovém systému nejvýše jeden účet. Aby mohla účet mít, musí mít nějakou roli, která koncový systém přiřazuje. Pokud identita takovou roli nemá, vazba mezi identitou a účtem nevznikne. Odebrání této role znamená pokyn ke smazání účtu. 3.3. Schémata Dovolím si malou odbočku k pojmu schéma. V původním návrhu CzechIdM se předpokládalo, že by jeden koncový systém mohl mít z pohledu CzechIdM několik stejně napojených částí, schémat. Nikdy se to ovšem nepoužilo a pravděpodobně nikdy nepoužije. Z pohledu CzechIdM je tedy každý koncový systém tvořen právě jedním schématem, které vždy pojmenováváme "default". Schémata jsou jakýsi rudiment, který bude nejspíš jednoho dne odstraněn, až k tomu někdo najde sílu. 3.4. Tok dat Uvažme nyní následující situaci: CzechIdM je napojeno na koncový systém, který má pro jednoduchost jen dva atributy: krestniJmeno a fullName. Systém očekává v atributu krestniJmeno křestní jméno (třeba velkými písmeny "JAROSLAV") a v atributu fullName celé jméno uživatele včetně titulů ("Jaroslav Novák DrSc."). V této sekci ti osvětlím, kdy, kudy a jak se data z CzechIdM dostanou na koncový systém. Ten si můžeš představovat třeba jako jednoduchou tabulku, ve které každému účtu odpovídá právě jeden řádek. 9 Kapitola 3. Koncové systémy Data se aktualizují na koncovém systému při každém checkinu neořezaného view. Zaslání dat na koncový systém se také říká provisioning. Identita, která má mít účet na koncovém systému, musí mít nějakou roli, která tento systém přiřazuje. Nejdřív se věnujme křestnímu jménu. Jak už víš z předchozí kapitoly, křestní jméno je běžný atribut každé identity a pod klíčem "firstName" ho můžeme číst v UserView. V mapování atributů proto můžeme vyplnit sloupeček idmName přímo řetězcem "firstName", sloupeček resourceName řetězcem "krestniJmeno" a CzechIdM zajistí, že se do konektoru pod názvem "krestniJmeno" pošle hodnota atributu "firstName" u identity. Pokud se atribut vyplňuje tímto základním způsobem, říkáme, že se vyplňuje z identity. Ve formuláři, ve kterém definuješ mapování atributů, můžeš pro některý atribut stanovit takzvané transformační pravidlo. To se hodí, pokud je potřeba na hodnotě atributu provést nějakou jednoduchou úpravu. V našem případě převedení křestního jména na velká písmena. Transformační pravidlo je jakýsi trychtýř, který vždy na poslední chvíli upraví data proudící do konektoru. Příklad 3.1. Ukázkové transformační pravidlo převádí zadaný řetězec do velkých písmen <?xml version="1.0" encoding="UTF-8"?> <!-řetězec na vstupu vrací ve velkých písmenech. Pokud je null, vrací null. --> <rule-definition xmlns="urn:jbpm.org:bcv_rule-1.0" name="transformToUpperCase"> String attrValue = (String) this.interpreter.get("attrValue"); if (attrValue == null) { return null; } else { return attrValue.toUpperCase(); } </rule-definition> Atribut fullName bude malinko složitější. Pod žádným klíčem v UserView není přímo ta hodnota, kterou bychom chtěli do konektoru poslat. Proto si musíme napsat pravidlo, které bude tuto hodnotu počítat. To dostane na vstupu UserView a očekává se, že na výstupu vrátí požadovanou hodnotu. Příkladem budiž následující jednoduché pravidlo, které vrací hodnotu extended atributu "employeeType": Příklad 3.2. Pravidlo vracející hodnotu extended atributu employeeType <?xml version="1.0" encoding="UTF-8"?> <!-Vrací hodnotu extended atributu "employeeType", na vstupu dostane userView --> <rule-definition xmlns="urn:jbpm.org:bcv_rule-1.0" name="getEmployeeType"> import eu.bcvsolutions.idm.data.view.View; View userView = this.interpreter.get("userView"); String[] path = {"attributes", "employeeType"}; return userView.get(path); </rule-definition> 10 Synchronizace a rekonciliace Naše pravidlo pro atribut fullName by vypadalo asi takto: Příklad 3.3. Pravidlo vracející fullName <?xml version="1.0" encoding="UTF-8"?> <!-Vrací řetězec "krestniJmeno prijmeni tituly", na vstupu dostane userView --> <rule-definition xmlns="urn:jbpm.org:bcv_rule-1.0" name="getFullNameWithTitle"> import eu.bcvsolutions.idm.data.Data; import eu.bcvsolutions.idm.data.dto.Criteria; import eu.bcvsolutions.idm.data.view.View; import eu.bcvsolutions.idm.app.Application; View userView = this.interpreter.get("userView"); String firstName = (String) userView.get("firstName"); if (firstName == null) { firstName = ""; } String lastName = (String) userView.get("lastName"); if (lastName == null) { lastName = ""; } // Nacteme tituly z extended atributu String[] pathToTitle = {"attributes", "tituly"}; String title = (String) userView.get(pathToTitle); if (title == null) { title = ""; } Application.logInfo("getFullNameWithTitle: " + lastName + " " + firstName + " " + title, new Object[0]); StringBuilder builder = new StringBuilder(); builder.append(lastName + " " + firstName); if (!"".equals(title)) { builder.append(" " + title); } return builder.toString(); </rule-definition> Pravidlo máme hotové, zbývá ho nakonfigurovat u příslušné role, která přiřazuje náš koncový systém. Říkáme, že takto vyplněný atribut je vyplněný z role. U každé role můžeš přes administrátorské rozhraní CzechIdM zadefinovat, které systémy má přiřazovat a jak má vyplňovat jeho atributy. Kromě vyplňování pravidlem může role ještě vyplňovat atribut řetězcovou konstantou. Rekapitulace: Data se na koncový systém (do konektoru) posílají během provisioningu, tedy po zavolání Data.checkinView na nějaké UserView s rolí přiřazující daný systém. Konkrétní atribut na koncovém systému se vyplňuje z identity, pokud se jedná o běžný atribut v UserView. Druhou možností je vyplňování z role. Existují dva typy vyplňování z role: řetězcovou konstantou nebo pravidlem. Dřív, než se data odešlou do konektoru, aplikuje se transformační pravidlo, pokud je definováno. 3.5. Synchronizace a rekonciliace V minulé sekci jsme se zabývali situací, kdy data proudí z CzechIdM na koncový systém. Někdy je ovšem potřeba i opačný směr, z napojeného systému do CzechIdM. To se stává obvykle ve dvou 11 Kapitola 3. Koncové systémy případech: buď je systém autoritativním zdrojem informací pro CzechIdM, anebo napojujeme systém, který u zákazníka už dlouho běží, a potřebujeme připárovat existující účty k identitám v CzechIdM. Když data tekla z CzechIdM na koncový systém, bylo to jednodušší. CzechIdM samo zaregistrovalo změnu a propagovalo ji na ostatní napojené systémy. Opačně z koncového systému žádné upozornění nepřijde, CzechIdM proto čas od času musí zkontrolovat, jestli na systému k něčemu nedošlo. Takovým kontrolám říkáme rekonciliace a synchronizace. Rozdíl mezi těmito dvěma pojmy je jen v počtu kontrolovaných účtů. Zatímco rekonciliace prochází úplně všechny účty na systému, synchronizace se zabývá jen těmi, které se od poslední synchronizace změnily. Synchronizaci není možné vždy implementovat, některé konektory synchronizaci nepodporují. Abys mohl používat rekonciliaci a synchronizaci, musíš napsat rekonciliační workflow. To je kus kódu, který se spustí pro každý kontrolovaný účet, respektive pro každou identitu, která by měla na systému mít účet, ale nemá. Ukázkové rekonciliační workflow najdeš v kapitole s příklady. Jakmile máš rekonciliační workflow hotové, musíš ho nastavit u příslušného systému přes administrátorské rozhraní CzechIdM. Rekonciliace řeší mimo jiné následující problém: na systému je účet - které identitě v CzechIdM ho má přiřadit? Pokud není řečeno jinak, zkusí rekonciliace přiřadit účet té identitě, jejíž uživatelské jméno se shoduje s identifikátorem účtu. Kdybys chtěl toto chování změnit, musíš vytvořit a nastavit korelační pravidlo. Tak nazýváme proceduru, která dostane na vstupu atributy daného účtu a má vrátit uživatelské jméno identity, ke které účet patří. Jedno takové pravidlo je v kapitole s příklady. 12 Workflow 4.1. High-level pohled na věc Pomocí workflow modelujeme postupy a procesy z reálného života. Ve škole ses nejspíš setkal s pojmem "stavový automat". Workflow nejsou nic jiného. Mají pevně zadefinované stavy, akce, které se ve stavech mají provést, a přechody mezi jednotlivými stavy na základě stanovených podmínek. Definici takového stavového automatu zapisujeme do xml dokumentu pomocí jazyka jPDL. Uzly zapisujeme to tagu <node>, přechody mezi nimi do tagu <transition>. Kromě toho musíme pevně stanovit počáteční stav do tagu <start-state> a koncový stav do uzlu <end-state>. V každém uzlu a v každém přechodu mohou být nějaké akce, něco spočítat, něco změnit v repository. K tomu používáme skriptovací jazyk beanShell, který se velmi podobá některým starším verzím Javy (nejvíce asi Javě 1.5). V zásadě s ním můžeš jako s Javou pracovat, hlavní rozdíl, který pocítíš, bude neexistence vývojového prostředí. Až zapomeneš napsat středník, Eclipse tě neopraví. Přijdeš na to až při spuštění workflow. Takže dvakrát měř, jednou řež. 4.2. Jak psát workflow Dostal jsi za úkol napsat workflow. Pojďme si projít krok za krokem, co musíš udělat. V adresáři ./Realization/BCV_IdM-ejb/repoObjects/projspec/workflows si prostřednictvím Eclipse vytvoř nový adresář, který se bude jmenovat stejně jako tvé workflow (zpravidla několik slov oddělených tečkou, prvním slovem je zkratka názvu zákazníka, třeba chmu.user.remove). Pokud vytváříš obecné workflow, které s žádným konkrétním nasazením nesouvisí, vytvoř adresář v ./Realization/BCV_IdM-ejb/repoObjects/czechidm/ workflows. V novém adresáři vytvoř prázdný textový soubor processdefinition.xml. Do něj budeš psát definici svého workflow. Do souboru vepiš základní strukturu xml, doporučuji ji zkopírovat z nějakého existujícího workflow, přepsat název (atribut name v tagu process-definition) a ponechat počáteční a koncový uzel (startstate, end-state). Ostatní uzly smaž. Rozmysli si vlastní logiku workflow. Jaké uzly(node) budeš potřebovat? Odkud kam povedou jednotlivé přechody(transition)? Z vlastní zkušenosti nedoporučuji rozbít workflow do příliš mnoha uzlů, přehlednost to nezvyšuje, spíš naopak. Pojďme si společně projít jedno reálné workflow. Jde o workflow rekonciliační, spouští se pro každý účet na koncovém systému a snaží se k němu připárovat nějakou identitu v CzechIdM. Přímo do kódu jsem dopsal komentáře: v <-- --> v xml a za // v Javě. <?xml version="1.0" encoding="UTF-8"?> <!-- hlavicka procesu. Obsahuje nazev workflow (atribut name), ktery se musi shodovat s nazvem adresare, ve kterem se workflow nachazi. Za povsimnuti stoji atribut xmlns. V nasich starsich workflow miva hodnotu "urn:jbpm.org:jpdl-3.2", v novejsich "urn:jbpm.org:bcv_jpdl-3.2". Vytvarej vzdy workflow druheho typu, umozni ti to napriklad definovat, pod jakymi pravy ma byt workflow spusteno. 13 Kapitola 4. Workflow K tomu slouzi atribut runAs, kde specifikujeme, ze ma byt workflow spusteno pro pravy uzivatele "admin"--> <process-definition xmlns="urn:jbpm.org:bcv_jpdl-3.2" name="cgate.recon" runAs="admin"> <!-- pocatecni uzel workflow. Tady workflow zacne, a jelikoz uzel neobsahuje zadne akce, jde se po jedinem prechodu do uzlu decideAction --> <start-state name="start-state1"> <transition to="decideAction" /> </start-state> <!-- rozhodovaci uzel. Definuje se v tagu "decision" a samotne rozhodnuti se provadi pomoci atributu "expression". Z uzlu, na ktery se spolu divame, se prejde po transition s nazvem v promenne situationTypeName.--> <decision name="decideAction" expression="#{situationTypeName}"> <!-- tady rikame, ze jakmile proces dorazi do tohoto uzlu, ma se neco stat --> <event type="node-enter"> <!-- ma se spustit skript, explicitne napsany v tagu "expression". Jako skriptovaci jazyk pouzivame beanshell. Na zacatku musis uvest vsechny importy, jinak bude workflow pri spusteni hlasit chybu. --> <script> <expression> import eu.bcvsolutions.idm.app.util.Utils; /* Zjistujeme typ situace. Existuji 4 hlavni moznosti: 1. MISSING_IDENTITY ... na koncovem systemu existuje ucet, ke kteremu se nepodarilo nalezt identitu 2. MISSING_ACCOUNT ... v CzechIdM je identita, ktera na systemu ma mit ucet. Ten tam ale neni. 3. ASSIGNED ... ucet je uz priparovan k nejake identite 4. MATCHED ... uctu odpovida nejaka identita v CzechIdM, ale jeste k ni neni priparovan */ String situationTypeName = Utils.getSyncSituationTypeName(sync); </expression> <!-- Toto je dulezite! Timto rikame, ze s promennou "situationTypeName" chceme dale pracovat. Kdybychom to neuvedli, byla by promenna pouze lokalni a zanikla by s opustenim uzlu. access muze nabyvat i hodnoty "read". Je-li nejaka promenna s pristupem "read" stanovena, neni mozne cist ostatni promenne, ktere "read" nemaji. Pokud zadna takova "read" promenna stanovena neni, je mozne cist v uzlu vsechny promenne. Moje doporuceni: vubec access="read" nepouzivej. Jsou s tim problemy --> <variable name="situationTypeName" access="write" /> </script> </event> <!-- Seznam prechodu. Jak je videt, chceme se zabyvat jen dvema typy situace: MATCHED a ASSIGNED. V ostatnich pripadech jdeme rovnou do koncoveho stavu. --> <transition to="end" name="MISSING_IDENTITY" /> <transition to="end" name="MISSING_ACCOUNT" /> <transition to="process" name="MATCHED" /> <transition to="process" name="ASSIGNED" /> <transition to="end" name="ERROR" /> </decision> <!-- klicovy uzel, ve kterem je hlavni logika. Je typu "node", jde tedy o bezny uzel--> <node name="process"> <event type="node-enter"> <script> 14 IdmUploader - jak nahrát workflow do repository //seznam importu import eu.bcvsolutions.idm.app.util.Utils; import eu.bcvsolutions.idm.data.dto.DTO; import eu.bcvsolutions.idm.data.Data; import eu.bcvsolutions.idm.app.Application; import eu.bcvsolutions.idm.data.view.View; import eu.bcvsolutions.idm.data.view.View.Type; import eu.bcvsolutions.idm.data.util.ObjectUtils; import eu.bcvsolutions.idm.data.logging.AuditLogger; //ano, tohle se smi pouzivat. Vystupy pak najdes v logu JBosse. System.out.println("---[cgate.recon]---"); //ke ktere identite se ucet vztahuje? String identityName = (String) sync.get("identityName"); //jak se jmenuje ucet na koncovem systemu? String accountUid = (String) sync.get("accountUid"); View view = null; try { //ziskame view pro daneho uzivatele view = Data.checkoutView(View.Type.USER, identityName); if (view == null) { throw new Exception("Cannot obtain view for " + identityName); } //priparujeme ucet k identite Utils.addSyncAccountToView(view, sync); //pridame roli prirazujici koncovy system Utils.addRoleToView(view, CGATE_ROLE); //ulozime zmeny do repository. Teprve v tomto okamziku se zmeny projevi. Data.checkinView(view); } catch (Exception e) { //neco se nepovedlo, musime odemknout identitu, aby s ni mohla pracovat ostatni workflow. if (view != null) { Data.releaseViewLock(view); view = null; } } </script> </event> <transition to="end" /> </node> <end-state name="end" /> Pokud potřebuješ provést nějakou složitější akci, třeba vytvořit schvalovací požadavek nebo zobrazit stránku, podívej se do kapitoly s příklady. V obou případech tam najdeš jednoduchou ukázku. 4.3. IdmUploader - jak nahrát workflow do repository V předchozích odstavcích jsme se zabývali tím, k čemu jsou workflow dobrá a jak je napsat. Už to umíme, napsali jsme své vlastní workflow a chtěli bychom si ho někde spustit. Pokud si vzpomínáš na úvodní kapitolu, zmínil jsme v ní, že workflow uchováváme v textové podobě v repository. Potřebujeme tedy způsob jak text definice workflow nahrát do databáze. 15 Kapitola 4. Workflow Přesně pro tyto účely slouží aplikace IdmUploader. Je jednoduchá, napsaná v Javě a vpodstatě dělá jen to nejnutnější: na vstupu dostane cestu k adresáři, projde všechny soubory v něm, ke každému přistoupí jako k definici workflow a jeho obsah nahraje do repository. IdMUploader je samostatný projekt, který najdeš v gitu v adresáři IdmUploader. Než ho spustíš, musíš v souboru conf.properties v balíčku eu.bcvsolutions.wfuploader nastavit některé vstupní parametry: port=3306 host=localhost database=bcv_idm_repository parameters= username=root password= wf-path=/home/matochav/actualProject/Realization/BCV_IdM-ejb/repoObjects/czechidm/workflows rule-path=/home/matochav/actualProject/Realization/BCV_IdM-ejb/repoObjects/czechidm/rules email-template-path=/home/matochav/actualProject/Realization/BCV_IdM-ejb/repoObjects/ czechidm/emailTemplates V properties souboru nastavíš parametry pro připojení k databázi (port, host, schéma, uživatelské jméno a heslo) a cestu, kde má uploader soubory s definicemi workflow hledat (wf-path). Uploader aktualizuje kromě workflow také pravidla a e-mailové šablony, i k nim stanovíš v properties souboru cestu. Hlavní třídou uploaderu je třída Main, tu musíš spustit. Při používání uploaderu si dej pozor na jednu drobnost: obecná workflow a workflow specifická pro konkrétní projekt jsou ve dvou různých adresářích, pokaždé tedy musíš cestu přepsat. Často se navíc nějaké specifické a obecné workflow stejně jmenují a to specifické je nějakou modifikací toho obecného. Až budeš někdy uploader používat, musíš proto nejdřív uploadovat obecná workflow a teprve po nich ta projektově specifická. Jakmile pomocí uploaderu nahraješ workflow a pravidla do repository, musíš v CzechIdM v záložce "Konfigurace" kliknout na "Refresh workflow" a "Refresh pravidel". Teprve potom CzechIdM začne pracovat s novou definicí workflow a vyprázdní cache na definice pravidel. Ještě jedna maličkost: kliknutí na "Refresh workflow" samo o sobě vyžaduje spuštění jednoho konkrétního workflow. Ocitnešli se tedy někdy v situaci, kdy v CzechIdM nejsou vůbec žádná workflow, tenhle postup selže. V takovém případě zadej do prohlížeče adresu localhost:8080/idm/wfs_redeploy.seam. 16 Pravidla 5.1. K čemu slouží pravidla Pravidla mají v CzechIdM roli procedur a funkcí, které se volají z workflow nebo jinde z kódu. Používáme je tak často místo standardních Java metod, protože pravidla se udržují mnohem snáz než běžné Java třídy. Když změníš Java třídu, musíš na ní provést build a nový class soubor deployovat na jbosse. U pravidla naproti tomu stačí spustit uploader a pravidlo můžeš hned používat. Důvodů, proč bys je mohl použít, je několik: • Abys nepsal pořád dokola stejný kód • Potřebuješ transformační pravidlo • Potřebuješ korelační pravidlo • Potřebuješ pravidlo pro vyplnění atributu z role Transformační pravidlo se dá nastavit v záložce "Systémy" k některému atributu. Když se na koncový systém posílá nová hodnota, zadané transformační pravidlo se na ni použije a nějak ji změní. Může třeba změnit všechna velká písmenka na malá, odstranit diakritiku, změnit boolean hodnotu na string "true/false" nebo třeba použít kódování Base64. Příklad transformačního pravidla najdeš v sekci "Příklady". Korelační pravidlo se používá při rekonciliaci, tedy při párování účtů na nějakém systému a uživatelů, které máme v CzechIdM. Pro každý účet, který zatím žádné identitě nepatří, se toto pravidlo zavolá a pokusí se účet nějaké identitě přiřadit. Na vstupu dostane všechny atributy účtu a snaží se podle toho nalézt identitu, které by mohlo účet přiřadit. Příklad korelačního pravidla najdeš v sekci "Příklady". Pravidlo pro vyplnění atributu z role říká následující: "Když někomu přiřadíš tuto roli, má mít tento atribut nastavený na hodnotu, která se spočítá tímto pravidlem." Pravidlo dostane na vstupu celé userView a z něj vypočítá hodnotu, která se pošle přes případné transformační pravidlo do konektoru. Příklad je opět k vidění v sekci "Příklady". 5.2. Na co si dát pozor BeanShell bohužel není Java, takže ti Eclipse moc nepomůžou při psaní. Dávej si proto pozor na zapomenuté středníky a na to, abys uvedl všechny importy. A ještě jedna perlička, na kterou zatím narazil každý náš programátor. Prohlédni si následující kód: System.out.println("Zacatek..."); if (true) { String ahoj = "ahoj"; if (true) { ahoj = "na shledanou"; } System.out.println(ahoj); } Na první pohled všechno jednoduché, že? Vypíše se "na shledanou", že? Jenže to nebude fungovat. BeanShell má totiž jednu podivnou vlastnost. Když deklaruješ proměnnou uvnitř bloku a v jiném vnořeném bloku ji přepíšeš, někdy se to neprovede. Neptej se proč, nevím to. Každopádně se s 17 Kapitola 5. Pravidla tím určitě potkáš a tahle chyba tě zabaví na hodně dlouho. Aby to fungovalo, musí být proměnná deklarovaná vně všech bloků, tedy takto: System.out.println("Zacatek..."); String ahoj = null; if (true) { ahoj = "ahoj"; if (true) { ahoj = "na shledanou"; } } System.out.println(ahoj); 5.3. Jak pravidlo ladit Napsal jsi své vlastní pravidlo a chtěl bys zjistit, co dělá? Doporučuji následující postup: napiš si jednoduchoučké workflow, ve kterém pravidlo spustíš. Toto workflow pak pomocí IdMUploaderu nahraj do své lokální repository a přes administrátorské rozhraní ho spusť. Pokud jsi do pravidla zahrnul kontrolní výpisy, uvidíš je v logu jbosse nebo jako výstup na konzoli v Eclipsech. Než workflow spustíš (a po každém uploadu), nezapomeň v záložce "Konfigurace" kliknout na tlačítko "Refresh workflow". Pokud to neuděláš, změny ve workflow se při spuštění neprojeví. Příklad spuštění pravidla z kódu najdeš v sekci "Příklady" 18 Příklady obvyklých akcí 6.1. Získání atributů uživatele View userView = null; try { //ziskani view jen pro cteni userView = Data.getReadOnlyView(View.Type.USER, userName); //login uzivatele String name = userView.get("name"); //krestni jmeno String firstName = userView.get("firstName"); //prijmeni String lastName = userView.get("lastName"); //email String email = userView.get("email"); //login nadrizeneho. Pokud nema, je null String idmManager = userView.get("idmManager"); //plny nazev domovske organizace // (napr. "top:firma:pobockaPraha:financniOddeleni") String homeOrganisation = userView.get("homeOrganisation"); //seznam nazvu kontrolovanych organizaci List controledOrganisations = (List) userView.get("controledOrganisations"); //seznam nazvu aktualne prirazenych roli List roles = (List) userView.get("roles"); //zda je uzivatel aktualne zablokovan Boolean disabled = userView.get("disabled"); //mapa uctu, pro konkretni atribut viz o par radku nize DTO accounts = (DTO) userView.get("accounts"); //mapa extended atributu, pro konkretni atribut viz o par radku nize DTO extAttributes = (DTO) userView.get("attributes"); //mapa relacnich extended atributu, pro konkretni atribut viz o par radku nize DTO relExtAttributes = (DTO) userView.get("relationAttributes"); //nacteni konkretniho atributu "muj_atribut" //z koncoveho systemu "Portal" a schemau "default" String[] path = {"accounts", "Portal", "default", "muj_atribut"}; String mujAtribut = userView.get(path); //nacteni konkretniho extended atributu "muj_extended_atribut" path = {"attributes", "muj_extended_atribut"}; String mujExtendedAtribut = userView.get(path); } catch (Exception e) { e.printStackTrace(); } 19 Kapitola 6. Příklady obvyklých akcí 6.2. Změna atributu uživatele View userView = null; try { //Ziskani view pro cteni i pro zapis. //V tomto okamziku se identita uzamkne a je nutne ji zase odemknout, //proto je v catch klauzuli prikaz pro odemknuti userView = Data.checkoutView(View.Type.USER, userName); //prejmenovani uzivatele userView.put("name", name); //zmena krestniho jmena userView.put("firstName", firstName); //zmena prijmeni userView.put("lastName", lastName); //zmena emailu userView.put("email", "[email protected]"); //zmena nadrizeneho userView.put("idmManager", "novakj"); //presun v organizacni strukture userView.put("homeOrganisation", "top:lide:kuchyne"); //zmena konkretniho extended atributu "muj_extended_atribut" String[] path = {"attributes", "muj_extended_atribut"}; userView.put(path, "moje_hodnota"); //V tomto okamziku se zmeny ulozi do repository a de facto probehnou. //Navic se odemkne identita. Data.checkinView(userView); } catch (Exception e) { //doslo k nejake chybe, musime odemknout identitu pro dalsi pouziti Data.releaseViewLock(userView); } 6.3. Přiřazení nebo odebrání role View userView = null; try { //Ziskani view pro cteni i pro zapis. //V tomto okamziku se identita uzamkne a je nutne ji zase odemknout, //proto je v catch klauzuli prikaz pro odemknuti userView = Data.checkoutView(View.Type.USER, userName); List roles = userView.get("roles"); //odebrani role roles.remove("kuchtik"); //pridani role roles.add("sefkuchar"); //V tomto okamziku se zmeny ulozi do repository a de facto probehnou. //Navic se odemkne identita. //pokud maji role nastavene schvalovani, zacne schvalovaci proces. 20 Zablokování nebo odblokování Data.checkinView(userView); } catch (Exception e) { //doslo k nejake chybe, musime odemknout identitu pro dalsi pouziti Data.releaseViewLock(userView); } 6.4. Zablokování nebo odblokování View userView = null; try { //Ziskani view pro cteni i pro zapis. //V tomto okamziku se identita uzamkne a je nutne ji zase odemknout, //proto je v catch klauzuli prikaz pro odemknuti userView = Data.checkoutView(View.Type.USER, userName); //zda je uzivatel zablokovany Boolean disabled = userView.get("disabled"); //zablokovani userView.put("disabled", true); //v tomto okamziku se zmeny ulozi do repository a de facto probehnou. //Navic se odemkne identita. Data.checkinView(userView); } catch (Exception e) { //doslo k nejake chybe, musime odemknout identitu pro dalsi pouziti Data.releaseViewLock(userView); } 6.5. Změna hesla View userView = null; try { //Ziskani view pro cteni i pro zapis. //V tomto okamziku se identita uzamkne a je nutne ji zase odemknout, //proto je v catch klauzuli prikaz pro odemknuti userView = Data.checkoutView(View.Type.USER, userName); //zmena hesla, heslo je potreba zadat dvakrat stejne userView.put("password", "tajneHeslo8888"); userView.put("password2", "tajneHeslo8888"); //V tomto okamziku se zmeny ulozi do repository a de facto probehnou. //Navic se odemkne identita. Data.checkinView(userView); } catch (Exception e) { //doslo k nejake chybe, musime odemknout identitu pro dalsi pouziti Data.releaseViewLock(userView); } 21 Kapitola 6. Příklady obvyklých akcí 6.6. Zaslání e-mailu //V tomto prikladu se snazime odeslat mail nadrizenemu //pana Dvoraka, kterym je pan Novak s loginem "novakj" String manager = "novakj"; //Email bude formatovan podle emailove sablony. //Emailova sablona je soubor v repoObjects/projspec/emailTemplates String templateName = "nazevMojiEmailoveSablony"; //Mapa parametru, ktere budou vyplneny do sablony Map params = new HashMap(); params.put("userFullName", "Josef Dvořák"); //Samotne odeslani emailu. Zda se to podarilo, bude v promenne emailWasSent boolean emailWasSent = Application.sendEmailToIdentity(idmManager, templateName, params); //Pokud se to nepodarilo, zalogujeme to if (!emailWasSent) { Application.logInfo("Nepodařilo se odeslat email ze šablony {0} s adresátem {1}.", new Object[] { templateName, manager }); } 6.7. Vytvoření schvalovacího požadavku <task-node name="createUserTask" end-tasks="true"> <event type="node-enter"> <script> <expression> import java.util.HashMap; import java.util.Date; import eu.bcvsolutions.idm.data.util.Enums.OperationTarget; import eu.bcvsolutions.idm.app.Application; HashMap approvalInfo = new HashMap(); //informace, ktere se pouziji ve schvalovacim formulari approvalInfo.put("operationTarget", OperationTarget.ROLE); approvalInfo.put("targetIdentityName", userView.getId()); approvalInfo.put("newValue", roleName); String description = "Uživatel: " + userView.getId() + " Role: " + roleName; //stranka, ktera se uzivateli zobrazi po rozkliknuti schvalovaciho pozadavku String pageId = "include/roleApprove"; //jak dlouho se ma cekat, nez probehne eskalace String period = ESCALATION_PERIOD; //pokud uz zadost eskalovala az k hlavnimu administratorovi, nastavi se finalni doba, zpravidla jeden rok. if (approvers.size() == 1 && approvers.get(0).equals(IT_DEPARTMENT_IDENTITY)) { period = ESCALATION_PERIOD_FINAL; } //vypocet, kdy ma dojit k eskalaci Date dueDate = Application.executeRule2("getTimerDueDate", new Object [] { "durationString", period }); 22 Zobrazení stránky z workflow </expression> <variable name="pageId" access="write" /> <variable name="approvalInfo" access="write" /> <variable name="description" access="write" /> <variable name="userView" access="read" /> <variable name="roleName" access="read" /> <variable <variable <variable <variable <variable </script> </event> name="dueDate" access="write" /> name="approvers" access="read" /> name="IT_DEPARTMENT_IDENTITY" access="read" /> name="ESCALATION_PERIOD" access="read" /> name="ESCALATION_PERIOD_FINAL" access="read" /> <!-- description je popisek, ktery se zobrazi uzivateli v seznamu schvalovacich pozadavku --> <!-- approvers je seznam uzivatelu, na ktere pozadavek pujde. Ke schvaleni staci, aby to schvalil jeden z nich --> <task name="SchvaleniPridaniRole" description="#{description}"> <assignment pooled-actors="#{approvers}"></assignment> <timer name="Escalation3" duedate="#{dueDate}" transition="escalate" /> <controller> <variable name="approvers" access="read" /> <variable name="roleName" access="read" /> <variable name="pageId" access="read" /> <variable name="userView" access="read" /> <variable name="description" access="read" /> <variable name="approvalInfo" access="read" /> <variable name="dueDate" access="read" /> </controller> </task> <transition name="approved" to="addRole" /> <transition name="denied" to="endState" /> <transition name="escalate" to="Escalate" /> </task-node> 6.8. Zobrazení stránky z workflow <state name="showPage"> <event type="node-enter"> <action class="eu.bcvsolutions.idm.app.workflow.ShowPageAction"> <!-- cesta ke zdrojovemu kodu stranky --> <pageName>admin/task/edit</pageName> <onlySaveContextVar>onlySaveContext</onlySaveContextVar> </action> </event> <transition <transition <transition <transition </state> to="showPage" /> name="save" to="save"/> name="reset" to="viewControl"/> name="close" to="releaseViewLock"/> 6.9. Logování 23 Kapitola 6. Příklady obvyklých akcí String message = "je to rozbite"; //bezne zalogovani informace o tom, co CzechIdM zrovna dela Application.logInfo("Neco se deje, konkretne: {0}", new Object[] { message }); //varovani, je mozne, ze doslo k chybe Application.logWarn("Neco se deje, konkretne: {0}", new Object[] { message }); //chybovy stav - zcela jiste doslo k chybe, kterou bude nutne opravit Application.logError("Neco se deje, konkretne: {0}", new Object[] { message }); //fatalni stav, CzechIdM neni schopno dale pracovat Application.logFatal("Neco se deje, konkretne: {0}", new Object[] { message }); 6.10. Seznam uživatelů, rolí, systémů, ... //ziskani vsech uzivatelu v CzechIdM criteria = new Criteria(); List users = Data.listIds(View.Type.USER, criteria); //ziskani vsech uzivatelu z organizace top:lide criteria = new Criteria(); criteria.add("homeOrganisation", "top:lide%", Relation.LIKE); List usersFromOrg = Data.listIds(View.Type.USER, criteria); //ziskani vsech uzivatelu s extended atributem //"typZamestnance" nastavenym na hodnotu "kuchtik" criteria = new Criteria(); criteria.createAlias("extendedAttributes", "attrs"); criteria.addEq("attrs.name", "typZamestnance"); criteria.addEq("attrs.value", "kuchtik"); List kuchtici = Data.listIds(View.Type.USER, criteria); //ziskani vsech roli s prefixem "PPT_" criteria = new Criteria(); Criteria.add("name", "PPT_%", Relation.LIKE); List rolesWithPrefix = Data.listIds(View.Type.ROLE, criteria); 6.11. Rekonciliační workflow <?xml version="1.0" encoding="UTF-8"?> <process-definition xmlns="urn:jbpm.org:bcv_jpdl-3.2" name="cgate.recon" runAs="admin"> <start-state name="start-state1"> <transition to="decideAction" /> </start-state> <decision name="decideAction" expression="#{situationTypeName}"> <event type="node-enter"> <script> <expression> import eu.bcvsolutions.idm.app.util.Utils; 24 Rekonciliační workflow String situationTypeName = Utils.getSyncSituationTypeName(sync); </expression> <variable name="situationTypeName" access="write" /> </script> </event> <transition <transition <transition <transition <transition </decision> to="end" name="MISSING_IDENTITY" /> to="end" name="MISSING_ACCOUNT" /> to="process" name="MATCHED" /> to="process" name="ASSIGNED" /> to="end" name="ERROR" /> <node name="process"> <event type="node-enter"> <script> import import import import import import import import eu.bcvsolutions.idm.app.util.Utils; eu.bcvsolutions.idm.data.dto.DTO; eu.bcvsolutions.idm.data.Data; eu.bcvsolutions.idm.app.Application; eu.bcvsolutions.idm.data.view.View; eu.bcvsolutions.idm.data.view.View.Type; eu.bcvsolutions.idm.data.util.ObjectUtils; eu.bcvsolutions.idm.data.logging.AuditLogger; System.out.println("---[cgate.recon]---"); String identityName = (String) sync.get("identityName"); String accountUid = (String) sync.get("accountUid"); View view = null; try { view = Data.checkoutView(View.Type.USER, identityName); if (view == null) { throw new Exception("Cannot obtain view for " + identityName); } Utils.addSyncAccountToView(view, sync); Utils.addRoleToView(view, CGATE_ROLE); Data.checkinView(view); } catch (Exception e) { if (view != null) { Data.releaseViewLock(view); } } </script> </event> <transition to="end" /> </node> <end-state name="end" /> </process-definition> 25 Kapitola 6. Příklady obvyklých akcí 6.12. Transformační pravidlo <?xml version="1.0" encoding="UTF-8"?> <!-řetězec na vstupu vrací dekódovaný z Base64. Pokud je na vstupu null, vrací prázdný řetězec. --> <rule-definition xmlns="urn:jbpm.org:bcv_rule-1.0" name="transformBase64StringToString"> import eu.bcvsolutions.idm.data.Data; import eu.bcvsolutions.idm.data.dto.Criteria; import eu.bcvsolutions.idm.data.view.View; import sun.misc.BASE64Decoder; String attrValue = (String) this.interpreter.get("attrValue"); BASE64Decoder decoder = new BASE64Decoder(); if (attrValue == null) { return ""; } else { return new String(decoder.decodeBuffer(attrValue)); } </rule-definition> 6.13. Pravidlo pro vyplnění hodnoty atributu na koncovém systému <?xml version="1.0" encoding="UTF-8"?> <!-Vrací řetězec "krestniJmeno prijmeni tituly", na vstupu dostane userView --> <rule-definition xmlns="urn:jbpm.org:bcv_rule-1.0" name="getFullNameWithTitle"> import eu.bcvsolutions.idm.data.Data; import eu.bcvsolutions.idm.data.dto.Criteria; import eu.bcvsolutions.idm.data.view.View; import eu.bcvsolutions.idm.app.Application; View userView = this.interpreter.get("userView"); String firstName = (String) userView.get("firstName"); if (firstName == null) { firstName = ""; } String lastName = (String) userView.get("lastName"); if (lastName == null) { lastName = ""; } // Nacteme tituly z uctu v personalistice String[] pathToTitle = {"accounts", HR_SYSTEM, HR_SYSTEM_SCHEMA, "tituly"}; String title = (String) userView.get(pathToTitle); if (title == null) { title = ""; } Application.logInfo("getFullNameWithTitle: " + lastName + " " + firstName + " " + title, new Object[0]); StringBuilder builder = new StringBuilder(); 26 Korelační pravidlo builder.append(lastName + " " + firstName); if (!"".equals(title)) { builder.append(" " + title); } return builder.toString(); </rule-definition> 6.14. Korelační pravidlo <?xml version="1.0" encoding="UTF-8"?> <!-k danemu uctu nalezne uzivatele na zaklade porovnani atributu "cn" na systemu a extended atributu "loginName" u identity --> <rule-definition xmlns="urn:jbpm.org:bcv_rule-1.0" name="unixLdapCorrelationByLogin"> import eu.bcvsolutions.idm.data.Data; import eu.bcvsolutions.idm.data.dto.Criteria; import eu.bcvsolutions.idm.data.dto.DTO; import eu.bcvsolutions.idm.data.view.View; import eu.bcvsolutions.idm.data.view.View.Type; import eu.bcvsolutions.idm.data.dto.Criteria; import eu.bcvsolutions.idm.data.dto.CriteriaElement.Relation; import eu.bcvsolutions.idm.data.dto.CriteriaElement; import eu.bcvsolutions.idm.data.view.UserView; import eu.bcvsolutions.idm.app.Application; //atributy uctu na koncovem systemu DTO attributes = this.interpreter.get("resourceAttributes"); //hodnota atributu cn String nameAttr = attributes.get("cn"); //pokud ucet nema atribut cn, je asi chybny, a tedy ho s nikym nebudeme parovat if (nameAttr == null) { return null; } List idList = null; //hledame uzivatele, jehoz extended atribut "loginName" se shoduje s atributem "cn" na systemu Criteria criteria = new Criteria(); criteria.createAlias("extendedAttributes", "attrs"); criteria.add("attrs.name","loginName", Relation.EQ); criteria.add("attrs.value", nameAttr, Relation.EQ); //identity, ktere by to mohly splnovat idList = Data.listIds(View.Type.USER, criteria); String result = null; //jedina nalezena identita, to je ona if (idList.size() == 1) { result = idList.get(0); //nalezeno vic identit, tedy chyba } else if (idList.size() > 1) { System.out.println("More than one identity with the same loginName!"); return null; } else { System.out.println("No identity with this loginName!"); return null; 27 Kapitola 6. Příklady obvyklých akcí } System.out.println("Correlation result: " + result); return result; </rule-definition> 6.15. Spuštění pravidla //spusteni pravidla s parametry predanymi v mape Map params = new HashMap(); params.put("userName", "novakj"); Object vystup = Application.executeRule("nazevMehoPravidla", params); //nebo ekvivalentne s parametry predanymi v poli Object vystup2 = Application.executeRule2("nazevMehoPravidla", new Object[] {"userName", "novakj"}); 6.16. Spuštění workflow //prvni moznost spusteni, parametry uvedene v mape Map params = new HashMap(); params.put("userName", "novakj"); Application.startWorkflow("nazevMehoWorkflow", params); //druha moznost spusteni, parametry uvedene v poli Application.startWorkflow2("nazevMehoWorkflow", new Object[] {"userName", "novakj"}); //prvni moznost spusteni workflow asynchronne (neceka se na skonceni), parametry uvedene v mape params = new HashMap(); params.put("userName", "novakj"); Application.startWorkflowAsynchronously("nazevMehoWorkflow", params); //druha moznost spusteni workflow asynchronne (neceka se na skonceni), parametry uvedene v poli Application.startWorkflowAsynchronously2("nazevMehoWorkflow", new Object[] {"userName", "novakj"}); 28 Tipy a triky 7.1. O čem bude řeč V této kapitolce najdeš příkazy, které by se ti při práci mohly hodit. Týkají se gitu, práce s JBossem, práce s repository, připojení na vzdálený server pomocí ssh a tak podobně. Kdybys při práci přišel na něco užitečného, neváhej to sem dopsat. 7.2. Git Stažení repository pro nový projekt (v našem případě VFN) ve vývojovém adresáři: $ git remote add vfn [email protected]:vfn.git $ git fetch vfn Stažení vzdálené větve a nastavení upstreamu (pushování na server): $ git checkout -b chmumaster chmu/master 7.3. JBoss Zapnutí JBosse: $ service jboss start Restart JBosse: $ service jboss restart Vypnutí JBosse: $ service jboss stop Sledování logů JBosse, pokud je proměnná JBOSS_HOME nastavena na /opt/java/jboss: $ tail -f /opt/java/jboss/server/default/log/server.log 29 Kapitola 7. Tipy a triky nebo takto (shift + G skočí na konec): $ less /opt/java/jboss/server/default/log/server.log Adresář, ve kterém je deployováno naše CzechIdM: $ cd /opt/java/jboss/server/default/deploy 7.4. Repository Nastavení hesla uživatele "admin" SQL příkazem na hodnotu "admin" (až to budeš používat, smaž z té hexadecimální zběsilosti ten nový řádek): update identities set password_digest= 0xC7AD44CBAD762A5DA0A452F9E854FDC1E0E7A52A38015F23F3EAB1D80B931DD472634 DFAC71CD34EBC35D16AB7FB8A90C81F975113D6C7538DC69DD8DE9077EC where name='admin'; Zazálohování repository $ mysqldump -u root bcv_idm_repository > dump.sql 7.5. SSH Ke vzdálenému serveru se připojíš následujícím způsobem (připojujeme se třeba na testovací prostředí ČHMÚ): nejprve do souboru /etc/hosts připiš #CHMI testovaci 10.5.75.25 idmchmu Tím jsi řekl, že IP adrese 10.5.75.25 chceš říkat "idmchmu". Pak do souboru ~/.ssh/config připiš: Host idmchmu User root Port 2233 To znamená, že až se budeš chtít připojit na "idmchmu", budeš se defaultně připojovat na port 2233 pod uživatelem "root". Nakonec napíšeš samotný příkaz: 30 SSH $ ssh idmchmu V tomto okamžiku to po tobě chce heslo na uživatele "root", nebo heslo ke tvému klíči, pokud je na vzdáleném serveru nastaven jako jeden z důvěryhodných klíčů. Pokud ses na server nějak dostal a chceš svůj klíč přidat mezi důvěryhodné, abys pořád nemusel psát heslo, zkopíruj svůj veřejný klíč na vzdálený server a zavolej: $ cat id_rsa.pub >> /root/.ssh/authorized_keys Jako uživatel root se nyní můžeš přihlašovat i svým klíčem. Občas je zapotřebí vytvořit tunel ze vzdáleného serveru na tvůj lokální počítač. Hodí se to, pokud se třeba chceš podívat do vzdálené repository našeho CzechIdM. Řekněme, že mysql na vzdáleném serveru běží na portu 3306. Potom musíš do ~/.ssh/config připsat jeden řádek: Host idmchmu User root Port 2233 LocalForward 3316 localhost:3306 Až se příště připojíš přes ssh na idmchmu, naváže se tunel mezi vzdáleným portem 3306 a tvým portem 3316. Díky tomu můžeš číst vzdálenou repository ze svého lokálního portu 3316. Pokud chceš ze svého počítače něco zkopírovat na vzdálený, použij příkaz scp. Takto třeba zkopíruješ svůj soubor mujsoubor.txt do adresáře /opt/czechidm na vzdálený server: $ scp mujsoubor.txt idmchmu:/opt/czechidm/ 31 32 Příloha A. Revision History Revize 1.1-0 Sun Dec 29 2012 Vojtěch Matocha [email protected] Vytvoření dokumentu. 33 34 Rejstřík 35 36
Podobné dokumenty
czechidm-progdoc - Programátorská dokumentace
5.4. Metody související s workflow na třídě Application ........................................................
5.5. Výstupní hodnota .................................................................
A) Přehled.........................................................
Takže máme nastavené barvy a vpravo vidíme skoro konečný výsledek.
Přímo ve vzhledu emailu, můžeme nastavit hlavičku.
Hlavička může obsahovat buď obrázek, nebo nějaký text.
Klikneme na „Image“ a vl...
systémový projekt
účely systémového projektu centrálního rozhraní sítí pro Krajský úřad Vysočina, a proto tento
materiál ani informace v něm obsažené nesmí být poskytnuty nebo vyzrazeny jiné straně
nebo použity pro ...
O soutěži Antivirový program avast! Free Antivirus 6
Koncem roku můžete opět čekat finále. V každé kategorii bude zvolen jeden vítěz. I když ke každé soutěži finále patří, určitě není tím nejpodstatnějším. Důležitější než absolutní vítězství je totiž...