Jak na CzechIdM - Úvod pro programátory systému CzechIdM

Transkript

Jak na CzechIdM - Úvod pro programátory systému CzechIdM
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

czechidm-progdoc - Programátorská dokumentace 5.4. Metody související s workflow na třídě Application ........................................................ 5.5. Výstupní hodnota .................................................................

Více

A) Přehled.........................................................

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...

Více

systémový projekt

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 ...

Více

O soutěži Antivirový program avast! Free Antivirus 6

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ž...

Více