abclinuxu.cz AbcLinuxu.cz itbiz.cz ITBiz.cz HDmag.cz HDmag.cz abcprace.cz AbcPráce.cz
AbcLinuxu hledá autory!
Inzerujte na AbcPráce.cz od 950 Kč
Rozšířené hledání
×
včera 20:59 | Zajímavý software
Projekt cwrap.org vydal nový přírůstek do své rodiny wrapperů. Tentokrát se jedná o nástroj resolv_wrapper, který je možné v unit testu použít k přesměrování DNS dotazů na vlastní testovací server nebo aplikaci vracet výsledky DNS dotazů z připraveného souboru. O resolv_wrapperu, stejně jako o ostatních knihovnách cwrap.org se můžete dozvědět více přímo na stránkách projektu cwrap.org.
jakubh | Komentářů: 0
včera 16:08 | Zajímavý projekt
Dlouhodbou absenci kvalitního IPC pro GNU/Linux řeší projekt kdbus Grega Kroah-Hartmana. Doposud nezávislý vývoj dospěl do stadia zařazení do hlavní větve jádra, s čímž se počítá pro verzi 3.19.
Petr Ježek | Komentářů: 48
29.10. 18:09 | Bezpečnostní upozornění
Byl vydán GNU Wget 1.16. Nejnovější verze tohoto programu pro stahování souborů pomocí protokolů HTTP, HTTPS a FTP řeší také bezpečnostní problém CVE-2014-4877. Rekurzivní stahování z FTP serveru ovládaného útočníkem může vést k vytváření libovolných souborů, adresářů a symbolických odkazů na lokálním souborovém systému (R7-2014-15).
Ladislav Hagara | Komentářů: 4
29.10. 17:26 | IT novinky
Organizace W3C včera vydala doporučení k používání HTML5 a označila tím tento webový standard za hotový. HTML5 je již delší dobu široce využívaným standardem ve vývoji webových aplikací a v mnoha případech se mu daří vytlačovat z webu proprietární řešení.
Pavel Půlpán | Komentářů: 6
29.10. 11:11 | Zajímavý projekt
Byl spuštěn třináctý Humble Indie Bundle. Do 11. listopadu lze koupit za vlastní cenu multiplatformní hry OlliOlli, Insanely Twisted Shadow Planet, Tower of Guns a při nadprůměrné platbě také Amnesia: A Machine for Pigs, Jazzpunk, Risk of Rain (plus 3 kopie k darování) a další zatím neznámé hry. Při platbě 12 a více dolarů přibude Shadowrun Returns a při platbě 65 a více dolarů také mikina, sběratelské karty a audiokazeta. V rámci Humble Bundle… více »
Ladislav Hagara | Komentářů: 6
28.10. 14:30 | Nová verze
Lwn.net informuje, že v rámci rc vydání linuxového jádra 3.18-rc2 byl (konečně) začleněn souborový systém overlayfs. Jde o tz. sjednocující souborový systém, který umožňuje zkombinovat několik souborových systémů do jednoho.
Martin B. | Komentářů: 34
28.10. 00:23 | IT novinky
Přibližně deset tisíc lidí se účastnilo protestů proti dani z internetu, kterou se snaží prosadit premiér Viktor Orbán. Video z demonstrace zachycuje rozzuřené lidi házející do vládní budovy monitory, klávesnice a kusy betonu. … více »
Bystroushaak | Komentářů: 275
26.10. 22:29 | Nová verze

Barry Kauler vydal novú verziu svojho minimalistického distra postaveného na deb balíčkoch z Ubuntu. Ako iste môžete tušiť, je použitý Utopic Unicorn. Výhodou pre tých, čo siahnú po Quirky, môže pre niekoho byť, že sa jedná o zostavenie bez systemd, ktoré obsahuje najnovšie jadro 3.16.6 a FreeOffice na 202MB xz archíve. Viac info.

skywaker | Komentářů: 8
26.10. 22:14 | Zajímavý článek
Nadace pro svobodný software (FSF) informuje o rozhodnutí italského Nejvyššího soudu zakázat praktiky odrazující uživatele od používání GNU/Linuxu tím, že je při koupi nového počítače nutí platit za licenci Windows. Výrobci nově nemohou odmítnout vrácení peněz za nepoužité licence. Prodej počítačů s předinstalovaným softwarem totiž není stejný jako prodej automobilů se 4 koly, motorem, atd. U počítače s předinstalovaným softwarem … více »
Ladislav Hagara | Komentářů: 72
24.10. 22:52 | Nová verze
Vývojáři Raw editoru RawTherapee vypustili verzi 4.2. Ta obsahuje nové nástroje (například barevné tónování, simulaci filmu), přidává podporu pro nové fotoaparáty a přináší řadu výkonnostních a paměťových optimalizací.
Marián Kyral | Komentářů: 12
Disketu jsem naposledy použil během
 (34%)
 (2%)
 (16%)
 (47%)
 (1%)
Celkem 300 hlasů
 Komentářů: 18, poslední včera 22:01
Rozcestník
Reklama
Autoškola testy online Levný benzín

Vývoj identity konektoru pro systém CzechIdM

20.6.2011 12:17 | Přečteno: 1199× | Práce | poslední úprava: 21.6.2011 22:30

Poslední dobou se moje pracovní povinnosti týkaly hlavně vývoje identity konektorů pro náš systém CzechIdM. Proto jsem se rozhodl, že napíši příspěvek, kde se pokusím letmo popsat problematiku Identity Managementu, Identity Connector Framework a vývoj konektorů v něm. Tento příspěvek zároveň volně navazuje na příspěvek mého kolegy Zdeňka Burdy o systému CzechIdM.

Úkolem Identity Managementu (IdM) je správa celého životní cyklu identit v různých systémech od vzniku identity, změn parametrů až po zánik identity. Zjednodušeně řešeno se jedná o systém, který spravuje uživatelské identity (kdo nebo co má přístup) a jejich vztah k jednotlivým aplikacím a datům (kam má přístup) a na základě jaké pravomoci získal přístupy (schváleno nadřízeným nebo dle role, organizačního zařazení atd.). Tyto informace jsou jednoduše auditovatelné a reportovatelné v čase, a to vše v souladu s bezpečnostními politikami. Aby to nebylo tak jednoduché, tak pod identitou si lze představit uživatelský účet, certifikát, reprezentace PC v doméně, ale i role a organizační strukturu. Každý druh identit mívá vlastní životní cyklus.

