5 Javaを使用したアイデンティティ・コネクタの開発
この章は、Identity Connector Framework (ICF)とOracle Identity Managerメタデータを使用してアイデンティティ・コネクタを開発するのに必要な手順全体を確認するチュートリアルとなります。ここには、重要なICFクラスとインタフェース、コネクタ・バンドルおよびコネクタ・サーバーについての情報と、フラット・ファイル・アイデンティティ・コネクタの実装のコード例や、ユーザー・プロビジョニングとリコンシリエーションのプロセス用にOracle Identity Managerメタデータを作成するコード例が含まれています。
この章の構成は、次のとおりです。
5.1 フラット・ファイル・コネクタ開発の概要
フラット・ファイル・コネクタを開発するには、構成インタフェースの実装を開発し、その後、コネクタ・クラスを実装するという手順で開発する必要があります。
開始する前に、フラット・ファイル・コネクタのすべての操作用のIO表現モジュールを準備する必要があります。このことには、次のすべてまたは一部が含まれる場合があります。
-
フラット・ファイルの列名を読み込み、メタデータ情報を準備します。
-
指定のデリミタで区切られた、対応する列値を含むレコードをフラット・ファイルに追加します。
-
UID値に基づいて、フラット・ファイルへのレコードを削除します。
-
フラット・ファイルで検索操作を実行します。
このチュートリアルではアイデンティティ・コネクタの開発に焦点を当てているため、これらの準備の詳細は説明していません。
ノート:
次のサポート・クラスが、アイデンティティ・コネクタ操作時のファイル入力および出力の処理に使用されます。
-
org.identityconnectors.flatfile.io.FlatFileIOFactory
-
org.identityconnectors.flatfile.io.FlatFileMetadata
-
org.identityconnectors.flatfile.io.FlatFileParser
-
org.identityconnectors.flatfile.io.FlatFileWriter
入力および出力処理のサポート・クラスの実装については、ファイル入力および出力処理用のサポート・クラスを参照してください。
5.2 フラット・ファイル・コネクタの開発
フラット・ファイル・コネクタを開発するには、AbstractConfiguration、PoolableConnector、AbstractFilterTranslatorの各クラスを実装し、コネクタ・バンドルJARファイルを作成します。
この項では、フラット・ファイル・コネクタを開発する手順の概要をコード・サンプルとともに示します。次の項目が含まれます。
5.2.1 フラット・ファイル・コネクタの開発の概要
フラット・ファイル・コネクタを開発するには、AbstractConfiguration、PoolableConnector、AbstractFilterTranslatorの各クラスを実装し、コネクタ・バンドルJARファイルを作成します。
フラット・ファイル・コネクタを開発するには:
-
org.identityconnectors.framework.spi.AbstractConfigurationベース・クラスを拡張して、フラット・ファイル・コネクタ用の構成クラスを実装します。
構成クラスのサンプル実装については、AbstractConfigurationの実装を参照してください。
詳細は、org.identityconnectors.framework.spi.Configurationインタフェースを参照してください。
-
org.identityconnectors.framework.spi.Connectorインタフェースを実装して、フラット・ファイル・コネクタのコネクタ・クラスを作成します。
PoolableConnectorクラスのサンプル実装については、PoolableConnectorの実装を参照してください。
-
このコネクタは、ContainsAllValuesFilter操作のみをサポートします。ContainsAllValuesFilter操作を実装します。AbstractFilterTranslator<T>クラスのサンプル実装については、AbstractFilterTranslatorの実装を参照してください。
-
コネクタ・バンドルJARを作成します。MANIFEST.MFファイルには、次のエントリが含まれている必要があります。
-
ConnectorBundle-FrameworkVersion
-
ConnectorBundle-Name
-
ConnectorBundle-Version
MANIFEST.MFファイルの内容については、MANIFEST.MFファイルを参照してください。
-
-
ステップ4で作成したコネクタ・バンドルJARを更新します。そのように行うには:
-
コネクタ・バンドルJARを目的の場所に抽出します。
-
JARを抽出したディレクトリ内にlibディレクトリを作成します。
-
依存しているサード・パーティのJARをこのlibディレクトリに追加します。
-
ディレクトリ全体をJARします。
ノート:
MANIFEST.MFファイルには、ステップ4で示したエントリが含まれている必要があります。
-
5.2.2 AbstractConfigurationの実装
AbstractConfigurationベース・クラスは、フラット・ファイル・コネクタ用の構成クラスを実装するように拡張できます。
次に、AbstratConfigurationクラスのサンプル実装を示します。
package org.identityconnectors.flatfile; import java.io.File; import org.identityconnectors.flatfile.io.FlatFileIOFactory; import org.identityconnectors.framework.common.exceptions.ConfigurationException; import org.identityconnectors.framework.spi.AbstractConfiguration; import org.identityconnectors.framework.spi.ConfigurationProperty; /** * Class for storing the flat file configuration */ public class FlatFileConfiguration extends AbstractConfiguration { /* * Storage file name */ private File storeFile; /* * Delimeter used */ private String textFieldDelimeter; /* * Unique attribute field name */ private String uniqueAttributeName = ""; /* * Change attribute field name. Should be numeric */ private String changeLogAttributeName = ""; public File getStoreFile() { return storeFile; } public String getTextFieldDelimeter() { return textFieldDelimeter; } public String getUniqueAttributeName() { return uniqueAttributeName; } public String getChangeLogAttributeName() { return changeLogAttributeName; } /** * Set the store file * @param storeFile */ @ConfigurationProperty(order = 1, helpMessageKey = "USER_ACCOUNT_STORE_HELP", displayMessageKey = "USER_ACCOUNT_STORE_DISPLAY") public void setStoreFile(File storeFile) { this.storeFile = storeFile; } /** * Set the text field delimeter * @param textFieldDelimeter */ @ConfigurationProperty(order = 2, helpMessageKey = "USER_STORE_TEXT_DELIM_HELP", displayMessageKey = "USER_STORE_TEXT_DELIM_DISPLAY") public void setTextFieldDelimeter(String textFieldDelimeter) { this.textFieldDelimeter = textFieldDelimeter; } /** * Set the field whose values will be considered as unique attributes * @param uniqueAttributeName */ @ConfigurationProperty(order = 3, helpMessageKey = "UNIQUE_ATTR_HELP", displayMessageKey = "UNIQUE_ATTR_DISPLAY") public void setUniqueAttributeName(String uniqueAttributeName) { this.uniqueAttributeName = uniqueAttributeName; } /** * Set the field name where change number should be stored * @param changeLogAttributeName */ @ConfigurationProperty(order = 3, helpMessageKey = "CHANGELOG_ATTR_HELP", displayMessageKey = "CHANGELOG_ATTR_DISPLAY") public void setChangeLogAttributeName(String changeLogAttributeName) { this.changeLogAttributeName = changeLogAttributeName; } @Override public void validate() { // Validate if file exists and is usable boolean validFile = (this.storeFile.exists() && this.storeFile.canRead() && this.storeFile.canWrite() && this.storeFile.isFile()); if (!validFile) throw new ConfigurationException("User store file not valid"); // Validate if there is a field on name of unique attribute field name // Validate if there is a field on name of change attribute field name FlatFileIOFactory.getInstance(this); // Initialization does the validation } }
5.2.3 PoolableConnectorの実装
org.identityconnectors.framework.spi.Connectorインタフェースは、フラット・ファイル・コネクタのコネクタ・クラスを作成するために実装されています。
次のコード・サンプルでは、CreateOp、DeleteOp、SearchOpおよびUpdateOpの各インタフェースを実装するため、4つの操作すべてをサポートします。FlatFileMetadata、FlatFileParserおよびFlatFileWriterの各クラスは、サポート・クラスです。これらの実装は、ICFに属していないため表示されません。
package org.identityconnectors.flatfile; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import org.identityconnectors.flatfile.io.FlatFileIOFactory; import org.identityconnectors.flatfile.io.FlatFileMetadata; import org.identityconnectors.flatfile.io.FlatFileParser; import org.identityconnectors.flatfile.io.FlatFileWriter; import org.identityconnectors.framework.api.operations.GetApiOp; import org.identityconnectors.framework.common.exceptions.AlreadyExistsException; import org.identityconnectors.framework.common.exceptions.ConnectorException; import org.identityconnectors.framework.common.objects.Attribute; import org.identityconnectors.framework.common.objects.AttributeInfo; import org.identityconnectors.framework.common.objects.AttributeInfoBuilder; import org.identityconnectors.framework.common.objects.ConnectorObject; import org.identityconnectors.framework.common.objects.ConnectorObjectBuilder; import org.identityconnectors.framework.common.objects.ObjectClass; import org.identityconnectors.framework.common.objects.OperationOptions; import org.identityconnectors.framework.common.objects.ResultsHandler; import org.identityconnectors.framework.common.objects.Schema; import org.identityconnectors.framework.common.objects.SchemaBuilder; import org.identityconnectors.framework.common.objects.Uid; import org.identityconnectors.framework.common.objects.filter.AbstractFilterTranslator; import org.identityconnectors.framework.common.objects.filter.FilterTranslator; import org.identityconnectors.framework.spi.Configuration; import org.identityconnectors.framework.spi.ConnectorClass; import org.identityconnectors.framework.spi.PoolableConnector; import org.identityconnectors.framework.spi.operations.CreateOp; import org.identityconnectors.framework.spi.operations.DeleteOp; import org.identityconnectors.framework.spi.operations.SchemaOp; import org.identityconnectors.framework.spi.operations.SearchOp; import org.identityconnectors.framework.spi.operations.UpdateOp; /** * The main connector class */ @ConnectorClass(configurationClass = FlatFileConfiguration.class, displayNameKey = "FlatFile") public class FlatFileConnector implements SchemaOp, CreateOp, DeleteOp, UpdateOp, SearchOp<Map<String, String>>, GetApiOp, PoolableConnector { private FlatFileConfiguration flatFileConfig; private FlatFileMetadata flatFileMetadata; private FlatFileParser flatFileParser; private FlatFileWriter flatFileWriter; private boolean alive = false; @Override public Configuration getConfiguration() { return this.flatFileConfig; } @Override public void init(Configuration config) { this.flatFileConfig = (FlatFileConfiguration) config; FlatFileIOFactory flatFileIOFactory = FlatFileIOFactory.getInstance(flatFileConfig); this.flatFileMetadata = flatFileIOFactory.getMetadataInstance(); this.flatFileParser = flatFileIOFactory.getFileParserInstance(); this.flatFileWriter = flatFileIOFactory.getFileWriterInstance(); this.alive = true; System.out.println("init called: Initialization done"); } @Override public void dispose() { this.alive = false; } @Override public Schema schema() { SchemaBuilder flatFileSchemaBldr = new SchemaBuilder(this.getClass()); Set<AttributeInfo> attrInfos = new HashSet<AttributeInfo>(); for (String fieldName : flatFileMetadata.getOrderedTextFieldNames()) { AttributeInfoBuilder attrBuilder = new AttributeInfoBuilder(); attrBuilder.setName(fieldName); attrBuilder.setCreateable(true); attrBuilder.setUpdateable(true); attrInfos.add(attrBuilder.build()); } // Supported class and attributes flatFileSchemaBldr.defineObjectClass (ObjectClass.ACCOUNT.getDisplayNameKey(),attrInfos); System.out.println("schema called: Built the schema properly"); return flatFileSchemaBldr.build(); } @Override public Uid create(ObjectClass arg0, Set<Attribute> attrs, OperationOptions ops) { System.out.println("Creating user account " + attrs); assertUserObjectClass(arg0); try { FlatFileUserAccount accountRecord = new FlatFileUserAccount(attrs); // Assert uid is there assertUidPresence(accountRecord); // Create the user this.flatFileWriter.addAccount(accountRecord); // Return uid String uniqueAttrField = this.flatFileConfig .getUniqueAttributeName(); String uniqueAttrVal = accountRecord .getAttributeValue(uniqueAttrField); System.out.println("User " + uniqueAttrVal + " created"); return new Uid(uniqueAttrVal); } catch (Exception ex) { // If account exists if (ex.getMessage().contains("exists")) throw new AlreadyExistsException(ex); // For all other causes System.out.println("Error in create " + ex.getMessage()); throw ConnectorException.wrap(ex); } } @Override public void delete(ObjectClass arg0, Uid arg1, OperationOptions arg2) { final String uidVal = arg1.getUidValue(); this.flatFileWriter.deleteAccount(uidVal); System.out.println("Account " + uidVal + " deleted"); } @Override public Uid update(ObjectClass arg0, Uid arg1, Set<Attribute> arg2, OperationOptions arg3) { String accountIdentifier = arg1.getUidValue(); // Fetch the account FlatFileUserAccount accountToBeUpdated = this.flatFileParser .getAccount(accountIdentifier); // Update accountToBeUpdated.updateAttributes(arg2); this.flatFileWriter .modifyAccount(accountIdentifier, accountToBeUpdated); System.out.println("Account " + accountIdentifier + " updated"); // Return new uid String newAccountIdentifier = accountToBeUpdated .getAttributeValue(this.flatFileConfig.getUniqueAttributeName()); return new Uid(newAccountIdentifier); } @Override public FilterTranslator<Map<String, String>> createFilterTranslator( ObjectClass arg0, OperationOptions arg1) { // TODO: Create a fine grained filter translator // Return a dummy object as its not applicable here. // All processing happens in the execute query return new AbstractFilterTranslator<Map<String, String>>() { }; } @Override public ConnectorObject getObject(ObjectClass arg0, Uid uid, OperationOptions arg2) { // Return matching record String accountIdentifier = uid.getUidValue(); FlatFileUserAccount userAcc = this.flatFileParser .getAccount(accountIdentifier); ConnectorObject userAccConnObject = convertToConnectorObject(userAcc); return userAccConnObject; } /* * (non-Javadoc) * This is the search implementation. * The Map passed as the query here, will map to all the records with * matching attributes. * * The record will be filtered if any of the matching attributes are not * found * * @see * org.identityconnectors.framework.spi.operations.SearchOp#executeQuery * (org.identityconnectors.framework.common.objects.ObjectClass, * java.lang.Object, * org.identityconnectors.framework.common.objects.ResultsHandler, * org.identityconnectors.framework.common.objects.OperationOptions) */ @Override public void executeQuery(ObjectClass objectClass, Map<String, String> matchSet, ResultsHandler resultHandler, OperationOptions ops) { System.out.println("Inside executeQuery"); // Iterate over the records and handle individually Iterator<FlatFileUserAccount> userAccountIterator = this.flatFileParser .getAccountIterator(matchSet); while (userAccountIterator.hasNext()) { FlatFileUserAccount userAcc = userAccountIterator.next(); ConnectorObject userAccObject = convertToConnectorObject(userAcc); if (!resultHandler.handle(userAccObject)) { System.out.println("Not able to handle " + userAcc); break; } } } private void assertUserObjectClass(ObjectClass arg0) { if (!arg0.equals(ObjectClass.ACCOUNT)) throw new UnsupportedOperationException( "Only user account operations supported."); } private void assertUidPresence(FlatFileUserAccount accountRecord) { String uniqueAttrField = this.flatFileConfig.getUniqueAttributeName(); String uniqueAttrVal = accountRecord.getAttributeValue(uniqueAttrField); if (uniqueAttrVal == null) { throw new IllegalArgumentException("Unique attribute not passed"); } } private ConnectorObject convertToConnectorObject(FlatFileUserAccount userAcc) { ConnectorObjectBuilder userObjBuilder = new ConnectorObjectBuilder(); // Add attributes List<String> attributeNames = this.flatFileMetadata .getOrderedTextFieldNames(); for (String attributeName : attributeNames) { String attributeVal = userAcc.getAttributeValue(attributeName); userObjBuilder.addAttribute(attributeName, attributeVal); if (attributeName.equals(this.flatFileConfig .getUniqueAttributeName())) { userObjBuilder.setUid(attributeVal); userObjBuilder.setName(attributeVal); } } return userObjBuilder.build(); } @Override public void checkAlive() { if (!alive) throw new RuntimeException("Connection not alive"); } }
5.2.4 AbstractFilterTranslatorの実装
org.identityconnectors.framework.common.objects.filter.AbstractFilterTranslator<T>クラスは、フィルタ操作を定義するために実装されています。
次に、フィルタ操作を定義するorg.identityconnectors.framework.common.objects.filter.AbstractFilterTranslator<T>のサンプル実装を示します。
package org.identityconnectors.flatfile.filteroperations; import java.util.HashMap; import java.util.Map; import org.identityconnectors.framework.common.objects.Attribute; import org.identityconnectors.framework.common.objects.filter.AbstractFilterTranslator; import org.identityconnectors.framework.common.objects.filter.ContainsAllValuesFilter; public class ContainsAllValuesImpl extends AbstractFilterTranslator<Map<String, String>>{ @Override protected Map<String, String> createContainsAllValuesExpression( ContainsAllValuesFilter filter, boolean not) { Map<String, String> containsAllMap = new HashMap<String, String>(); Attribute attr = filter.getAttribute(); containsAllMap.put(attr.getName(), attr.getValue().get(0).toString()); return containsAllMap; } }
5.2.5 MANIFEST.MFファイル
MANIFEST.MFファイルは、コネクタ・バンドルJARファイルを作成するために使用します。
MANIFEST.MFファイルの内容は次のとおりです。
Manifest-Version: 1.0 Ant-Version: Apache Ant 1.7.0 Created-By: 14.1-b02 (Sun Microsystems Inc.) ConnectorBundle-FrameworkVersion: 1.0 ConnectorBundle-Name: org.identityconnectors.flatfile ConnectorBundle-Version: 1.0 Build-Number: 609 Subversion-Revision: 4582
5.3 ファイル入力および出力処理のサポート・クラス
ファイル入力および出力処理のサポート・クラスはFlatFileIOFactory、FlatFileMetaData、FlatFileParser、FlatFileWriter、FlatfileLineIterator、FlatfileUserAccount、FlatfileAccountConversionHandlerおよびMessages.Propertiesです。
この項では、ファイル入力および出力処理用の次のサポート・クラスの実装について説明します。
5.3.1 FlatFileIOFactoryサポート・クラスの実装
次のコード・サンプルは、FlatFileIOFactoryサポート・クラスの実装を示しています。
package org.identityconnectors.flatfile.io; import org.identityconnectors.flatfile.FlatFileConfiguration; public class FlatFileIOFactory { private FlatFileMetadata flatFileMetadata; private FlatFileConfiguration flatFileConfig; /** * Provides instance of the factory * @param flatfileConfig Configuration bean for the flat file */ public static FlatFileIOFactory getInstance(FlatFileConfiguration fileConfig) { return new FlatFileIOFactory(fileConfig); } /** * Making it private to avoid public instantiation. Encouraging use of getInstance * @param fileConfig */ private FlatFileIOFactory(FlatFileConfiguration fileConfig) { this.flatFileConfig = fileConfig; this.flatFileMetadata = new FlatFileMetadata(flatFileConfig); System.out.println("Metadata set"); } /** * Returns the metadata instance * @return */ public FlatFileMetadata getMetadataInstance() { return this.flatFileMetadata; } /** * Returns the FlatFileParser instance * @return */ public FlatFileParser getFileParserInstance() { return new FlatFileParser(this.flatFileMetadata, this.flatFileConfig); } /** * Returns the FlatFileWriter instance * @return */ public FlatFileWriter getFileWriterInstance() { return new FlatFileWriter(this.flatFileMetadata, this.flatFileConfig); } }
5.3.2 FlatFileMetaDataサポート・クラスの実装
次のコード・サンプルは、FlatFileMetaDataサポート・クラスの実装を示しています。
package org.identityconnectors.flatfile.io; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.StringTokenizer; import org.identityconnectors.flatfile.FlatFileConfiguration; /** * This class contains all the metadata related information Example: Ordering of * columns, Number of columns etc. * * @author harsh * */ public class FlatFileMetadata { private FlatFileConfiguration fileConfig; private List<String> orderedTextFieldNames; private String changeLogFieldName; private String uniqueAttributeFiledName; /** * Instantiates the class with the file configuration. * Making it package private to encourage instantiation from Factory class * @param fileConfig */ FlatFileMetadata(FlatFileConfiguration fileConfig) { /* * Ideally you should not take connector specific configuration class in * flat file resource classes. Change if this has to go to production. * Probably make another configuration class for flat file with same * signatures. */ this.fileConfig = fileConfig; initializeMetadata(); validateConfigProps(); } /** * Returns the text field names in the order of their storage * * @return */ public List<String> getOrderedTextFieldNames() { return this.orderedTextFieldNames; } /** * Returns the number of columns */ public int getNumberOfFields() { int numberOfTextFields = this.orderedTextFieldNames.size(); return numberOfTextFields; } /** * Specifies if number of tokens are matching with the standard length of metadata * @param countTokens * @return */ public boolean isDifferentFromNumberOfFields(int countTokens) { return (getNumberOfFields() != countTokens); } /** * Reads the header line and sets the metadata */ private void initializeMetadata() { // Read the file. File recordsStore = this.fileConfig.getStoreFile(); try { BufferedReader storeFileReader = new BufferedReader(new FileReader( recordsStore.getAbsolutePath())); // Read the header line String headerString = storeFileReader.readLine(); // Tokenize the headerString StringTokenizer tokenizer = new StringTokenizer(headerString, fileConfig.getTextFieldDelimeter()); this.orderedTextFieldNames = new ArrayList<String>(); while (tokenizer.hasMoreTokens()) { String header = tokenizer.nextToken(); this.orderedTextFieldNames.add(header); } System.out.println("Columns read - " + this.orderedTextFieldNames); } catch (IOException e) { throw new RuntimeException("How can I read a corrupted file"); } // Store the change log and unique attribute field names this.changeLogFieldName = fileConfig.getChangeLogAttributeName(); this.uniqueAttributeFiledName = fileConfig.getUniqueAttributeName(); } /** * Validate if the attribute names in config props object are present in the * column names * * @throws RuntimeException * if validation fails */ private void validateConfigProps() { // Check if unique attribute col name is present if (!this.orderedTextFieldNames.contains(this.changeLogFieldName)) throw new RuntimeException("Change log field name " + this.changeLogFieldName + " not found in the store file "); // Check if change col name is present if (!this.orderedTextFieldNames.contains(this.uniqueAttributeFiledName)) throw new RuntimeException("Unique attribute field name " + this.uniqueAttributeFiledName + " not found in the store file"); } }
5.3.3 FlatFileParserサポート・クラスの実装
次のコード・サンプルは、FlatFileParserサポート・クラスの実装を示しています。
package org.identityconnectors.flatfile.io; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.identityconnectors.flatfile.FlatFileConfiguration; import org.identityconnectors.flatfile.FlatFileUserAccount; import org.identityconnectors.flatfile.utils.AccountConversionHandler; public class FlatFileParser { private File recordsStore; private FlatFileConfiguration fileConfig; private FlatFileMetadata metadata; private AccountConversionHandler accountConverter; /** * Instantiates the parser class. Making it package private to encourage * instantiation from Factory class * * @param metadata * @param fileConfig */ FlatFileParser(FlatFileMetadata metadata, FlatFileConfiguration fileConfig) { this.fileConfig = fileConfig; this.recordsStore = fileConfig.getStoreFile(); this.accountConverter = new AccountConversionHandler(metadata, fileConfig); this.metadata = metadata; } /** * Returns all accounts in the file * * @return */ public List<FlatFileUserAccount> getAllAccounts() { try { BufferedReader userRecordReader = new BufferedReader( new FileReader(recordsStore.getAbsolutePath())); String recordStr; // Skip headers userRecordReader.readLine(); // Loop over records and make list of objects List<FlatFileUserAccount> allAccountRecords = new ArrayList<FlatFileUserAccount>(); while ((recordStr = userRecordReader.readLine()) != null) { try { FlatFileUserAccount accountRecord = accountConverter .convertStringRecordToAccountObj(recordStr); allAccountRecords.add(accountRecord); } catch (RuntimeException e) { System.out.println("Invalid entry " + e.getMessage()); } } userRecordReader.close(); return allAccountRecords; } catch (IOException e) { throw new RuntimeException("How can I read a corrupted file"); } } /** * Gets the account of matching account identifier * * @param accountIdentifier * @return */ public FlatFileUserAccount getAccount(String accountIdentifier) { /* * I know its not right to get all account details. Don't want to focus * on efficiency and scalability as this is just a sample. */ // Iterate over all records and check for matching account Map<String, String> matchSet = new HashMap<String, String>(); matchSet.put(fileConfig.getUniqueAttributeName(), accountIdentifier); for (FlatFileUserAccount userRecord : getAllAccounts()) { if (userRecord.hasMatchingAttributes(matchSet)) return userRecord; } // Got nothing.. return null; } /** * Returns all records with matching Attributes If more than attributes are * passed. it will check all the attributes * * @param matchSet * Checks if all provided attributes are matched */ public List<FlatFileUserAccount> getAccountsByMatchedAttrs( Map<String, String> matchSet) { /* * I know its not right to get all account details. Don't want to focus * on efficiency and scalability as this is just a sample. */ // Iterate over all records and check for matching account List<FlatFileUserAccount> matchingRecords = new ArrayList<FlatFileUserAccount>(); for (FlatFileUserAccount userRecord : getAllAccounts()) { if (userRecord.hasMatchingAttributes(matchSet)) matchingRecords.add(userRecord); } return matchingRecords; } /** * Returns the records that fall after the specified change number This * function helps in checking the function of sync * * @param changeNumber * the change number for the last search */ public List<FlatFileUserAccount> getUpdatedAccounts(int changeNumber) { /* * I know its not right to get all account details. Don't want to focus * on efficiency and scalability as this is just a sample. */ // Iterate over all records and check for matching account List<FlatFileUserAccount> matchingRecords = new ArrayList<FlatFileUserAccount>(); String changeLogAttrName = fileConfig.getChangeLogAttributeName(); for (FlatFileUserAccount userRecord : getAllAccounts()) { int recordChangeNumber = userRecord .getChangeNumber(changeLogAttrName); if (recordChangeNumber >= changeNumber) matchingRecords.add(userRecord); } return matchingRecords; } /** * Returns an iterator that iterates over the records. This is provided for * dynamic retrieval of records * * @param matchSet * Filters the records by matching the given attributes. Use null * or empty set to avoid filtering * @return */ public Iterator<FlatFileUserAccount> getAccountIterator( Map<String, String> matchSet) { Iterator<FlatFileUserAccount> recordIterator = new FlatFileLineIterator( this.metadata, this.fileConfig, matchSet); return recordIterator; } /** * Gives the next change number. Logic is max of existing change numbers + 1 * @return */ public int getNextChangeNumber() { int maximumChangeNumber = 0; /* * I know its not right to get all account details. Don't want to focus * on efficiency and scalability as this is just a sample. */ // Iterate over all records and check for matching account String changeLogAttrName = fileConfig.getChangeLogAttributeName(); for (FlatFileUserAccount userRecord : getAllAccounts()) { int changeNumber = userRecord.getChangeNumber(changeLogAttrName); if (changeNumber >= maximumChangeNumber) { maximumChangeNumber = changeNumber + 1; } } return maximumChangeNumber; } }
5.3.4 FlatFileWriterサポート・クラスの実装
次のコード・サンプルは、FlatFileWriterサポート・クラスの実装を示しています。
package org.identityconnectors.flatfile.io; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import org.identityconnectors.flatfile.FlatFileConfiguration; import org.identityconnectors.flatfile.FlatFileUserAccount; import org.identityconnectors.flatfile.utils.AccountConversionHandler; /** * Class for searching operations on files * * @author Harsh */ public class FlatFileWriter { private File recordsStore; private FlatFileParser recordParser; private FlatFileConfiguration fileConfig; private AccountConversionHandler accountConverter; /** * Initializes the writer with the configuration Making it package private * to encourage use of Factory class for global instantiation * * @param metadata * @param fileConfig */ FlatFileWriter(FlatFileMetadata metadata, FlatFileConfiguration fileConfig) { this.fileConfig = fileConfig; this.recordsStore = fileConfig.getStoreFile(); recordParser = new FlatFileParser(metadata, fileConfig); accountConverter = new AccountConversionHandler(metadata, fileConfig); } /** * Appends the user record at the end of * * @param accountRecord */ public void addAccount(FlatFileUserAccount accountRecord) { try { BufferedWriter userRecordWriter = new BufferedWriter( new FileWriter(this.recordsStore.getAbsolutePath(), true)); // Set the latest changelog number int latestChangeNumber = recordParser.getNextChangeNumber(); accountRecord.setChangeNumber(fileConfig .getChangeLogAttributeName(), latestChangeNumber); // Validate if same account id doesn't exist String accountUid = accountRecord.getAttributeValue(fileConfig .getUniqueAttributeName()); FlatFileUserAccount accountByAccountId = recordParser .getAccount(accountUid); if (accountByAccountId != null) throw new RuntimeException("Account " + accountUid + " already exists"); // Put the user record in formatted way String userRecordAsStr = accountConverter .convertAccountObjToStringRecord(accountRecord); userRecordWriter.write("\n" + userRecordAsStr); // Close the output stream userRecordWriter.close(); } catch (IOException e) {// Catch exception if any throw new RuntimeException("How can I write on a corrupted file"); } } /** * Removes the entry for respective account identifier * * @param accountIdentifier */ public void deleteAccount(String accountIdentifier) { String blankRecord = ""; this.modifyAccountInStore(accountIdentifier, blankRecord); } /** * Updates the entry with respective account identifier * * @param accountIdentifier * @param updatedAccountRecord * @return new accountIdentifier */ public String modifyAccount(String accountIdentifier, FlatFileUserAccount updatedAccountRecord) { // Frame a record string and update back to file int nextChangeNumber = recordParser.getNextChangeNumber(); String changeNumberFieldName = fileConfig.getChangeLogAttributeName(); updatedAccountRecord.setChangeNumber(changeNumberFieldName, nextChangeNumber); String newRecordAsStr = accountConverter .convertAccountObjToStringRecord(updatedAccountRecord); // Update to the file this.modifyAccountInStore(accountIdentifier, newRecordAsStr); // Return new UID String uniqueAttrFieldName = fileConfig.getUniqueAttributeName(); String newAccountIdentifier = updatedAccountRecord .getAttributeValue(uniqueAttrFieldName); return newAccountIdentifier; } /** * Returns the complete flat file as string. * * @return */ private String getCompleteFlatFileAsStr() { try { BufferedReader userRecordReader = new BufferedReader( new FileReader(recordsStore.getAbsolutePath())); String recordStr; // Loop over records and make list of objects StringBuilder flatFileStr = new StringBuilder(); while ((recordStr = userRecordReader.readLine()) != null) { if (!recordStr.isEmpty()) flatFileStr.append(recordStr + "\n"); } userRecordReader.close(); return flatFileStr.toString(); } catch (IOException e) { throw new RuntimeException("How can I read a corrupted file"); } } /** * Updates the account with the new record. this can also be used for delete * * @param accountIdentifier * @param updatedRecord */ private void modifyAccountInStore(String accountIdentifier, String updatedRecord) { try { // Load the complete flat file String completeFlatFile = this.getCompleteFlatFileAsStr(); // Construct the string to be removed and replace it with blank FlatFileUserAccount accountToBeRemoved = recordParser .getAccount(accountIdentifier); String updatableString = accountConverter .convertAccountObjToStringRecord(accountToBeRemoved); String updatedFlatFile = completeFlatFile.replaceAll( updatableString, updatedRecord); // Rewrite the file BufferedWriter userRecordWriter = new BufferedWriter( new FileWriter(this.recordsStore.getAbsolutePath(), false)); userRecordWriter.write(updatedFlatFile); /*** debug ***/ System.out.println("Old string " + updatableString); System.out.println("New String" + updatedRecord); System.out.println("new file - " + updatedFlatFile); /******/ // Close the output stream userRecordWriter.close(); } catch (IOException e) {// Catch exception if any throw new RuntimeException("How can I write on a corrupted file"); } } }
5.3.5 FlatfileLineIteratorサポート・クラスの実装
次のコード・サンプルは、FlatfileLineIteratorサポート・クラスの実装を示しています。
package org.identityconnectors.flatfile.io; . import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.util.Iterator; import java.util.Map; . import org.identityconnectors.flatfile.FlatFileConfiguration; import org.identityconnectors.flatfile.FlatFileUserAccount; import org.identityconnectors.flatfile.utils.AccountConversionHandler; . /** * Iterator class to fetch the records dynamically during search operations This * is needed to prevent VM overloading when all records are stored in memory * * @author admin * */ public class FlatFileLineIterator implements Iterator<FlatFileUserAccount> { . private File recordsStore; private AccountConversionHandler accountConverter; private FlatFileUserAccount nextRecord; private BufferedReader userRecordReader; private Map<String, String> attrConstraints; . /** * Making it package private to prevent global initialization * * @param metadata * @param fileConfig * @param attributeValConstraints * Iterator will apply this constraint and filter the result */ FlatFileLineIterator(FlatFileMetadata metadata, FlatFileConfiguration fileConfig, Map<String, String> attributeValConstraints) { this.recordsStore = fileConfig.getStoreFile(); this.accountConverter = new AccountConversionHandler(metadata, fileConfig); this.attrConstraints = attributeValConstraints; . initializeReader(); this.nextRecord = readNextValidRecord(); } . private void initializeReader() { try { userRecordReader = new BufferedReader(new FileReader(recordsStore .getAbsolutePath())); . // Skip headers userRecordReader.readLine(); . } catch (IOException io) { throw new IllegalStateException("Unable to read " + recordsStore.getName()); } } . @Override public boolean hasNext() { return (nextRecord != null); } . @Override public FlatFileUserAccount next() { FlatFileUserAccount currentRecord = this.nextRecord; this.nextRecord = readNextValidRecord(); return currentRecord; } . @Override public void remove() { // Nothing to do here } . /** * Returns next valid record. This happens after applying * * @return */ private FlatFileUserAccount readNextValidRecord() { try { FlatFileUserAccount userAccObj = null; String recordStr; // Match the constraints or read next line do { System.out.println("Before record string"); recordStr = getNextLine(); . // No more records ?? if (recordStr == null) return null; . userAccObj = accountConverter .convertStringRecordToAccountObj(recordStr); } while (!userAccObj.hasMatchingAttributes(attrConstraints)); return userAccObj; } catch (Exception e) { System.out.println("Error reading record" + e.getMessage()); e.printStackTrace(); return null; } } . private String getNextLine() throws IOException { String nextLine = userRecordReader.readLine(); . // No records ?? if (nextLine == null) { this.userRecordReader.close(); return null; } . if (nextLine.trim().isEmpty()) { return getNextLine(); } . return nextLine; } }
5.3.6 FlatfileUserAccountサポート・クラスの実装
次のコード・サンプルは、FlatfileUserAccountサポート・クラスの実装を示しています。
package org.identityconnectors.flatfile; . import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; . import org.identityconnectors.framework.common.objects.Attribute; . /** * Object representing a user entity * * @author admin * */ public class FlatFileUserAccount { . /* * Mandatory attribute names */ private Set<String> mandatoryAttrNames = new HashSet<String>(); . /* * Attributes making the account */ private Map<String, String> attributes = new HashMap<String, String>(); . /** * Instantiates the attribute value map * * @param mandatoryAttributeNames * Names of the attributes that are necessary * @param attributeValMap * Name value map for the attributes. * @throws IllegalStateException * If mandatory attributes are not found in attribute val map */ public FlatFileUserAccount(Set<String> mandatoryAttributeNames, Map<String, String> attributeValMap) { // Check if mandatory attribute values are passed Set<String> attrValuesKeySet = attributeValMap.keySet(); if (!attrValuesKeySet.containsAll(mandatoryAttributeNames)) throw new IllegalStateException("Mandatory attributes missing"); . // Initialize this.mandatoryAttrNames = mandatoryAttributeNames; this.attributes = attributeValMap; . } /** * Instantiates the attribute value map. * Considers all attributes to be mandatory * @param attributeValMap */ public FlatFileUserAccount(Map<String, String> attributeValMap) { this.mandatoryAttrNames = attributeValMap.keySet(); this.attributes = attributeValMap; } /** * Instantiates the attribute value map * @param attrs */ public FlatFileUserAccount(Set<Attribute> attrs) { for(Attribute attr: attrs) { String attrName = attr.getName(); //Consider first value. Multivalued not supported String attrVal = (String) attr.getValue().get(0); this.attributes.put(attrName, attrVal); } } . /** * Updates the set of attributes. If new attributes present, they are added, * If old attributes are present in the parameter set, values are updated * * @param updatedAttributeValMap */ public void updateAttributes(Map<String, String> updatedAttributeValMap) { this.attributes.putAll(updatedAttributeValMap); } /** * Updates the set of attributes. * @param upatedAttributes */ public void updateAttributes(Set<Attribute> upatedAttributes) { Map<String, String> updatedAttributeValMap = new HashMap<String, String>(); for(Attribute attr: upatedAttributes) { String attrName = attr.getName(); //Consider first value. Multivalued not supported String attrVal = (String) attr.getValue().get(0); updatedAttributeValMap.put(attrName, attrVal); } this.attributes.putAll(updatedAttributeValMap); } . /** * Deletes the attributes with given name. * * @param attributeKeys * Set of the attribute names that are needed * @throws UnsupportedOperationException * if delete for mandatory attributes is attempted */ public void deleteAttributes(Set<String> attributeKeys) { // Check if mandatory attributes are not there. for (String attrKey : attributeKeys) { if (this.mandatoryAttrNames.contains(attrKey)) throw new UnsupportedOperationException( "Delete for mandatory attributes not supported. Try update"); // Not deleting here as it might result inconsistent } // Remove the attributes for (String attrKey : attributeKeys) { this.attributes.remove(attrKey); } } . /** * Gets the attribute of a given name * * @param attributeName * @return * @throws IllegalArgumentException * if attribute is not there for a given name */ public String getAttributeValue(String attributeName) { return this.attributes.get(attributeName); } . /** * Returns the current set of attributes * * @return */ public Map<String, String> getAllAttributes() { return this.attributes; } . /** * Returns true if all passed attributes are matching for this object * * @param attrValMap * @return */ public boolean hasMatchingAttributes(Map<String, String> attrValMap) { boolean noFilterSupplied = (attrValMap == null )|| (attrValMap.isEmpty()); if (noFilterSupplied) // No filter. Everything matches return true; // Iterate to match attributes one by one Set<String> keySet = attrValMap.keySet(); for (String attrName : keySet) { String objAttrVal = this.attributes.get(attrName); String passedValue = attrValMap.get(attrName); . if (!objAttrVal.equals(passedValue)) // This attribute is not same return false; } . // All attributes are same return true; } . /** * Returns the change log number * * @param changeLogAttrName * attribute representing the number * @return */ public int getChangeNumber(String changeLogAttrName) { String changeNumStr = this.attributes.get(changeLogAttrName); int changeNumber = 0; . try { changeNumber = Integer.parseInt(changeNumStr); } catch (Exception e) { System.out.println("Not a valid change log number " + changeLogAttrName + " :" + changeNumStr); } . return changeNumber; } /** * Sets the given attribute with a new value * @param attrName * @param attrVal */ public void setAttribute(String attrName, String attrVal) { this.attributes.put(attrName, attrVal); } /** * Updates the changelog number * @param changeLogAttrName * @param newChangeNumber */ public void setChangeNumber(String changeLogAttrName, int newChangeNumber) { String changeNumberValStr = "" + newChangeNumber; this.attributes.put(changeLogAttrName, changeNumberValStr); } . @Override public String toString() { // Just print the attributes return this.attributes.toString(); } . }
5.3.7 FlatfileAccountConversionHandlerサポート・クラスの実装
次のコード・サンプルは、FlatfileAccountConversionHandlerサポート・クラスの実装を示しています。
package org.identityconnectors.flatfile.utils; . import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.StringTokenizer; . import org.identityconnectors.flatfile.FlatFileConfiguration; import org.identityconnectors.flatfile.FlatFileUserAccount; import org.identityconnectors.flatfile.io.FlatFileMetadata; . /** * Class for the utility functions * * @author Admin * */ public class AccountConversionHandler { . private FlatFileConfiguration fileConfig; private FlatFileMetadata metadata; . /** * Instantiates the handler class. But needs the configuration * * @param metadata * @param fileConfig */ public AccountConversionHandler(FlatFileMetadata metadata, FlatFileConfiguration fileConfig) { this.fileConfig = fileConfig; this.metadata = metadata; } . /** * Converts strings records to the user account objects. * * @param accountRecord * @return * @throws RuntimeException * If string is not formatted as per accepted standards */ public FlatFileUserAccount convertStringRecordToAccountObj( String accountRecord) { . StringTokenizer tokenizer = new StringTokenizer(accountRecord, fileConfig.getTextFieldDelimeter()); . // Assert number of columns matching with number of tokens if (metadata.isDifferentFromNumberOfFields(tokenizer.countTokens())) throw new RuntimeException( "Number of tokens doesn't match number of columns"); . // Get the attributes List<String> attrNames = metadata.getOrderedTextFieldNames(); Map<String, String> attrValMap = new HashMap<String, String>(); . // Number of tokens are same. Same loop will work for (String attrName : attrNames) { String attrVal = ""; if (tokenizer.hasMoreTokens()) attrVal = tokenizer.nextToken(); . attrValMap.put(attrName, attrVal); } . // Assumption : All attributes are mandatory for user. Change with the // change in assumption Set<String> mandatoryAttributeNames = attrValMap.keySet(); FlatFileUserAccount userAccountRecordObj = new FlatFileUserAccount( mandatoryAttributeNames, attrValMap); return userAccountRecordObj; . } . /** * Converts account objects to storable string records * * @param accountObj * @return */ public String convertAccountObjToStringRecord( FlatFileUserAccount accountObj) { StringBuilder strRecord = new StringBuilder(); . // Build the string record from the object List<String> attrNames = metadata.getOrderedTextFieldNames(); int index=0; for (String attrName: attrNames) { String attrVal = accountObj.getAttributeValue(attrName); strRecord.append(attrVal); // Add delimeter if (index < attrNames.size()-1) { strRecord.append(fileConfig.getTextFieldDelimeter()); index++; } else { // Record ended String newLineCharacter = "\n"; strRecord.append(newLineCharacter); break; } } return strRecord.toString(); } . /** * Asserts if given object is not null * * @param message * @param obj */ public void assertNotNull(String message, Object obj) { if (obj == null) throw new RuntimeException(message); } }
5.3.8 Messages.Propertiesサポート・クラスの実装
次のコード・サンプルは、Messages.Propertiesサポート・クラスの実装を示しています。
USER_ACCOUNT_STORE_HELP=File in which user account will be stored USER_ACCOUNT_STORE_DISPLAY=User Account File USER_STORE_TEXT_DELIM_HELP=Text delimeter used for separating the columns USER_STORE_TEXT_DELIM_DISPLAY=Text Field Delimeter UNIQUE_ATTR_HELP=The name of the attribute which will act as unique identifier UNIQUE_ATTR_DISPLAY=Unique Field CHANGELOG_ATTR_HELP=The name of the attribute which will act as changelog CHANGELOG_ATTR_DISPLAY=Changelog Field
5.4 Oracle Identity Governanceデータベースへのアイデンティティ・コネクタ・バンドルのアップロード
アイデンティティ・コネクタ・バンドルは、Oracle Identity GovernanceデータベースのICFで使用できる必要があります。
ICFアイデンティティ・コネクタをOracle Identity Managerと統合するには、次に示す項に従います。これらの手順の一部には、Oracle Identity Manager Design Consoleを使用して行う構成が含まれています。
5.4.1 Oracle Identity Governanceへのコネクタ・バンドルの登録
コネクタ・バンドルは、Oracle Identity Managerに対してローカルなコネクタ・サービスで使用できる必要があります。
このことを実現する手順は、次のとおりです。
5.4.2 基本的なアイデンティティ・コネクタ・メタデータの作成
コネクタ・メタデータ構成は、プロビジョニングとリコンシリエーションの両方で必要となります。
この項における次の一連の手順は、Oracle Identity Manager Design Consoleを使用して完了します。
5.4.2.1 ITリソース・タイプ定義の作成
ITリソース・タイプ定義は、リソースの接続情報の表現です。ITリソース・タイプ定義内の構成パラメータは、コネクタ・バンドルの構成パラメータと一致する必要があります。ITリソース内のパラメータの値は、バンドル構成で設定されます。
ノート:
バンドル構成によって使用されないパラメータを含めることができます。これらは、バンドル操作に悪影響は与えません。
5.4.2.2 リソース・オブジェクトの作成
リソース・オブジェクトは、Oracle Identity Managerでのリソースの表現です。コネクタ・バンドルは、リソース・オブジェクトに関連付けられています。
5.4.2.3 参照の作成
コネクタ・バンドルでサポートされている様々なオブジェクトに対して個別の参照を定義する必要があります。この参照には、これらのオブジェクトのプロビジョニングおよびリコンシリエーションに関連する情報を含めることができます。メインの構成参照は、これらの参照へのポインタが含まれているため、オブジェクト固有の参照のルートになります。次の項では、参照の作成方法について説明します。
5.4.2.3.1 メインの構成参照の作成
(ITリソース・タイプ定義の作成で定義した)構成参照には、接続情報とはみなされないコネクタ・バンドル構成が保持されます。ITリソース・タイプ定義で構成パラメータが見つからない場合、Oracle Identity Managerは構成参照を検索します。メインの構成参照には、バンドル・プロパティおよびバンドル構成が含まれています。バンドル・プロパティ・パラメータは、正しいバンドルの識別に必要であるため、必須です。ITリソース・タイプ定義の一部として定義されていないバンドル構成(ITリソース・タイプの定義の作成を参照)は、ここで宣言できます。
ノート:
コード・キーの値は、図と完全に一致する必要があります。デコードの値は、コネクタ・バンドルに固有です。
メインの構成参照を作成するには:
5.4.2.3.2 オブジェクト・タイプの構成参照の作成
オブジェクト・タイプの構成参照には、特定のオブジェクト・タイプに固有のパラメータが含まれています。オブジェクト・タイプは、アイデンティティ・コネクタが動作するエンティティです。ICF ObjectClassにマップされます。メインの構成参照の作成でUser Configuration Lookupが参照されているため、Userがオブジェクト・タイプとなり、この場合はObjectClass.ACCOUNTにマップされています。(RolesとUserJobDataが、その他の2つのオブジェクト・タイプです。)オブジェクト・タイプ名は、アイデンティティ・コネクタ・バンドルでサポートされているオブジェクト・クラス名と一致する必要があります。Userオブジェクト・タイプは事前定義済のObjectClass.ACCOUNTにマップされ、Groupオブジェクト・タイプは事前定義済ObjectClass.GROUPにマップされます。アイデンティティ・コネクタが複数のオブジェクトをサポートしている場合、このステップをそれぞれに対して繰り返す必要があります。
ノート:
これらのユースケースは基本機能のみを対象としているため、この構成は必須属性として保持されます。
オブジェクト・タイプの構成参照を作成するには:
5.4.3 プロビジョニング・メタデータの作成
フラット・ファイル・プロビジョニング用にOracle Identity Managerを構成するには、プロビジョニング・メタデータを作成します。そのためには、プロセス・フォーム、アダプタ、プロセス定義およびプロビジョニング属性マッピング参照を作成します。
次の項は、フラット・ファイル・プロビジョニング用にOracle Identity Managerを構成するために実行する必要があります。
5.4.3.1 プロセス・フォームの作成
プロセス・フォームは、Oracle Identity Managerでのオブジェクト属性の表現として使用されます。
この項では、プロセス・フォームとプロセス・フォームを作成する方法について説明します。次の項目が含まれます。
5.4.3.1.2 プロセス・フォームについて
プロセス・フォームは、Oracle Identity Managerでのオブジェクト属性の表現として使用されます。これにより、操作のためにオブジェクト属性がコネクタ・バンドルに渡される前に、ユーザーが設定を容易に入力できます。
プロセス・フォームで定義された属性は、規則ではありません。このフォームは、アイデンティティ・コネクタに渡される必要がある属性に対するチャレンジの方法です。一般的に、アイデンティティ・コネクタのサポートされている各属性に対して属性を定義します。
ノート:
アイデンティティ・コネクタ属性で1対1のマッピングを設定することをお薦めします。
対応するITリソース・タイプ定義に関連付ける必要があるITリソースを問い合せるためのフィールドが存在する必要があります。各フィールドの変数タイプは、オブジェクト属性のタイプにマップする必要があります。
5.4.3.1.3 コネクタ・スキーマの属性
表5-1に、コネクタ・スキーマに定義されている属性をリストします。
表5-1 「フォーム・デザイナ」のフィールド
名前 | バリアント | フィールド・ラベル | フィールド・タイプ |
---|---|---|---|
UD_FLAT_FIL_FIRSTNAME |
文字列 |
名 |
TextField |
UD_FLAT_FIL_UID |
文字列 |
ユニバーサルID |
TextField |
UD_FLAT_FIL_CHANGENO |
文字列 |
変更番号 |
TextField |
UD_FLAT_FIL_MAILID |
文字列 |
電子メールID |
TextField |
UD_FLAT_FIL_SERVER |
long |
サーバー |
ITResource |
UD_FLAT_FIL_LASTNAME |
文字列 |
姓 |
TextField |
UD_FLAT_FIL_ACCOUNTID |
文字列 |
アカウントID |
TextField |
UD_FLAT_FIL_RETURN |
文字列 |
Return ID |
TextField |
ノート:
フラット・ファイルの列名は、FirstName、ChangeNo、EmailID、Server、LastNameおよびAccountIDです。
5.4.3.2 アダプタの作成
コネクタ・バンドルでサポートされているすべての操作(作成、更新、削除など)に対してアダプタを作成する必要があります。
アダプタを作成するには:
-
Oracle Identity Manager Design Consoleにログインします。
-
「開発ツール」の下の「アダプタ・ファクトリ」をクリックします。
-
新しいアダプタを作成し、「アダプタ名」としてFFCreateUserを追加します。
-
「アダプタ・タイプ」として「プロセス・タスク」を追加します。
-
アダプタを保存します。
-
図5-7に示すように、変数リスト・タブをクリックして、次の変数を追加します。
-
「タイプ」が「文字列」で、マップが実行時に解決であるobjectType。
-
「タイプ」が「ロング」で、マップが実行時に解決であるprocessInstanceKey。
-
「タイプ」が「文字列」で、マップが実行時に解決であるitResourceFieldName。
-
-
図5-8に示すように、次のサブ手順を実行して、Java機能タスクをアダプタに追加します。
-
アダプタ・タスク・タブをクリックします。
-
アダプタを選択し、「追加」をクリックします。
-
タスク・オプションからJavaを選択します。
-
APIソースから「icf-oim-intg.jar」を選択します。
-
APIソースとして「oracle.iam.connetors.icfcommon.prov.ICProvisioninManager」を選択します。
-
タスクのメソッドとして「createObject」を選択します。
-
構成を保存します。
-
(すでに変数リストに追加した)変数を、適切なメソッド入力および出力にマップします。
-
構成パラメータを、適切なメソッド入力および出力にマップします。
データベース参照はデータベース参照(アダプタ参照)にマップし、戻り変数は戻り変数(アダプタ変数)にマップします。
-
-
アダプタを保存してビルドします。
5.4.3.3 プロセス定義の作成
プロセス定義では、特定の操作用のコネクタ・バンドルの動作を定義します。すべての操作には、関連付けられている対応タスクがあります。
次の手順では、作成操作のプロセス定義、およびプロセス・タスクの統合を構成します。
5.4.3.4 プロビジョニング属性マッピング参照の作成
プロビジョニング属性マッピング参照には、アイデンティティ・コネクタ・バンドル属性へのOracle Identity Managerフィールドのマッピングが含まれます。
この項では、プロビジョニング属性マッピング参照に関する次のトピックについて説明します。
5.4.3.4.1 プロビジョニング属性マッピング参照について
プロビジョニング属性マッピング参照には、アイデンティティ・コネクタ・バンドル属性へのOracle Identity Managerフィールドのマッピングが含まれます。プロビジョニング属性マッピング参照の説明を次に示します。
-
コード・キーは、プロセス・フォームのフィールド・ラベルです。
-
デコードは、アイデンティティ・コネクタ・バンドル属性です。
-
子フォーム属性は、入力での埋込みオブジェクトとして構成できます。
-
アイデンティティ・コネクタのプロビジョニング操作は、レスポンスでUIDを返します。これは、アイデンティティ・コネクタ・バンドル属性に対してコーディングして、フォーム・フィールドに設定できます。
5.4.3.4.3 プロビジョニング属性マップで使用されるフィールド・フラグ
プロビジョニング属性マッピングの場合、次のフィールド・フラグをコード・キーに追加できます。
-
LOOKUP: これは、参照リコンシリエーション・ジョブの実行によって値が取得されるすべてのフィールドに指定する必要があります。参照リコンシリエーション・ジョブから取得された値には、ITリソース名/キーが追加されています。このフラグを指定すると、追加された値をバンドルに渡す直前にICF統合で削除するのに役立ちます。たとえば、ラベルがDatabaseで、参照リコンシリエーション・ジョブの実行により値が取得されたフィールドのコード・キーは、Database[LOOKUP]のようになります。
ノート:
LOOKUPフラグは、プロビジョニングとリコンシリエーション両方の属性マップに指定できます。プロビジョニングの場合、ITリソース名/ITリソース・キー接頭辞を削除する必要があります。リコンシリエーションの場合、ITリソース名/ITリソース・キー接頭辞を追加する必要があります。
-
IGNORE: これは、値が無視され、バンドルに送信されないすべてのフィールドに指定する必要があります。たとえば、ラベルがDatabaseで、値をバンドルに送信する必要がないフィールドのコード・キーは、Database[IGNORE]のようになります。
-
WRITEBACK: これは、作成操作または更新操作の直後に値をプロセス・フォームに書き戻す必要があるすべてのフィールドに指定する必要があります。このフラグを追加すると、ICF統合レイヤー・コールICF Get APIが、WRITEBACKフラグのマークがある属性の値を取得するようになります。たとえば、ラベルがDatabaseで、作成/更新の直後に値がプロセス・フォームに書き戻される必要があるフィールドのコード・キーは、Database[WRITEBACK]のようになります。これが機能するには、コネクタはGetApiOpインタフェースを実装し、ConnectorObject getObject(ObjectClass objClass、Uid uid、OperationOptionsオプション)APIに実装を提供する必要があります。このAPIは、Uidが渡されたUidと同じであるアカウントのターゲットを検索し、プロセス・フォームに書き戻されるすべての属性(およびその値)が含まれたコネクタ・オブジェクトをビルドします。
ノート:
コネクタがGetApiOpインタフェースを実装していない場合、WRITEBACKフラグは機能せず、エラーが生成されます。
-
DATE: これは、タイプがDateと見なされる必要があるフィールドに指定する必要があり、これがない場合、値は標準の文字列と見なされます。たとえば、ラベルがTodayで、値が日付形式で表示される必要があるフィールドのコード・キーは、Today[DATE]のようになります。
-
PROVIDEONPSWDCHANGE: これは、パスワード更新が発生した場合に、バンドル(ターゲット)に提供する必要があるすべてのフィールドに指定する必要があります。一部のターゲットは、追加の属性がすべてのパスワード変更で指定されると予測します。PROVIDEONPSWDCHANGEフラグを指定することで、パスワード変更がリクエストされた場合は必ずすべての追加フィールドまたは属性を送信するようICF統合に伝えます。たとえば、ラベルがExtra Attribute Needed for Password Changeで、パスワード更新時に値をバンドル(ターゲット)に提供する必要があるフィールドのコード・キーは、Extra Attribute Needed for Password Change[PROVIDEONPSWDCHANGE]のようになります。
5.4.4 リコンシリエーション・メタデータの作成
フラット・ファイルからレコードのリコンシリエーションを構成できます。ターゲット・リコンシリエーションを例として使用できますが、信頼できるリコンシリエーションも同様の方法で構成できます。
リストされている順序で手順を実行してください。
5.4.4.1 リコンシリエーション・スケジュール済タスクの作成
デフォルトでは、リコンシリエーションはコネクタ・バンドルで検索操作を使用します。この操作は、Oracle Identity Managerを使用して構成されたスケジュール済タスクで呼び出されます。この手順は、次のサブ手順で構成されています。
5.4.4.2 リコンシリエーション・プロファイルの作成
リコンシリエーション・プロファイルでは、リコンシリエーション時のオブジェクト属性の構造を定義します。リコンシリエーション・プロファイルには、リコンシリエーションをサポートしているすべての属性が含まれている必要があります。
リコンシリエーション・プロファイルを作成するには:
5.4.4.3 リコンシリエーション・アクション・ルールの設定
リコンシリエーション・アクション・ルールでは、リコンシリエーションの動作を定義します。この手順では、一致が見つかった場合に行われるアクションを定義します。この手順では、Oracle Identity Manager Design Consoleにログインしていることを前提にしています。
5.4.4.4 リコンシリエーション・マッピングの作成
リコンシリエーション・マッピングは、プロセス定義で実行する必要があります。これは、サポートされているリコンシリエーション・フィールドを(リソース・オブジェクトから)プロセス・フォーム・フィールドにマッピングすることです。このマッピングは、ターゲット・リコンシリエーションを構成する場合にのみ必要となります。
リコンシリエーション・マッピングを作成するには:
5.4.4.5 リコンシリエーション属性マップで使用されるフィールド・フラグ
リコンシリエーション属性マッピングの場合は、次のフィールド・フラグをコード・キーに追加できます。
-
TRUSTED: これは、アカウントのステータスを表すフィールドのリコンシリエーション属性マップで指定する必要があります。このフラグは、信頼できるリコンシリエーションにのみ指定する必要があります。これが指定されている場合、アカウントのステータスは「アクティブ」または「無効」のいずれかになります。それ以外の場合、ステータスは「有効」または「無効」のいずれかになります。たとえば、ラベルがStatusで、値が「アクティブ」または「無効」のいずれかである必要があるフィールドのコード・キーは、Status[TRUSTED]のようになります。
-
DATE: これは、リコンシリエーション属性マップにおいて、タイプがDateと見なされる必要があるフィールドに指定する必要があります。たとえば、ラベルがTodayで、値が日付形式で表示される必要があるフィールドのコード・キーは、Today[DATE]のようにする必要があります。
5.5 フラット・ファイル・アカウントのプロビジョニング
フラット・ファイル・アカウントをプロビジョニングするには、ITリソース・パラメータおよびLookup.FF.Configurationパラメータを使用して、タイプがFlat FileであるITリソースを作成します。
フラット・ファイル・コネクタの準備は整っています。ユーザーは、Oracle Identity Managerにログインし、次の手順を使用してITリソース(ターゲット)を作成する必要があります。
-
タイプがFlat FileであるITリソースを作成します。
-
必要に応じて、ITリソース・パラメータを指定します。
-
必要に応じて、Lookup.FF.Configurationに構成パラメータを指定します。
5.7 Oracle Identity Governance用にSSLを使用するJavaコネクタ・サーバーを構成
ConnectorServer.properties
ファイルにキーストア資格証明を指定して、Javaコネクタ・サーバーのSSLを構成できます。
5.9 Javaコネクタ・サーバーのアップグレード
Javaコネクタ・サーバー・パックの12.2.1.3.0バージョンでは、connectorserver.protocol
プロパティを使用してOracle Identity ManagerとJavaコネクタ・サーバーの間のSSL通信のプロトコルを選択できます。このプロパティのサポートされている値は、TLSv1
、TLSv1.1
、TLSv1.2
です。ここで、TLSv1
はTLS1.0プロトコル、TLSv1.1
はTLS1.1プロトコル、TLSv1.2
はTLS1.2プロトコルをそれぞれ示しています。このプロパティのデフォルト値はTLSv1
で、TLS 1.0プロトコルを意味します。
Javaコネクタ・サーバーをアップグレードするには: