| Oracle® Fusion Middleware Oracle Identity Governanceのためのアプリケーションの開発とカスタマイズ 12c (12.2.1.3.3) E91981-03 |
|
![]() 前 |
![]() 次 |
この章は、Identity Connector Framework (ICF)とOracle Identity Managerメタデータを使用してアイデンティティ・コネクタを開発するのに必要な手順全体を確認するチュートリアルとなります。ここには、重要なICFクラスとインタフェース、コネクタ・バンドルおよびコネクタ・サーバーについての情報と、フラット・ファイル・アイデンティティ・コネクタの実装のコード例や、ユーザー・プロビジョニングとリコンシリエーションのプロセス用にOracle Identity Managerメタデータを作成するコード例が含まれています。
この章の構成は、次のとおりです。
フラット・ファイル・コネクタを開発するには、構成インタフェースの実装を開発し、その後、コネクタ・クラスを実装するという手順で開発する必要があります。
開始する前に、フラット・ファイル・コネクタのすべての操作用のIO表現モジュールを準備する必要があります。このことには、次のすべてまたは一部が含まれる場合があります。
フラット・ファイルの列名を読み込み、メタデータ情報を準備します。
指定のデリミタで区切られた、対応する列値を含むレコードをフラット・ファイルに追加します。
UID値に基づいて、フラット・ファイルへのレコードを削除します。
フラット・ファイルで検索操作を実行します。
このチュートリアルではアイデンティティ・コネクタの開発に焦点を当てているため、これらの準備の詳細は説明していません。
注意:
次のサポート・クラスが、アイデンティティ・コネクタ操作時のファイル入力および出力の処理に使用されます。
org.identityconnectors.flatfile.io.FlatFileIOFactory
org.identityconnectors.flatfile.io.FlatFileMetadata
org.identityconnectors.flatfile.io.FlatFileParser
org.identityconnectors.flatfile.io.FlatFileWriter
入力および出力処理のサポート・クラスの実装については、ファイル入力および出力処理用のサポート・クラスを参照してください。
フラット・ファイル・コネクタを開発するには、AbstractConfiguration、PoolableConnector、AbstractFilterTranslatorの各クラスを実装し、コネクタ・バンドルJARファイルを作成します。
この項では、フラット・ファイル・コネクタを開発する手順の概要をコード・サンプルとともに示します。次の項目が含まれます。
フラット・ファイル・コネクタを開発するには、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で示したエントリが含まれている必要があります。
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
}
}
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");
}
}
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;
}
}
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
ファイル入力および出力処理のサポート・クラスはFlatFileIOFactory、FlatFileMetaData、FlatFileParser、FlatFileWriter、FlatfileLineIterator、FlatfileUserAccount、FlatfileAccountConversionHandlerおよびMessages.Propertiesです。
この項では、ファイル入力および出力処理用の次のサポート・クラスの実装について説明します。
次のコード・サンプルは、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);
}
}
次のコード・サンプルは、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");
}
}
次のコード・サンプルは、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;
}
}
次のコード・サンプルは、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");
}
}
}
次のコード・サンプルは、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;
}
}
次のコード・サンプルは、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();
}
.
}
次のコード・サンプルは、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);
}
}
次のコード・サンプルは、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
アイデンティティ・コネクタ・バンドルは、Oracle Identity GovernanceデータベースのICFで使用できる必要があります。
ICFアイデンティティ・コネクタをOracle Identity Managerと統合するには、次に示す項に従います。これらの手順の一部には、Oracle Identity Manager Design Consoleを使用して行う構成が含まれています。
コネクタ・バンドルは、Oracle Identity Managerに対してローカルなコネクタ・サービスで使用できる必要があります。
このことを実現する手順は、次のとおりです。
コネクタ・メタデータ構成は、プロビジョニングとリコンシリエーションの両方で必要となります。
この項における次の一連の手順は、Oracle Identity Manager Design Consoleを使用して完了します。
ITリソース・タイプ定義は、リソースの接続情報の表現です。ITリソース・タイプ定義内の構成パラメータは、コネクタ・バンドルの構成パラメータと一致する必要があります。ITリソース内のパラメータの値は、バンドル構成で設定されます。
注意:
バンドル構成によって使用されないパラメータを含めることができます。これらは、バンドル操作に悪影響は与えません。
リソース・オブジェクトは、Oracle Identity Managerでのリソースの表現です。コネクタ・バンドルは、リソース・オブジェクトに関連付けられています。
コネクタ・バンドルでサポートされている様々なオブジェクトに対して個別の参照を定義する必要があります。この参照には、これらのオブジェクトのプロビジョニングおよびリコンシリエーションに関連する情報を含めることができます。メインの構成参照は、これらの参照へのポインタが含まれているため、オブジェクト固有の参照のルートになります。次の項では、参照の作成方法について説明します。
(ITリソース・タイプ定義の作成で定義した)構成参照には、接続情報とはみなされないコネクタ・バンドル構成が保持されます。ITリソース・タイプ定義で構成パラメータが見つからない場合、Oracle Identity Managerは構成参照を検索します。メインの構成参照には、バンドル・プロパティおよびバンドル構成が含まれています。バンドル・プロパティ・パラメータは、正しいバンドルの識別に必要であるため、必須です。ITリソース・タイプ定義の一部として定義されていないバンドル構成(ITリソース・タイプの定義の作成を参照)は、ここで宣言できます。
注意:
コード・キーの値は、図と完全に一致する必要があります。デコードの値は、コネクタ・バンドルに固有です。
メインの構成参照を作成するには、次の手順を実行します。
オブジェクト・タイプの構成参照には、特定のオブジェクト・タイプに固有のパラメータが含まれています。オブジェクト・タイプは、アイデンティティ・コネクタが動作するエンティティです。ICF ObjectClassにマップされます。メインの構成参照の作成でUser Configuration Lookupが参照されているため、Userがオブジェクト・タイプとなり、この場合はObjectClass.ACCOUNTにマップされています。(RolesとUserJobDataが、その他の2つのオブジェクト・タイプです。)オブジェクト・タイプ名は、アイデンティティ・コネクタ・バンドルでサポートされているオブジェクト・クラス名と一致する必要があります。Userオブジェクト・タイプは事前定義済のObjectClass.ACCOUNTにマップされ、Groupオブジェクト・タイプは事前定義済ObjectClass.GROUPにマップされます。アイデンティティ・コネクタが複数のオブジェクトをサポートしている場合、この手順をそれぞれに対して繰り返す必要があります。
注意:
これらのユースケースは基本機能のみを対象としているため、この構成は必須属性として保持されます。
オブジェクト・タイプの構成参照を作成するには、次の手順を実行します。
フラット・ファイル・プロビジョニング用にOracle Identity Managerを構成するには、プロビジョニング・メタデータを作成します。そのためには、プロセス・フォーム、アダプタ、プロセス定義およびプロビジョニング属性マッピング参照を作成します。
次の項は、フラット・ファイル・プロビジョニング用にOracle Identity Managerを構成するために実行する必要があります。
プロセス・フォームは、Oracle Identity Managerでのオブジェクト属性の表現として使用されます。
この項では、プロセス・フォームとプロセス・フォームを作成する方法について説明します。次の項目が含まれます。
プロセス・フォームは、Oracle Identity Managerでのオブジェクト属性の表現として使用されます。これにより、操作のためにオブジェクト属性がコネクタ・バンドルに渡される前に、ユーザーが設定を容易に入力できます。
プロセス・フォームで定義された属性は、規則ではありません。このフォームは、アイデンティティ・コネクタに渡される必要がある属性に対するチャレンジの方法です。一般的に、アイデンティティ・コネクタのサポートされている各属性に対して属性を定義します。
注意:
アイデンティティ・コネクタ属性で1対1のマッピングを設定することをお薦めします。
対応するITリソース・タイプ定義に関連付ける必要があるITリソースを問い合せるためのフィールドが存在する必要があります。各フィールドの変数タイプは、オブジェクト属性のタイプにマップする必要があります。
表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です。
コネクタ・バンドルでサポートされているすべての操作(作成、更新、削除など)に対してアダプタを作成する必要があります。
アダプタを作成するには、次の手順を実行します。
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」を選択します。
構成を保存します。
(すでに変数リストに追加した)変数を、適切なメソッド入力および出力にマップします。
構成パラメータを、適切なメソッド入力および出力にマップします。
データベース参照はデータベース参照(アダプタ参照)にマップし、戻り変数は戻り変数(アダプタ変数)にマップします。
アダプタを保存してビルドします。
プロセス定義では、特定の操作用のコネクタ・バンドルの動作を定義します。すべての操作には、関連付けられている対応タスクがあります。
次の手順では、作成操作のプロセス定義、およびプロセス・タスクの統合を構成します。
プロビジョニング属性マッピング参照には、アイデンティティ・コネクタ・バンドル属性へのOracle Identity Managerフィールドのマッピングが含まれます。
この項では、プロビジョニング属性マッピング参照に関する次のトピックについて説明します。
プロビジョニング属性マッピング参照には、アイデンティティ・コネクタ・バンドル属性へのOracle Identity Managerフィールドのマッピングが含まれます。プロビジョニング属性マッピング参照の説明を次に示します。
コード・キーは、プロセス・フォームのフィールド・ラベルです。
デコードは、アイデンティティ・コネクタ・バンドル属性です。
子フォーム属性は、入力での埋込みオブジェクトとして構成できます。
アイデンティティ・コネクタのプロビジョニング操作は、レスポンスでUIDを返します。これは、アイデンティティ・コネクタ・バンドル属性に対してコーディングして、フォーム・フィールドに設定できます。
プロビジョニング属性マッピングの場合、次のフィールド・フラグをコード・キーに追加できます。
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]のようになります。
フラット・ファイルからレコードのリコンシリエーションを構成できます。ターゲット・リコンシリエーションを例として使用できますが、信頼できるリコンシリエーションも同様の方法で構成できます。
リストされている順序で手順を実行してください。
デフォルトでは、リコンシリエーションはコネクタ・バンドルで検索操作を使用します。この操作は、Oracle Identity Managerを使用して構成されたスケジュール済タスクで呼び出されます。この手順は、次のサブ手順で構成されています。
リコンシリエーション・プロファイルでは、リコンシリエーション時のオブジェクト属性の構造を定義します。リコンシリエーション・プロファイルには、リコンシリエーションをサポートしているすべての属性が含まれている必要があります。
リコンシリエーション・プロファイルを作成するには、次の手順を実行します。
リコンシリエーション・アクション・ルールでは、リコンシリエーションの動作を定義します。この手順では、一致が見つかった場合に行われるアクションを定義します。この手順では、Oracle Identity Manager Design Consoleにログインしていることを前提にしています。
リコンシリエーション・マッピングは、プロセス定義で実行する必要があります。これは、サポートされているリコンシリエーション・フィールドを(リソース・オブジェクトから)プロセス・フォーム・フィールドにマッピングすることです。このマッピングは、ターゲット・リコンシリエーションを構成する場合にのみ必要となります。
リコンシリエーション・マッピングを作成するには、次の手順を実行します。
リコンシリエーション属性マッピングの場合は、次のフィールド・フラグをコード・キーに追加できます。
TRUSTED: これは、アカウントのステータスを表すフィールドのリコンシリエーション属性マップで指定する必要があります。このフラグは、信頼できるリコンシリエーションにのみ指定する必要があります。これが指定されている場合、アカウントのステータスは「アクティブ」または「無効」のいずれかになります。それ以外の場合、ステータスは「有効」または「無効」のいずれかになります。たとえば、ラベルがStatusで、値が「アクティブ」または「無効」のいずれかである必要があるフィールドのコード・キーは、Status[TRUSTED]のようになります。
DATE: これは、リコンシリエーション属性マップにおいて、タイプがDateと見なされる必要があるフィールドに指定する必要があります。たとえば、ラベルがTodayで、値が日付形式で表示される必要があるフィールドのコード・キーは、Today[DATE]のようにする必要があります。
フラット・ファイル・アカウントをプロビジョニングするには、ITリソース・パラメータおよびLookup.FF.Configurationパラメータを使用して、タイプがFlat FileであるITリソースを作成します。
フラット・ファイル・コネクタの準備は整っています。ユーザーは、Oracle Identity Managerにログインし、次の手順を使用してITリソース(ターゲット)を作成する必要があります。
タイプがFlat FileであるITリソースを作成します。
必要に応じて、ITリソース・パラメータを指定します。
必要に応じて、Lookup.FF.Configurationに構成パラメータを指定します。
ConnectorServer.propertiesファイルにキーストア資格証明を指定して、Javaコネクタ・サーバーのSSLを構成できます。
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コネクタ・サーバーをアップグレードする方法は次のとおりです。