Výše popsané může vyvolávat dojem, že připojení koncových systémů k systému IdM musí vyžadovat jejich nemalé uzpůsobení pro podporu identity managementu. Opak je pravdou. Velkým přínosem IdM je snadnost připojení koncového systému bez nutnosti jeho přizpůsobení nebo dokonce i restartu. A jak na to? Využívají se obecnější přístupy mezi něž patří také tzv. identity konektory (Identity Connectors). A právě popisem vývoje jednoho takového konektoru v Identity Connector Frameworku pro náš systém CzechIdM se zabývá tento článek.

Nejprve si povíme, co jsou to konektory a jak se používají. Poté se dostaneme k samotnému vývoji konektoru v Javě. Zde si popíšeme jednotlivé části konektoru a jejich vývoj. Závěrem si ukážeme, jak napsat jednoduchou klientskou aplikaci na ověření požadované funkcionality konektoru.

Co to jsou identity konektory a k čemu slouží

Identity konektory (dále jen konektory) jsou aplikační komponenty, prostřednictvím kterých se spravují uživatelské účty (a nemusí se jednat pouze o ně) na koncových systémech. Tvoří k nim jakousi fasádu, tj. poskytují jednotné rozhraní pro práci s nimi. Koncové systémy jsou v tomto případě všechny aplikace a systémy, které používají vlastní správu uživatelů, např. systémy pro sledování požadavků, HR systémy, mzdové systémy atd. Úkolem konektorů je poskytovat funkcionalitu pro připojení se k daným koncovým systémům a delegovat operace pro správu uživatelských účtů na tyto systémy.

Konektory obecně poskytují metody pro:

Toto jsou základní operace, které nám konektory poskytují pro práci s uživatelskými účty a skupinami. Obecně mohou konektory pracovat i s jinými objekty než jen s účty a skupinami, ale primárně jsou určeny a optimalizovány právě pro ně.

Pokud chceme používat nějaký konektor v naší aplikaci, tak potřebujeme mít:

  1. Identity Connector Framework.
  2. Daný konektor samozřejmě (označován také jako bundle).

Na oficiálních stránkách projektu Identity Connectors je k dispozici několik konektorů pro různé koncové systémy (od Google Apps až po LDAP). Framework lze získat z SVN-ka na adrese https://svn.java.net/svn/identityconnectors~svn (je potřeba se předem zaregistrovat). Po checkoutu máme k dispozici zdrojové kódy frameworku (jak pro Javu, tak i pro .NET) a zdrojové kódy několika konektorů. Právě zdrojové kódy konektorů jsou k nezaplacení při psaní vlastního konektoru, protože dokumentace není mnoho.

Dále předpokládejme, že jsme provedli checkou trunku do adresáře ${connector_framework}. Potom zdrojové kódy konektorů jsou v adresáři ${connector_framework}/trunk/projects/bundles a framework pro Javu v adresáři ${connector_framework}/trunk/projects/framework/java. Zde jsou pouze zdrojové kódy, a proto je potřeba provézt build frameworku. Ten se provádí ANTem dle konfiguračního souboru ${connector_framework}/trunk/projects/framework/java/build.xml.

Popis Identity Connector Frameworku

Identity Connector Framework lze rozdělit na dvě části:
  1. Connector API - aplikace používají toto rozhraní pro volání implementovaných metod konektoru.
  2. Connector SPI - SPI deklaruje rozhraní, které implementují SPI vývojáři při tvorbě nových konektorů.

Na obrázku níže je struktura Identity Connector Frameworku. Zde lze vidět, že prostřednictvím SPI komunikuje konektor s koncovými systémy a API poskytuje rozhraní pro volání metod konektoru.

Connector API

Connector API poskytuje jednotné rozhraní pro volání metod konektorů bez ohledu na to, jaké metody konektor implementuje. SPI vývojářům usnadňuje práci tím, že jsou zde již implementovány některé API funkcionality, které mohou jednoduše použít (s minimem vlastní implementace). Z pohledu klienta konektoru není většinou potřeba pro využívání těchto vlastností dělat vůbec nic, maximálně je nakonfigurovat.

Mezi poskytované API vlastnosti patří například:

Connector SPI

Connector SPI tvoří sada několika rozhraní, které vývojáři konektorů musí implementovat, pokud požadují, aby konektor poskytoval danou operaci. Pro každou operaci existuje ve frameworku vlastní rozhraní.

Seznam SPI operačních rozhraní:

Další zajímavým SPI rozhraním je PoolableConnector. V tomto rozhraní je deklarována metoda checkAlive, která ověřuje, zda je daná instance konektoru stále aktivní. Pokud instance aktivní již není (např. vypršel timeout na spojení s koncovým systémem), tak by měla vyhodit výjimku RuntimeException, což dává frameworku signál, že je potřeba vytvořit novou instanci konektoru. Jelikož se tato metoda volá často, tak by měla být doba jejího běhu co možná nejkratší.

Vývoj univerzálního SSH konektoru

Nyní se již pustíme do vývoje vlastního konektoru. Bude se jednat se o "univerzální SSH konektor". Co se skrývá za tímto názvem? Popis SSH konektoru: Implementované třídy (popř. rozhraní) SSH konektoru:

Třída SSHConfiguration

Tato třída dědí od třídy AbstractConfiguration a zapouzdřuje konfiguraci konektoru. V tomto případě se v konfiguraci uvádějí údaje potřebné pro připojení se ke koncovému systému (uživatelské jméno, heslo, adresa koncového systému, port atd.), cesty k jednotlivým skriptům pro operace konektoru atd..Třída má defaultní konstruktor a pro každý konfigurační údaj jsou zde metody "set" a "get", přičemž metoda "get" je anotována anotací @ConfigurationProperty. Níže je uveden příklad pro uživatelské jméno klienta, pod kterým se konektor připojuje na koncový systém. Anotace @ConfigurationProperty má několik možných parametrů.
@ConfigurationProperty(order = 3,
	displayMessageKey = "SSH_USER_NAME",
	helpMessageKey = "SSH_USER_HELP",
	required = true)
public String getUsername() {
	return username;
}
Tato třída je také odpovědná za validaci vstupních hodnot. K tomuto účelu slouží metoda validate. Ta pouze kontroluje syntaktickou stránku konfiguračních údajů, neměla by ověřovat dostupnost zdrojů (např. připojení k databázi). Pokud zadané údaje nejsou "well-formed", tam metoda vyhazuje výjimku typu RuntimeException. Implementace rozhraní AbstractConfiguration je vyžadováno a musí ho implementovat všechny konektory.
/**
 * Validuje konfiguraci konektoru. Kontroluje, zda jsou nastaveny všechny potřebné parametry.
 * Implementace by měla pouze kontrolovat syntaktickou stránku, tjn. jestli jsou vsechny potřebné
 * parametry "well-formed". Neměla by se snažit ověřovat dostupnost zdrojů, např. připojovat se k nim. 
 */
@Override
public void validate() {    	
	if (StringUtil.isBlank(getHost())) {
		throw new IllegalArgumentException("Hostname must be set.");
	}    	
	if (getPort() < 0 || getPort() >= 65535) {
		throw new IllegalArgumentException("Port must be in range between 1 to 65535.");
	}
	if (StringUtil.isBlank(getUsername())) {
		throw new IllegalArgumentException("Username must be specified.");
	} 
	if (!getEscapeMode().equals(SSHMessages.SSH_ESCAPE_MODE_DOUBLED) && !getEscapeMode().equals(SSHMessages.SSH_ESCAPE_MODE_BACKSLASH)) {
		throw new IllegalArgumentException("Escape mode must be BACKSLASH or DOUBLED");
	}    	
}

Třída SSHConnection

Úkolem této třídy je spravovat spojení s koncovým systémem. Konstruktoru této třídy se předává instance konfigurační třídy SSHConfiguration. Pro připojení ke koncovému systému slouží metoda startConnection, pro ukončení spojení slouží metoda dispose a pro testování spojení metoda test.
/**
 * Konstruktor třídy SSHConnection.
 * 
 * @param cfg konfigurace, tj. instance třídy SSHConfiguration
 * @throws Exception
 */
public SSHConnection(SSHConfiguration cfg) throws Exception {
	if (cfg == null) {
		throw new Exception("Configuration not set.");
	}    	
	config = cfg;    	    	    	    
} 

/**
 * Metoda pro vytvoření spojení s koncovým systémem. Pokud je uveden privátní klíč, tak
 * se implicitně použije pro autentizaci. Jinak se použije dvojice uživatelské jméno a
 * heslo. Pokud je uveden otisk veřejného klíče serveru, ke kterému se připojujeme, tak
 * se použije pro jeho verifikaci.
 *  
 * @return Instance třídy Session. 
 */
public Session startConnection() {		
	String privateKey = asString(config.getPrivkey());    	    	    	
	try {    		
		if (!StringUtil.isBlank(privateKey)) {				
			//Private key used for authentication
			log.info("Private key used for authentication.");
			createSSHConnectionWithPrivateKey(privateKey, config.getPrivkeyPassword());
		} else {
			//Authentication via password
			log.info("Authentication via password.");
			createSSHConnectionWithPassword(config.getPassword());
		}
	    	
		session.connect(SSHConfiguration.CONNECTION_TIMEOUT);					
		log.info("Succesfull connection.");
	} catch (Exception ex){
		log.error("Connecting to server failed. {0}",ex.getMessage());
		throw new ConnectionFailedException("Connecting to server failed.");
	} finally {
		//clear user password
		session.setPassword("");
	}
	return session;
}

/**
 * Metoda pro ukončení spojení.
 * {@inheritDoc}
 */
public void dispose() {
	log.info("Dispose connection.");    	
	if (session != null) {
		session.disconnect();
	}
}

/**
 * Metoda testující navázané spojení.
 * {@inheritDoc}
 */
public void test() {
	config.validate();    	
	startConnection();
	dispose();
}

Třídy SSHUserFilterTranslator a SSHGroupFilterTranslator

Tyto třídy dědí z AbstractFilterTranslator<String> a slouží pro vytváření vyhledávacích dotazů. Stačí implementovat pouze ty metody třídy AbstractFilterTranslator podle nichž chceme objekty vyhledávat. V našem případě stačilo implementovat metodu createEqualsExpression (viz. níže).
/**
 * Metoda vytvoří dotaz (ve formě atributů pro příslušný skript koncového systému) 
 * pro vyhledání uživatelského účtu dle specifikovaného uživatelského jména (prozatím). 
 * Uživatelské jméno (instance třídy Name nebo Uid) je uloženo jako atribut v objektu 
 * filter.	
 */
@Override
protected String createEqualsExpression(EqualsFilter filter, boolean not) {		
	if (not) {
		throw new UnsupportedOperationException("Not supported yet.");
	}
	
	String username = "";
	Attribute attrib = filter.getAttribute();	
	if (attrib == null) {
		return null;
	}
	
	if (attrib.is(Name.NAME)) {
		username = ((Name)attrib).getNameValue();
	} else if (attrib.is(Uid.NAME)) {
		username = ((Uid)attrib).getUidValue();
	} else if (attrib.getValue() != null) {
		username = (String)attrib.getValue().get(0);
	}		
	
	String operationName = SSHMessages.SSH_GETUSER;
	String header = SSHMessages.SSH_HEADER_ACCOUNTID;         
            
        String scriptParams = String.format("%s\n%s\n%s\n", operationName, header, username);
	return scriptParams;
}
Tato metoda vytvoří vyhledávací dotaz. Při vyhledávání konkrétního uživatele se vytvoří instance daného filtru (metodou createFilterTranslator třídy SSHConnector), vytvoří se odpovídající dotaz, který se poté předá jako parametr query metodě executeQuery (také ve třídě SSHConnector), která se již pokusí uživatelský záznam najít.
/**
 * Metoda slouží pro spuštění dotazu nad objekty koncového systému.
 */
