Společnost Amazon miliardáře Jeffa Bezose vypustila na oběžnou dráhu první várku družic svého projektu Kuiper, který má z vesmíru poskytovat vysokorychlostní internetové připojení po celém světě a snažit se konkurovat nyní dominantnímu Starlinku nejbohatšího muže planety Elona Muska.
Poslední aktualizací začal model GPT-4o uživatelům příliš podlézat. OpenAI jej tak vrátila k předchozí verzi.
Google Chrome 136 byl prohlášen za stabilní. Nejnovější stabilní verze 136.0.7103.59 přináší řadu novinek z hlediska uživatelů i vývojářů. Podrobný přehled v poznámkách k vydání. Opraveno bylo 8 bezpečnostních chyb. Vylepšeny byly také nástroje pro vývojáře.
Homebrew (Wikipedie), správce balíčků pro macOS a od verze 2.0.0 také pro Linux, byl vydán ve verzi 4.5.0. Na stránce Homebrew Formulae lze procházet seznamem balíčků. K dispozici jsou také různé statistiky.
Byl vydán Mozilla Firefox 138.0. Přehled novinek v poznámkách k vydání a poznámkách k vydání pro vývojáře. Řešeny jsou rovněž bezpečnostní chyby. Nový Firefox 138 je již k dispozici také na Flathubu a Snapcraftu.
Šestnáctý ročník ne-konference jOpenSpace se koná 3. – 5. října 2025 v Hotelu Antoň v Telči. Pro účast je potřeba vyplnit registrační formulář. Ne-konference neznamená, že se organizátorům nechce připravovat program, ale naopak dává prostor všem pozvaným, aby si program sami složili z toho nejzajímavějšího, čím se v poslední době zabývají nebo co je oslovilo. Obsah, který vytvářejí všichni účastníci, se skládá z desetiminutových
… více »Richard Stallman přednáší ve středu 7. května od 16:30 na Technické univerzitě v Liberci o vlivu technologií na svobodu. Přednáška je určená jak odborné tak laické veřejnosti.
Jean-Baptiste Mardelle se v příspěvku na blogu rozepsal o novinkám v nejnovější verzi 25.04.0 editoru videa Kdenlive (Wikipedie). Ke stažení také na Flathubu.
TmuxAI (GitHub) je AI asistent pro práci v terminálu. Vyžaduje účet na OpenRouter.
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.
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.
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ě.
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
.
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 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 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ší.
order
- určuje pořadí konfiguračního údaje ve výpisu všech konfiguračních údajůdisplayMessageKey
- slouží jako klíč k názvu konfiguračního údaje (tyto popisky jsou v property souboru Messages.properties a jsou načítány metodou getMessage)helpMessageKey
- popisek konfiguračního údaje, opět jako klíč do property souborurequired
- určuje, jestli je daný údaj povinnýconfidential
- určuje, zde má být hodnota zašifrována
@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");
}
}
/**
* 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();
}
/**
* 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;
}
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:
/**
* 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.
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
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.
Tiskni
Sdílej:
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 :).