public void executeQuery(ObjectClass oclass, String query, ResultsHandler handler, OperationOptions options) {    	    	
	ConnectorObject object = null;
	if (query == null) {
		//Vylistovat vsechny objekty dane tridy.
		Session session = connection.startConnection();
		Iterator<Name> it = getAllObjectNames(oclass).iterator();
		Name m = null;
		String scriptParams;
		try {
			while (it.hasNext()) {
				m = it.next();
				scriptParams = createGetQuery(oclass, m.getNameValue());				
				object = getConnectorObject(oclass, scriptParams, session);						
				if (object != null) {
					handler.handle(object);
				}							
			}
		} catch (ConnectorException ex) {
			throw new ConnectorException(ex.getMessage());
		} finally {
			if (session != null && session.isConnected()) {
				session.disconnect();
			}
		}			
	} else {
		//Vylistovat pouze zaznam odpovidajici danemu dotazu (query).			
		object = getConnectorObject(oclass, query, null);			
		if (object != null) {
			handler.handle(object);
		}
	}    	    	    	            
}

/**
 * Metoda navrací dle použité třídy objektů odpovídající filtr. 
 */
public FilterTranslator<String> createFilterTranslator(ObjectClass oclass, OperationOptions options) {
	if (oclass.is(ObjectClass.ACCOUNT_NAME)) {
		return new SSHUserFilterTranslator();
	} else if (oclass.is(ObjectClass.GROUP_NAME)) {
		return new SSHGroupFilterTranslator();
	}
	return null;
}

Rozhraní SSHMessages

Rozhraní SSHMessages slouží pouze pro definování použitých konstant v celém konektoru.

Třída SSHConnector

Je hlavní a nejpodstatnější třídou konektoru. Je anotována anotací @ConnectorClass a implementuje interface org.identityconnectors.framework.spi.Connector a SPI rozhraní operací, které má konektor podporovat. Jejími členskými proměnnými jsou instance tříd SSHConnection a SSHConfiguration. Vyžadován je bezparametrický konstruktor. Rozhraní org.identityconnectors.framework.spi.Connector deklaruje metody init(Configuration) a dispose, které jsou volány při každém spuštění operace nad koncovým systémem. Metoda init slouží pro získání konfigurace konektoru a pro navázání spojení, metoda dispose naopak spojení ukončuje. Spolu se třídou Configuration jsou to jediné třídy, které musejí být implementovány ve všech konektorech.
package org.identityconnectors.ssh;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.util.*;

import org.identityconnectors.common.StringUtil;
import org.identityconnectors.common.logging.Log;
import org.identityconnectors.common.security.GuardedString;
import org.identityconnectors.framework.common.exceptions.ConnectorException;
import org.identityconnectors.framework.common.objects.*;
import org.identityconnectors.framework.common.objects.AttributeInfo.Flags;
import org.identityconnectors.framework.common.objects.filter.FilterTranslator;
import org.identityconnectors.framework.spi.Configuration;
import org.identityconnectors.framework.spi.Connector;
import org.identityconnectors.framework.spi.ConnectorClass;
import org.identityconnectors.framework.spi.operations.*;
import org.identityconnectors.ssh.filters.SSHGroupFilterTranslator;
import org.identityconnectors.ssh.filters.SSHUserFilterTranslator;

import com.csvreader.CsvReader;
import com.csvreader.CsvWriter;
import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.Session;

/**
 * Třída implementující funkcionalitu poskytovanou SSH konektorem.
 * 
 * @author Jaromír Mlejnek
 */
@ConnectorClass(displayNameKey="SSH_Universal_Connector",
		configurationClass = SSHConfiguration.class)
public class SSHConnector implements Connector, CreateOp, DeleteOp, SearchOp<String>, 
	UpdateOp, SchemaOp, TestOp {
	
    private static Schema schema;        
    private static final String ENCODING = "UTF-8";
    
    private SSHConfiguration config;
    private SSHConnection connection;    
    
    private List<String> multiValueAttribs; 
	
    //Logger
    Log log = Log.getLog(SSHConnector.class);
	
    /**
     * Implicitní konstruktor.
     */
    public SSHConnector() {
    }    

   /**
    * Metoda navracející konfiguraci.
    */
    public Configuration getConfiguration() {
        return this.config;
    }
    
    /**
     * Metoda pro načtení konfigurace a inicializaci spojení. 
     */
    public void init(Configuration cfg) {    	
        config = (SSHConfiguration)cfg;
        try {
        	connection = new SSHConnection(config);        	
        } catch (Exception ex){
        	log.error("Exception during initialization.");
        	ex.printStackTrace();
        }
        
        if (config.getMultiValueAttributes() == null || config.getMultiValueAttributes().length == 0) {
        	multiValueAttribs = new ArrayList<String>();
        } else {
        	multiValueAttribs = Arrays.asList(config.getMultiValueAttributes());
        }          
    }
    
    /**
     * Metoda pro ukončení spojení.
     */
    public void dispose() {    	    
    	if (connection != null) {
    		connection.dispose();    		
    	}
    }
    
    /**
     * Metoda spouštějící test spojení. Pokud není spojení s koncovým systémem navázáno, tak 
     * metoda vyhodí výjimku.
     */
    public void test() {    	
    	log.info("SSHConnector - test");
    	connection.test();   	
    } 
    
    .
    .
    .
Nemá cenu podrobně rozebírat implementaci všech metod, podívejme se tedy alespoň na metodu create. Jejími parametry jsou: Předpokládejme, že chceme vytvořit uživatelský účet (parametr ObjectClass oclas bude ObjectClass.ACCOUNT). Načteme si cestu k příslušnému skriptu pro vytvoření uživatele na koncovém systému (do proměnné pathToScript) a zavoláme metodu createOrUpdateUser. Ta projde předávanou množinu atributů a vytvoří vstupní parametry příslušného skriptu (ve formátu CSV). Pak spustí příslušný skript a předá mu dané CSV parametry. Skript vstup rozparsuje, vytvoří uživatelský účet a navrátí konektoru jedinečný identifikátor tohoto účtu (opět ve formátu CSV). Ten pak konektor deleguje volající aplikaci jako instanci třídy Uid (podtřída třídy Attribute, slouží jako jednoznačný identifikátor objektů).
/**
 * Metoda pro zakládání objektu daného typu (ACCOUNT nebo GROUP) na koncovém systému.
 */
public Uid create(ObjectClass oclass, Set<Attribute> attrs, OperationOptions options) {    	    	    	
	String operationName = ""; 
	String pathToScript = "";
        Uid returnUid = null;    	        
    
	if (oclass.is(ObjectClass.ACCOUNT_NAME)) {
		operationName = SSHMessages.SSH_CREATEUSER;   		
		pathToScript = config.getCreateUser();    		
		checkPathToScript(pathToScript, operationName);    		
		returnUid = createOrUpdateUser(operationName, pathToScript, attrs);
		
	} else if (oclass.is(ObjectClass.GROUP_NAME)) {
		operationName = SSHMessages.SSH_CREATEGROUP;
		pathToScript = config.getCreateGroup();
		checkPathToScript(pathToScript, operationName);
		returnUid = createOrUpdateGroup(operationName, pathToScript, attrs);
	}            
        return returnUid;     	
}      

/**
 * Metoda provádí dle názvu operace, cesty ke skriptu a zadaných atributů příslušnou operaci s 
 * uživatelským účtem.
 *  
 * @param operationName název prováděné operace.
 * @param pathToScript cesta k danému skriptu. 
 * @param attrs množina zadaných atributů. 
 * @return Uid uživatelského účtu, který se vytvořil nebo měnil.
 */
private Uid createOrUpdateUser(String operationName, String pathToScript, Set<Attribute> attrs) {
	StringBuffer userHeader = new StringBuffer();
        List<String> dataForUserLine = new ArrayList<String>();
           
	Attribute attrib = null;
	Iterator<Attribute> it = attrs.iterator();
	while (it.hasNext()) {
		attrib = it.next();
		if (attrib.is(Name.NAME)) {							
			String name = getName(attrib);
			userHeader.append(SSHMessages.SSH_HEADER_ACCOUNTID);
			userHeader.append(SSHConfiguration.DELIMITER);
			dataForUserLine.add(name);								
		} else if (attrib.is(OperationalAttributes.PASSWORD_NAME)) {					        
	                userHeader.append(SSHMessages.SSH_HEADER_PASSWORD);
	                userHeader.append(SSHConfiguration.DELIMITER);
	                dataForUserLine.add(getPassword(attrib));		        
		} else {
			userHeader.append(attrib.getName());
    		        userHeader.append(SSHConfiguration.DELIMITER);
    		        dataForUserLine.add(getAttributeValue(attrib));
		}    			
	}
	//Odstranime delimiter na konci radku
	userHeader = removeLastChar(userHeader);
	
	String scriptParams = createCommandCSV(operationName, userHeader, dataForUserLine);			
	String result = runCommand(pathToScript, scriptParams);		
	String accountUid = "";
	try {
		CsvReader reader = CsvReader.parse(result);
		reader.setDelimiter(SSHConfiguration.DELIMITER);
		reader.setEscapeMode(getCsvReaderMode());
		reader.readHeaders();
		reader.readRecord();
		accountUid = reader.get(0);
	} catch (IOException ioExc) {
		log.error("Exception during read from CSV file. \nError: {0}", ioExc.getMessage());
	} 	
	if (StringUtil.isBlank(accountUid)) {
		return null;
	}
	return new Uid(accountUid);
}
Na podobném principu pracují i ostatní operace (metody update, delete atd.). Vždy se určí cesta k odpovídajícímu skriptu, sestaví se jeho vstupní parametry ve formátu CSV, které se mu následně předají, skript se provede a případný výstup je předán konektoru (opět jako CSV). Konektor výstup rozparsuje a dále dle potřeby zpracuje. Posledním krokem, co musíme při vývoji konektoru udělat, je napsat ANT build skript build.xml, podle kterého se provede build konektoru. Není to nic těžkého, protože zde stačí pouze uvézt cestu ke konfiguračnímu souboru connector_build.xml (standardně se nachází v adresáří ${connector_framework}/trunk/projects/framework/java). V tomto souboru jsou uvedeny všechny potřebné cesty k třídám frameworku atd. Pokud tedy neproběhne build konektoru bez problému, tak je nejspíše chybně nastavená některá z cest v tomto souboru. V našem případě obsahuje soubor build.xml následující řádky.
<project name="connector-ssh" default="all">
    <property name="framework.dir" value="../java"/>
    <import file="${framework.dir}/connector_build.xml"/>
</project>
Spolu s build skriptem je ještě potřeba vytvořit build.properties soubor, ve kterém je uvedena použitá verze franeworku a název konektoru. V našem případě:
MAJOR=1
MINOR=0
ConnectorBundle-FrameworkVersion=1.0
ConnectorBundle-Name=org.identityconnectors.ssh
connectorName=org.identityconnectors.ssh.SSHConnector
Nyní již můžeme provézt buidl konektoru ANTem.

Vzorový klient konektoru

Nyní, když již máme vytvořený náš SSH konektor, tak se podíváme, jakým způsobem ho můžeme použít. Ukážeme si, jak napsat jednoduchou aplikaci, která bude tento konektor používat pro správu uživatelů na systému Request Tracker.

Vytvoříme si obyčejný Java projekt, přidáme do něho knihovny connector-framework.jar a connector-framework-internal.jar a můžeme začít. Vytvoříme hlavní metodu main, do které zapíšeme následující kód.

try {
	// 1. Soubor s konektorem.
	File bundleDirectory = new File("/home/jarda/workspace/Connector_Client/bundles");
	URL db1Url = IOUtil.makeURL(bundleDirectory, "org.identityconnectors.ssh-1.0.956.jar");			
	
	// 2. Inicializace connector manageru, který udržuje seznam konektorů (přesněji informace o nich).
	ConnectorInfoManagerFactory factory = ConnectorInfoManagerFactory.getInstance();
	ConnectorInfoManager manager = factory.getLocalManager(db1Url);
	
	List<ConnectorInfo> connectorInfos = manager.getConnectorInfos();
	
	// 3. Vypsání dostupných konektorů.
	System.out.println("Available connector bundles:");
	for (ConnectorInfo connectorInfo : connectorInfos) {
		ConnectorKey key = connectorInfo.getConnectorKey();
		System.out.println(key.toString());
	}
	
	// 4. Identifikátor určitého konektoru.
	ConnectorKey ffKey = new ConnectorKey("org.identityconnectors.ssh",
	"1.0.956",
	"org.identityconnectors.ssh.SSHConnector");			
	 ConnectorInfo ffConInfo = manager.findConnectorInfo(ffKey);
	 
	 // 5. Načítá defaultní konfiguraci konektoru.
	 APIConfiguration ffConfig = ffConInfo.createDefaultAPIConfiguration();
	
	 // 6. Vypsání názvů konfiguračních údajů. 
	 ConfigurationProperties ffConfigProps = ffConfig.getConfigurationProperties();
	 for (String name : ffConfigProps.getPropertyNames()) {
		 System.out.println("property: " + name);
	 }	
	 			 
	 // 7. Vypsání názvů konektorem podporovaných operací.
	 for (Class<? extends APIOperation> supportedOp : ffConfig.getSupportedOperations()) {
		 System.out.println("supported operation: " + supportedOp.getSimpleName());
	 }
	 
	 // 8. Konfigurace konektoru.
	 ffConfigProps.setPropertyValue("host", "localhost");
	 ffConfigProps.setPropertyValue("port", 22);
	 ffConfigProps.setPropertyValue("username", "test");
	 ffConfigProps.setPropertyValue("password", new GuardedString("demo1234".toCharArray()));			 			 			 
	 
	 ffConfigProps.setPropertyValue("user", "/home/jarda/workspace/bcv-idm/Realizace/ssh_connector/skripty_RT/sshuni_rt.sh");
	 ffConfigProps.setPropertyValue("createUser", "/home/jarda/workspace/bcv-idm/Realizace/ssh_connector/skripty_RT/sshuni_rt.sh");
	 ffConfigProps.setPropertyValue("deleteUser", "/home/jarda/workspace/bcv-idm/Realizace/ssh_connector/skripty_RT/sshuni_rt.sh");
	 ffConfigProps.setPropertyValue("enableUser", "/home/jarda/workspace/bcv-idm/Realizace/ssh_connector/skripty_RT/sshuni_rt.sh");
	 ffConfigProps.setPropertyValue("disableUser", "/home/jarda/workspace/bcv-idm/Realizace/ssh_connector/skripty_RT/sshuni_rt.sh");
	 ffConfigProps.setPropertyValue("updateUser", "/home/jarda/workspace/bcv-idm/Realizace/ssh_connector/skripty_RT/sshuni_rt.sh");			 			 
	
	 ffConfigProps.setPropertyValue("listObjects", "/home/jarda/workspace/bcv-idm/Realizace/ssh_connector/skripty_RT/sshuni_rt.sh");
	 
	 ffConfigProps.setPropertyValue("escapeMode", "DOUBLED");
	 ffConfigProps.setPropertyValue("multiValueAttributes", new String[] {});			 
	 ffConfigProps.setPropertyValue("multiValueAttributesSeparator", ',');			 
	 
	 // 9. Vytvoření instance konektoru, validace konfiguračních údajů, test spojení.
	 ConnectorFacade ffConnector = ConnectorFacadeFactory.getInstance().newInstance(ffConfig);				           
	 ffConnector.validate();
	 ffConnector.test();
	 
	 //Nyní již můžeme nad konektorem volat implementované metody.
	 
	 //Vytvoření nového uživatelského účtu "sokrates11".
	 Set<Attribute> attrs = new HashSet<Attribute>();
	 attrs.add(AttributeBuilder.build("Name", "sokrates1"));
	 attrs.add(AttributeBuilder.build("EmailAddress", "sokrates1@example.com"));
	 attrs.add(AttributeBuilder.build("Gecos", "sokr1"));
	 Uid uid = ffConnector.create(ObjectClass.ACCOUNT, attrs, null);
	 System.out.println("New users's UID: " + uid.getUidValue());
	 
	 //Vyhledání uživatele "sokrates11".
	 ffConnector.getObject(ObjectClass.ACCOUNT, uid, null);
	 
	 //Smazání uživatele "sokrates11".
         ffConnector.delete(ObjectClass.ACCOUNT, uid, null);			 
		 
} catch (Exception ex) {
	ex.printStackTrace();
}									
V prvním kroku nastavíme cestu k danému konektoru. Dále si vytvoříme instanci třídy ConnectorInfoManager, která udržuje seznam načtených konektorů. Ve třetím kroku si všechny načtené konektory vypíšeme. Poté si ve čtvrtém kroku vytvoříme referenci na konektor a v pátém načteme defaultní API konfiguraci. V šestém kroku vypíšeme názvy všech konfiguračních údajů a v sedmém všechny podporované operace na koncovém systému. V kroku číslo 8 nastavíme požadované konfigurační údaje a v devátém kroku již vytvoříme instanci konektoru. Nyní již můžeme volat požadované metody. Zkusíme si tedy vytvořit nový uživatelský účet na systému Request Tracker, pak ho vyhledáme a na závěr smažeme. Výstup celého běhu klientské aplikace je níže.
Available connector bundles:
ConnectorKey( bundleName=org.identityconnectors.ssh bundleVersion=1.0.956 connectorName=org.identityconnectors.ssh.SSHConnector )
property: host
property: port
property: username
property: password
property: privkey
property: privkeyPassword
property: hostkey
property: user
property: createUser
property: deleteUser
property: enableUser
property: disableUser
property: updateUser
property: group
property: createGroup
property: deleteGroup
property: updateGroup
property: listObjects
property: escapeMode
property: multiValueAttributes
property: multiValueAttributesSeparator
supported operation: GetApiOp
supported operation: SearchApiOp
supported operation: ScriptOnConnectorApiOp
supported operation: SchemaApiOp
supported operation: DeleteApiOp
supported operation: ValidateApiOp
supported operation: TestApiOp
supported operation: UpdateApiOp
supported operation: CreateApiOp
Thread Id: 1	Time: 2011-06-05 16:30:03.223	Class: org.identityconnectors.framework.api.operations.ValidateApiOp	Method: validate	Level: OK	Message: Enter: validate()
Thread Id: 1	Time: 2011-06-05 16:30:03.229	Class: org.identityconnectors.framework.api.operations.ValidateApiOp	Method: validate	Level: OK	Message: Return: null
Thread Id: 1	Time: 2011-06-05 16:30:03.240	Class: org.identityconnectors.framework.api.operations.TestApiOp	Method: test	Level: OK	Message: Enter: test()
Thread Id: 1	Time: 2011-06-05 16:30:03.267	Class: org.identityconnectors.ssh.SSHConnector	Method: test	Level: INFO	Message: SSHConnector - test
Thread Id: 1	Time: 2011-06-05 16:30:03.267	Class: org.identityconnectors.ssh.SSHConnection	Method: startConnection	Level: INFO	Message: Authentication via password.
Thread Id: 1	Time: 2011-06-05 16:30:03.574	Class: org.identityconnectors.ssh.SSHConnection	Method: startConnection	Level: INFO	Message: Succesfull connection.
Thread Id: 1	Time: 2011-06-05 16:30:03.575	Class: org.identityconnectors.ssh.SSHConnection	Method: dispose	Level: INFO	Message: Dispose connection.
Thread Id: 1	Time: 2011-06-05 16:30:03.587	Class: org.identityconnectors.framework.api.operations.TestApiOp	Method: test	Level: OK	Message: Return: null
Thread Id: 1	Time: 2011-06-05 16:30:03.605	Class: org.identityconnectors.framework.api.operations.CreateApiOp	Method: create	Level: OK	Message: Enter: create(ObjectClass: __ACCOUNT__, [Attribute: {Name=EmailAddress, Value=[sokrates1@example.com]}, Attribute: {Name=Gecos, Value=[sokr1]}, Attribute: {Name=Name, Value=[sokrates1]}], null)
Thread Id: 1	Time: 2011-06-05 16:30:03.618	Class: org.identityconnectors.ssh.SSHConnection	Method: startConnection	Level: INFO	Message: Authentication via password.
Thread Id: 1	Time: 2011-06-05 16:30:03.754	Class: org.identityconnectors.ssh.SSHConnection	Method: startConnection	Level: INFO	Message: Succesfull connection.
Thread Id: 1	Time: 2011-06-05 16:30:04.306	Class: org.identityconnectors.ssh.SSHConnection	Method: dispose	Level: INFO	Message: Dispose connection.
Thread Id: 1	Time: 2011-06-05 16:30:04.312	Class: org.identityconnectors.ssh.SSHConnection	Method: dispose	Level: INFO	Message: Dispose connection.
Thread Id: 1	Time: 2011-06-05 16:30:04.317	Class: org.identityconnectors.framework.api.operations.CreateApiOp	Method: create	Level: OK	Message: Return: Attribute: {Name=__UID__, Value=[user/143]}
New users's UID: user/143
Thread Id: 1	Time: 2011-06-05 16:30:04.322	Class: org.identityconnectors.framework.api.operations.GetApiOp	Method: getObject	Level: OK	Message: Enter: getObject(ObjectClass: __ACCOUNT__, Attribute: {Name=__UID__, Value=[user/143]}, null)
Thread Id: 1	Time: 2011-06-05 16:30:04.335	Class: org.identityconnectors.ssh.SSHConnection	Method: startConnection	Level: INFO	Message: Authentication via password.
Thread Id: 1	Time: 2011-06-05 16:30:04.439	Class: org.identityconnectors.ssh.SSHConnection	Method: startConnection	Level: INFO	Message: Succesfull connection.
Thread Id: 1	Time: 2011-06-05 16:30:04.838	Class: org.identityconnectors.ssh.SSHConnection	Method: dispose	Level: INFO	Message: Dispose connection.
Thread Id: 1	Time: 2011-06-05 16:30:04.858	Class: org.identityconnectors.ssh.SSHConnection	Method: dispose	Level: INFO	Message: Dispose connection.
Thread Id: 1	Time: 2011-06-05 16:30:04.859	Class: org.identityconnectors.framework.api.operations.GetApiOp	Method: getObject	Level: OK	Message: Return: {ObjectClass=ObjectClass: __ACCOUNT__, Attributes=[Attribute: {Name=Status, Value=[UNLOCK]}, Attribute: {Name=password, Value=[]}, Attribute: {Name=__UID__, Value=[user/143]}, Attribute: {Name=EmailAddress, Value=[sokrates1@example.com]}, Attribute: {Name=__NAME__, Value=[user/143]}, Attribute: {Name=Name, Value=[sokrates1]}], Name=Attribute: {Name=__NAME__, Value=[user/143]}, Uid=Attribute: {Name=__UID__, Value=[user/143]}}
Thread Id: 1	Time: 2011-06-05 16:30:04.862	Class: org.identityconnectors.framework.api.operations.DeleteApiOp	Method: delete	Level: OK	Message: Enter: delete(ObjectClass: __ACCOUNT__, Attribute: {Name=__UID__, Value=[user/143]}, null)
Thread Id: 1	Time: 2011-06-05 16:30:04.868	Class: org.identityconnectors.ssh.SSHConnection	Method: startConnection	Level: INFO	Message: Authentication via password.
Thread Id: 1	Time: 2011-06-05 16:30:05.040	Class: org.identityconnectors.ssh.SSHConnection	Method: startConnection	Level: INFO	Message: Succesfull connection.
Thread Id: 1	Time: 2011-06-05 16:30:05.132	Class: org.identityconnectors.ssh.SSHConnection	Method: dispose	Level: INFO	Message: Dispose connection.
Thread Id: 1	Time: 2011-06-05 16:30:05.134	Class: org.identityconnectors.ssh.SSHConnection	Method: dispose	Level: INFO	Message: Dispose connection.
Thread Id: 1	Time: 2011-06-05 16:30:05.134	Class: org.identityconnectors.framework.api.operations.DeleteApiOp	Method: delete	Level: OK	Message: Return: null

Závěrem

V tomto článku jsme si ukázali, co to jsou identity konektory, k čemu se používají v identity managementu. Zběžně jsme si popsali vývoj jednoho takového konektoru, který používáme v našem Identity Manageru CzechIdM. Jak již bylo uvedeno, tak oficiální dokumentace týkající se connector frameworku není mnoho. Případným zájemcům o bližší informace lze doporučit hlavně zdrojové kódy konektorů distribuovaných spolu s frameworkem a JavaDOC jednotlivých tříd. A snad i tento článek může sloužit jako takový letmý úvod do problematiky identity konektorů.

V případě dotazů mne nebo kolegy neváhete kontaktovat na mailu info@bcvsolutions.eu.

       

Hodnocení: 58 %

        špatnédobré        

Obrázky

Vývoj identity konektoru pro systém CzechIdM, obrázek 1

Tiskni Sdílej: Linkuj Jaggni to Vybrali.sme.sk Google Del.icio.us Facebook

Komentáře

Vložit další komentář

20.6.2011 16:03 kavol | skóre: 28
Rozbalit Rozbalit vše Re: Vývoj identity konektoru pro systém CzechIdM
Asimov by se divil ... nejen roboti maj identitu, už i pouhé konektory :-)
20.6.2011 20:41 moris | blog: Morisův Blog
Rozbalit Rozbalit vše Re: Vývoj identity konektoru pro systém CzechIdM
uznávám, že slovní spojení "identity konektory" je poněkud zavádějící :-)
pavlix avatar 21.6.2011 18:16 pavlix | skóre: 49 | blog: pavlix
Rozbalit Rozbalit vše Re: Vývoj identity konektoru pro systém CzechIdM
Zajímalo by mě, jestli je tenhle blogpost něco víc než jen reklama na proprietární software v open source blogu.

Franta už se ptal u toho minulého článku na licenci a NEDOSTAL odpověď. Byl bych docela nerad, aby se nám tu něco podobného rozmáhalo.

21.6.2011 18:23 pasmen | skóre: 45 | blog: glob | Praha
Rozbalit Rozbalit vše Re: Vývoj identity konektoru pro systém CzechIdM
Tady je někdo magor.
21.6.2011 20:27 Zdeněk Burda | skóre: 61 | blog: Zdendův blog | Praha
Rozbalit Rozbalit vše Re: Vývoj identity konektoru pro systém CzechIdM
Chceš určovat co kdo může psát do blogu?

Jestli je Jardův zápisek reklama nebo ne nebudu hodnotit. Do toho kde si svůj text zveřejní mu nekecám a to že píše o své práci snad není nic špatného. Kdyby spravoval unixové servery, bude asi psát o nich... Není náhodou reklama to co si cpeš do patičky?

V mém textu jsem hned na začátku psal "CzechIdM je open source Identity Manager šířený zdarma pod naší vlastní licencí." Nejsem si vědom toho, že bysme museli licenci publikovat veřejně. Až spolu budeme řešit služby ve vztahu zákazník-dodavatel, rád s tebou licenční podmínky proberu. Věř, že jsme se vhodnou licencí zabývali velmi intenzivně.

-- Nezdar není hanbou, hanbou je strach z pokusu.
pavlix avatar 21.6.2011 21:09 pavlix | skóre: 49 | blog: pavlix
Rozbalit Rozbalit vše Re: Vývoj identity konektoru pro systém CzechIdM
Chceš určovat co kdo může psát do blogu?
On můj dotaz nějak určuje, kdo co může do blogu psát?

Pokud chceš konzultovat, co je ok a co není, budeš se muset obrátit na provozovatele serveru :). Pravděpodobně tě nechá do blogu psát relativně cokoli, jen se rozhodne, jestli tomu blogu bude dělat reklamu (zobrazovat ho na seznamu nových blogpostů) nebo ne.
V mém textu jsem hned na začátku psal "CzechIdM je open source Identity Manager šířený zdarma pod naší vlastní licencí." Nejsem si vědom toho, že bysme museli licenci publikovat veřejně.
Franta se slušně zeptal pod minulým blogpostem a mně přišlo na místě, aby dostal alespoň slušnou odpověď. Nedostal žádnou (tedy ani informaci, že licence je tajná).

Jinak pokud jsou licenční podmínky tak tajné, tak je pro mě tvrzení, že se jedná o open source (alespoň v tom smyslu, jak ho definuje OSI) neověřená informace, když ani nevím, jestli autor toho tvrzení tomuto pojmu vůbec rozumí.
Až spolu budeme řešit služby ve vztahu zákazník-dodavatel, rád s tebou licenční podmínky proberu.
Břemeno získání důvěry bývá obvykle na straně dodavatele :).
21.6.2011 22:27 moris | blog: Morisův Blog
Rozbalit Rozbalit vše Re: Vývoj identity konektoru pro systém CzechIdM
Hlavním smyslem tohoto příspěvku je:
  1. Zjednodušeně popsat problematiku identity managementu.
  2. Ukázat, jakým způsobem lze připojit koncový systém k identity manageru.
  3. Zběžně popsat Identity Connector framework.
  4. Zběžně popsat vývoj konektoru pod daným frameworkem.
Nevidím důvod, proč při popisu vývoje konektoru nemůžu konstatovat, že je používán tím a tím systémem. Nechávám na každém, ať zhodnotí, zda převládá reklama, a nebo obecný popis dané problematiky.
pavlix avatar 21.6.2011 22:37 pavlix | skóre: 49 | blog: pavlix
Rozbalit Rozbalit vše Re: Vývoj identity konektoru pro systém CzechIdM
Já bohužel četl už ten předchozí.
22.6.2011 09:02 Lukáš Cirkva | skóre: 10 | Praha
Rozbalit Rozbalit vše Re: Vývoj identity konektoru pro systém CzechIdM
Příspěvek do blogu je o technologii Identity Connectors frameworku. Popisuje co to jsou identity konektory a k čemu slouží a obsahuje podrobnou ukázku praktické realizace SSH konektoru. Text je ryze technického rázu od vývojáře javy o vývoji a použití technologie, která je opensource (Oracle se jí snaží uzavřít). Tento příspěvek reaguje na četné dotazy, které jsme za poslední měsic obdrželi, například dotaz v blogu.

CzechIdM je SW s otevřeným kódem, do kterého může klient zasahovat. Informace o licenci jsou na webu CzechIdM nebo zde.

Kolegové začali psát o CzechIdM, protože si myslíme, že téma identity managementu a opensource řešení vyvíjené v ČR může čtenáře zajímat. Proč si to myslíme?
  • Máme dotazy na toto téma i z komunity. (Protože identity management děláme opravdu hodně dlouho i na jiných komerčních produktech, školíme a publikujeme...)
  • Na předchozí články o této tématice byla dobrá odezva v diskuzi a 100% hodnocení čtenáři viz: Správa uživatelů v síti a viz: Správa uživatelů v síti II
Zda sem psát o tématu identity managementu a CzechIdM, ať zhodnotí (a hodnotí u každého příspěvku) sami čtenáři. Zatím jsou u každého příspěvku kladné body 86% a 67% pro.

Založit nové vláknoNahoru

ISSN 1214-1267   Powered by Hosting 90 Server hosting
© 1999-2013 Argonit s. r. o. Všechna práva vyhrazena.