この章は、Identity Connector Framework (ICF)とOracle Identity Managerメタデータを使用してアイデンティティ・コネクタを開発するのに必要な手順全体を確認するチュートリアルとなります。ここには、重要なICFクラスとインタフェース、コネクタ・バンドルおよびコネクタ・サーバーについての情報と、フラット・ファイル・アイデンティティ・コネクタの実装のコード例や、ユーザー・プロビジョニングとリコンシリエーションのプロセス用にOracle Identity Managerメタデータを作成するコード例が含まれています。この章には次の項目があります。
フラット・ファイル・コネクタは、構成インタフェースの実装を開発し、その後、コネクタ・クラスを実装するという手順で開発します。開始する前に、フラット・ファイル・コネクタのすべての操作用のIO表現モジュールを準備する必要があります。このことには、次のすべてまたは一部が含まれる場合があります。
フラット・ファイルの列名を読み込み、メタデータ情報を準備します。
指定のデリミタで区切られた、対応する列値を含むレコードをフラット・ファイルに追加します。
UID値に基づいて、フラット・ファイルへのレコードを削除します。
フラット・ファイルで検索操作を実行します。
このチュートリアルではアイデンティティ・コネクタの開発に焦点を当てているため、これらの準備の詳細は説明していません。
|
注意: 次のサポート・クラスが、アイデンティティ・コネクタ操作時のファイル入力および出力の処理に使用されます。
入力および出力処理のサポート・クラスの実装については、「ファイル入力および出力処理用のサポート・クラス」を参照してください。 |
フラット・ファイル・コネクタを開発するには、次の手順を実行します。
org.identityconnectors.framework.spi.AbstractConfigurationベース・クラスを拡張して、フラット・ファイル・コネクタ用の構成クラスを実装します。
例10-1に、このサンプルを示します。詳細は、第9.4.1.2項「org.identityconnectors.framework.spi.Configuration」を参照してください。
例10-1 AbstractConfigurationの実装
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インタフェースを実装して、フラット・ファイル・コネクタのコネクタ・クラスを作成します。
例10-2では、CreateOp、DeleteOp、SearchOpおよびUpdateOpの各インタフェースを実装するため、4つの操作すべてをサポートします。FlatFileMetadata、FlatFileParserおよびFlatFileWriterの各クラスは、サポート・クラスです。これらの実装は、ICFに属していないため表示されません。
例10-2 PoolableConnectorの実装
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");
}
}
このコネクタは、ContainsAllValuesFilter操作のみをサポートします。ContainsAllValuesFilter操作を実装します(例10-3に、フィルタ操作を定義するorg.identityconnectors.framework.common.objects.filter.AbstractFilterTranslator<T>のサンプル実装を示します)。
例10-3 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;
}
}
コネクタ・バンドルJARを作成します。MANIFEST.MFファイルには、次のエントリが含まれている必要があります。
ConnectorBundle-FrameworkVersion
ConnectorBundle-Name
ConnectorBundle-Version
例10-4に、MANIFEST.MFファイルの内容を示します。
手順4で作成したコネクタ・バンドルJARを更新します。これを行うには、次の手順を実行します。
コネクタ・バンドルJARを目的の場所に抽出します。
JARを抽出したディレクトリ内にlibディレクトリを作成します。
依存しているサード・パーティのJARをこのlibディレクトリに追加します。
ディレクトリ全体をJARします。
|
注意: MANIFEST.MFファイルには、手順4で示したエントリが含まれている必要があります。 |
この項では、ファイル入力および出力処理用の次のサポート・クラスの実装について説明します。
例10-5に、FlatFileIOFactoryサポート・クラスの実装を示します。
例10-5 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);
}
}
例10-6に、FlatFileMetaDataサポート・クラスの実装を示します。
例10-6 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");
}
}
例10-7に、FlatFileParserサポート・クラスの実装を示します。
例10-7 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;
}
}
例10-8に、FlatFileWriterサポート・クラスの実装を示します。
例10-8 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");
}
}
}
例10-9 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;
}
}
例10-10 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();
}
.
}
例10-11 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);
}
}
例10-12 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 ManagerデータベースのICFで使用できる必要があります。ICFアイデンティティ・コネクタをOracle Identity Managerと統合するには、次に示す項に従います。これらの手順の一部には、Oracle Identity Manager Design Consoleを使用して行う構成が含まれています。
コネクタ・バンドルは、Oracle Identity Managerに対してローカルなコネクタ・サービスで使用できる必要があります。このことを実現する手順は、次のとおりです。
Oracle Identity Managerがインストールされているマシンに、コネクタ・バンドルJARをコピーします。
次のコマンドを実行して、JARをアップロードします。
$MW_HOME/server/bin/UploadJars.sh
|
注意: この章では、DW_HOMEは$MW_HOME/Oracle_IDM1を表します。 |
ICFBundleをJARタイプとして選択します。
コネクタ・バンドルJARの場所を入力します。
[Enter]を押します。
このメタデータ構成は、プロビジョニングとリコンシリエーションの両方で必要となります。この項における一連の手順は、Oracle Identity Manager Design Consoleを使用して完了します。
ITリソース・タイプ定義は、リソースの接続情報の表現です。ITリソース・タイプ定義内の構成パラメータは、コネクタ・バンドルの構成パラメータと一致する必要があります。ITリソース内のパラメータの値は、バンドル構成で設定されます。
|
注意: バンドル構成によって使用されないパラメータを含めることができます。これらは、バンドル操作に悪影響は与えません。 |
Oracle Identity Manager Design Consoleにログインします。
「リソース管理」の下のITリソース・タイプ定義をクリックします。
「サーバー・タイプ」をFlat Fileとして定義して、新しいITリソース・タイプ定義を作成します。
図10-1に示すように、次のパラメータを追加します。
Configuration Lookupは、リソース用のメインの構成参照のマーカーです。パラメータの名前は、Configuration Lookupである必要があります。「デフォルトのフィールド値」に値を追加することをお薦めします。
textFieldDelimeterは、バンドル構成のtextFieldDelimeterパラメータにマップされます。このパラメータの値が渡されます。
storeFileは、バンドル構成のstoreFileパラメータにマップされます。このパラメータの値が渡されます。
リソース・オブジェクトは、Oracle Identity Managerでのリソースの表現です。コネクタ・バンドルは、リソース・オブジェクトに関連付けられています。
Oracle Identity Manager Design Consoleにログインします。
「リソース管理」の下の「リソース・オブジェクト」をクリックします。
FLATFILEROという名前で新しいリソース・オブジェクトを作成します。
リソース・オブジェクトはターゲット・リソースであるため、図10-2に示すように、「信頼できるソース」ボックスは選択しません。
コネクタ・バンドルでサポートされている様々なオブジェクトに対して個別の参照を定義する必要があります。この参照には、これらのオブジェクトのプロビジョニングおよびリコンシリエーションに関連する情報を含めることができます。メインの構成参照は、これらの参照へのポインタが含まれているため、オブジェクト固有の参照のルートになります。次の項では、参照の作成方法について説明します。
(第10.2.2.1項「ITリソース・タイプ定義の作成」で定義した)構成参照には、接続情報とは見なされないコネクタ・バンドル構成が保持されます。ITリソース・タイプ定義で構成パラメータが見つからない場合、Oracle Identity Managerは構成参照を検索します。メインの構成参照には、バンドル・プロパティおよびバンドル構成が含まれています。バンドル・プロパティ・パラメータは、正しいバンドルの識別に必要であるため、必須です。ITリソース・タイプ定義の一部として定義されていないバンドル構成(第10.2.2.1項「ITリソース・タイプの定義の作成」を参照)は、ここで宣言できます。
|
注意: コード・キーの値は、図と完全に一致する必要があります。デコードの値は、コネクタ・バンドルに固有です。 |
Oracle Identity Manager Design Consoleにログインします。
「管理」の下の「参照定義」をクリックします。
新しい参照を作成し、Lookup.FF.Configurationをコードの値として追加します。
図10-3に示すように、次の参照コード情報を追加します。
VERSIONを必須バンドル・バージョンとして追加します。
org.identityconnectors.flatfileを必須バンドル名として追加します。
org.identityconnectors.flatfile.FlatFileConnectorを必須コネクタ名として追加します。
AccountIdをuniqueAttributeNameの値として追加します。AccountIdは、プロビジョニングまたはリコンサイルされるアカウントを表す一意の文字列識別子です。フラット・ファイル内の列の名前です。AccountIdは、一意であり、ユーザー(アカウントの詳細)を一意に表すために使用されます。
ChangeNumberをchangeLogAttributeNameの値として追加します。アカウントが作成されると、作成されたアカウントの合計数を示すために番号が付加されます。この値は、ChangeNumberと呼ばれる変数で保持されます。
OBJECT_TYPE_NAME Configuration Lookupは、特定のオブジェクト・タイプの構成参照です。この例では、User Configuration Lookupが定義されているため、オブジェクト・タイプはUserになります。
オブジェクト・タイプの構成参照には、特定のオブジェクト・タイプに固有のパラメータが含まれています。オブジェクト・タイプは、アイデンティティ・コネクタが動作するエンティティです。ICF ObjectClassにマップされます。第10.2.2.3.1項「メインの構成参照の作成」でUser Configuration Lookupが参照されているため、Userがオブジェクト・タイプとなり、この場合はObjectClass.ACCOUNTにマップされています。(RolesとUserJobDataが、その他の2つのオブジェクト・タイプです。)オブジェクト・タイプ名は、アイデンティティ・コネクタ・バンドルでサポートされているオブジェクト・クラス名と一致する必要があります。Userオブジェクト・タイプは事前定義済のObjectClass.ACCOUNTにマップされ、Groupオブジェクト・タイプは事前定義済ObjectClass.GROUPにマップされます。アイデンティティ・コネクタが複数のオブジェクトをサポートしている場合、この手順をそれぞれに対して繰り返す必要があります。
|
注意: これらのユースケースは基本機能のみを対象としているため、この構成は必須属性として保持されます。 |
Oracle Identity Manager Design Consoleにログインします。
「管理」の下の「参照定義」をクリックします。
新しい参照を作成し、Lookup.FF.UM.Configurationをコードとして追加します。
図10-4に示すように、次の属性を設定します。
|
注意: このチュートリアルは、アイデンティティ・コネクタの実行に最低限必要な構成に焦点を当てています。 |
プロビジョニング属性マップの値はLookup.FF.UM.ProvAttrMapとします。この参照には、Oracle Identity Managerフィールドとアイデンティティ・コネクタ属性との間のマッピングが含まれています。このマッピングは、プロビジョニング時に使用されます。
リコンシリエーション属性マップの値はLookup.FF.UM.ReconAttributeMapとします。この参照には、Oracle Identity Managerリコンシリエーション・フィールドとアイデンティティ・コネクタ属性との間のマッピングが含まれています。このマッピングは、リコンシリエーション時に使用されます。
次の項は、フラット・ファイル・プロビジョニング用にOracle Identity Managerを構成するために実行する必要があります。
プロセス・フォームは、Oracle Identity Managerでのオブジェクト属性の表現として使用されます。これにより、操作のためにオブジェクト属性がコネクタ・バンドルに渡される前に、ユーザーが設定を容易に入力できます。
プロセス・フォームで定義された属性は、規則ではありません。このフォームは、アイデンティティ・コネクタに渡される必要がある属性に対するチャレンジの方法です。一般的に、アイデンティティ・コネクタのサポートされている各属性に対して属性を定義します。
|
注意: アイデンティティ・コネクタ属性で1対1のマッピングを設定することをお薦めします。 |
対応するITリソース・タイプ定義に関連付ける必要があるITリソースを問い合せるためのフィールドが存在する必要があります。各フィールドの変数タイプは、オブジェクト属性のタイプにマップする必要があります。
Oracle Identity Manager Design Consoleにログインします。
「開発ツール」の下の「フォーム・デザイナ」をクリックします。
図10-5に示すように、「表名」がUD_FLAT_FILである新しいフォームを作成します。
表10-1に示されているように、コネクタ・スキーマで定義されている属性を追加します。
表10-1 「フォーム・デザイナ」のフィールド
| 名前 | バリアント | フィールド・ラベル | フィールド・タイプ |
|---|---|---|---|
|
UD_FLAT_FIL_FIRSTNAME |
String |
First Name |
TextField |
|
UD_FLAT_FIL_UID |
String |
Universal ID |
TextField |
|
UD_FLAT_FIL_CHANGENO |
String |
Change Number |
TextField |
|
UD_FLAT_FIL_MAILID |
String |
Email ID |
TextField |
|
UD_FLAT_FIL_SERVER |
long |
Server |
ITResource |
|
UD_FLAT_FIL_LASTNAME |
String |
Last Name |
TextField |
|
UD_FLAT_FIL_ACCOUNTID |
String |
Account ID |
TextField |
|
UD_FLAT_FIL_RETURN |
String |
Return ID |
TextField |
|
注意: フラット・ファイルの列名は、FirstName、ChangeNo、EmailID、Server、LastNameおよびAccountIDです。 |
「Properties」タブをクリックします。
図10-6に示しているように、次のプロパティをServer(ITResourceLookupField)に追加します。
Required = true
Type = Flat File
フォームを保存します。
バージョンのアクティブ化をクリックします。
コネクタ・バンドルでサポートされているすべての操作(作成、更新、削除など)に対してアダプタを作成する必要があります。
Oracle Identity Manager Design Consoleにログインします。
「開発ツール」の下の「アダプタ・ファクトリ」をクリックします。
新しいアダプタを作成し、「アダプタ名」としてFFCreateUserを追加します。
「アダプタ・タイプ」として「プロセス・タスク」を追加します。
アダプタを保存します。
図10-7に示すように、変数リスト・タブをクリックして、次の変数を追加します。
「タイプ」が「文字列」で、マップが実行時に解決であるobjectType。
「タイプ」が「ロング」で、マップが実行時に解決であるprocessInstanceKey。
「タイプ」が「文字列」で、マップが実行時に解決であるitResourceFieldName。
図10-8に示すように、次のサブ手順を実行して、Java機能タスクをアダプタに追加します。
アダプタ・タスク・タブをクリックします。
アダプタを選択し、「追加」をクリックします。
タスク・オプションからJavaを選択します。
APIソースから「icf-oim-intg.jar」を選択します。
APIソースとして「oracle.iam.connetors.icfcommon.prov.ICProvisioninManager」を選択します。
タスクのメソッドとして「createObject」を選択します。
構成を保存します。
(すでに変数リストに追加した)変数を、適切なメソッド入力および出力にマップします。
構成パラメータを、適切なメソッド入力および出力にマップします。
データベース参照はデータベース参照(アダプタ参照)にマップし、戻り変数は戻り変数(アダプタ変数)にマップします。
アダプタを保存してビルドします。
プロセス定義では、特定の操作用のコネクタ・バンドルの動作を定義します。すべての操作には、関連付けられている対応タスクがあります。この手順では、作成操作のプロセス定義、およびプロセス・タスクの統合を構成します。
Oracle Identity Manager Design Consoleにログインします。
「プロセス管理」タブの「プロセス定義」をクリックします。
図10-9に示すように、新しいプロセス定義を作成し、それにFlat Fileという名前を付けます。
プロセスのタイプとして「プロビジョニング」を選択します。
アイデンティティ・コネクタのリソース・オブジェクト名を指定します(この例ではFLATFILERO)。
プロセス・フォームの表名を指定します(この例ではUD_FLAT_FIL)。
プロセス・タスクを追加してCreate Userという名前を付けます。
図10-10に示すように、「Create User」をダブルクリックして、編集します。
統合タブをクリックします。
図10-11に示すように、「追加」をクリックして、リストからFFCreateUserアダプタを選択します。
このアダプタは、コンパイルされた後にのみ使用できます。
次のように変数をマップして、アイデンティティ・コネクタによって返されるレスポンス・コードを設定します。
Adapter Return Variable - レスポンス・コード
Object Type - [Literal:String] User(オブジェクト・タイプの名前)
Process Instance Key - [Process Data] プロセス・インスタンス
IT Resource Field Name - [Literal:String] UD_FLAT_FIL_SERVER (ITリソース情報が含まれたフォーム・フィールド名)
図10-12に示すように、「レスポンス」タブをクリックして、レスポンスを構成します。
UNKNOWNを、ステータスがR(拒否)の不明なレスポンスの受信として指定できます。
SUCCESSを、ステータスがC(完了)の操作の完了として指定できます。
ERRORを、ステータスがRのエラーの発生として指定できます。
タスクのオブジェクト・ステータス・マッピング・タブをクリックします。
図10-13に示すように、オブジェクト・ステータスをステータスがCのProvisionedに更新します。
プロセス・タスクを保存します。
プロビジョニング属性マッピング参照には、アイデンティティ・コネクタ・バンドル属性へのOracle Identity Managerフィールドのマッピングが含まれます。プロビジョニング属性マッピング参照の説明を次に示します。
コード・キーは、プロセス・フォームのフィールド・ラベルです。
デコードは、アイデンティティ・コネクタ・バンドル属性です。
子フォーム属性は、入力での埋込みオブジェクトとして構成できます。
アイデンティティ・コネクタのプロビジョニング操作は、レスポンスでUIDを返します。これは、アイデンティティ・コネクタ・バンドル属性に対してコーディングして、フォーム・フィールドに設定できます。
プロビジョニング属性マッピング参照を作成する手順は、次のとおりです。
Oracle Identity Manager Design Consoleにログインします。
「管理」タブの「参照定義」をクリックします。
新しい参照を作成し、Lookup.FF.UM.ProvAttrMapという名前を付けます。
参照の名前は、オブジェクト・タイプの構成参照から参照されます。第10.2.2.3.2項「オブジェクト・タイプの構成参照の作成」を参照してください。
フォーム・フィールド・ラベルをコード・キーとして追加し、アイデンティティ・コネクタ・バンドル属性をデコードとして追加します。
Return ID : __UID__
Account ID: AccountId
Change Number: ChangeNumber
First Name: FirstName
Last Name: LastName
Email ID: MailId
プロビジョニング属性マッピングの場合、次のフィールド・フラグをコード・キーに追加できます。
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を使用して構成されたスケジュール・タスクで呼び出されます。この手順は、次のサブ手順で構成されています。
スケジュール済タスクを定義するには、次の手順を実行します。
例10-13に示すように、スケジュール済タスク詳細が含まれたデプロイメント・マネージャXMLファイルを作成します。データベースの値を、使用しているデータベースに必ず更新してください。
例10-13 スケジュール済タスク詳細が含まれたデプロイメント・マネージャXML
<?xml version = '1.0' encoding = 'UTF-8'?>
<xl-ddm-data version="2.0.1.0" user="XELSYSADM" database="jdbc:oracle:thin:@localhost:5524/estView.regress.rdbms.dev.mycompany.com" exported-date="1307546406635" description="FF">
<scheduledTask repo-type="MDS" name="Flat File Connector User Reconciliation" mds-path="/db" mds-file="Flat File Connector User Reconciliation.xml">
<completeXml>
<scheduledTasks xmlns="http://xmlns.oracle.com/oim/scheduler">
<task>
<name>Flat File Connector User Reconciliation</name>
<class>oracle.iam.connectors.icfcommon.recon.SearchReconTask</class>
<description>Flat File Connector User Reconciliation</description>
<retry>0</retry>
<parameters>
<string-param required="false" encrypted="false" helpText="Filter">Filter</string-param>
<string-param required="false" encrypted="false" helpText="Incremental Recon Date Attribute">Incremental Recon Date Attribute</string-param>
<string-param required="false" encrypted="false" helpText="IT Resource Name">IT Resource Name</string-param>
<string-param required="false" encrypted="false" helpText="Object Type">Object Type</string-param>
<string-param required="false" encrypted="false" helpText="Latest Token">Latest Token</string-param>
<string-param required="false" encrypted="false" helpText="Resource Object Name">Resource Object Name</string-param>
</parameters>
</task>
</scheduledTasks>
</completeXml>
</scheduledTask>
</xl-ddm-data>
このファイルをFlat File Connector User Reconciliation.xmlとして保存します。
Oracle Identity System Administrationにログインします。「システム管理」の下で、「インポート」をクリックします。
Flat File Connector User Reconciliation.xmlファイルを選択し、「インポート」をクリックします。
ウィザードのステップを完了します。
この手順では、スケジュール済タスクの作成方法について説明します。
Oracle Identity Manager拡張管理にログインします。
「システム管理」タブの「スケジューラ」をクリックします。
図10-14に示すように、スケジュール・タスクを追加し、タイプとしてFlat File Connector User Reconciliationを追加します。
パラメータを次のように設定します。
「ITリソース名」の値はFlat Fileとします。
リソース・オブジェクト名の値はFLATFILEROとします。
「オブジェクト・タイプ」の値は「ユーザー」とします。
「適用」をクリックします。
リコンシリエーション・プロファイルでは、リコンシリエーション時のオブジェクト属性の構造を定義します。リコンシリエーション・プロファイルには、リコンシリエーションをサポートしているすべての属性が含まれている必要があります。
Oracle Identity Manager Design Consoleにログインします。
「リソース管理」の下の「リソース・オブジェクト」をクリックします。
「FLATFILERO」リソース・オブジェクトを開きます。
図10-15に示すように、オブジェクト・リコンシリエーション・タブをクリックします。
次のリコンシリエーション・フィールドを追加します。
First Name [String]
Universal ID [String]
Email ID [String]
IT Resource Name [String]
Last Name [String]
Account ID [String]、必須
構成を保存します。
リコンシリエーション・アクション・ルールでは、リコンシリエーションの動作を定義します。この手順では、一致が見つかった場合に行われるアクションを定義します。この手順では、Oracle Identity Manager Design Consoleにログインしていることを前提にしています。
「FLATFILERO」リソース・オブジェクトを開きます。
オブジェクト・リコンシリエーション・タブをクリックします。
右側のフレームのリコンシリエーション・アクション・ルール・タブをクリックします。
1つのプロセス一致が見つかった場合(「ルール条件」)およびリンクの確立(「アクション」)として定義したアクション・ルールを追加します。
1つのエンティティ一致が見つかった場合(「ルール条件」)およびリンクの確立(「アクション」)として定義したアクション・ルールを追加します。
リコンシリエーション・プロファイルの作成をクリックします。
「保存」をクリックします。
リコンシリエーション・マッピングは、プロセス定義で実行する必要があります。これは、サポートされているリコンシリエーション・フィールドを(リソース・オブジェクトから)プロセス・フォーム・フィールドにマッピングすることです。このマッピングは、ターゲット・リコンシリエーションを構成する場合にのみ必要となります。
Oracle Identity Manager Design Consoleにログインします。
「プロセス管理」の下の「プロセス定義」をクリックします。
Flat Fileプロセス定義を開きます。
図10-17に示すように、リコンシリエーション・フィールド・マッピング・タブをクリックします。
リコンシリエーション・プロファイル・フィールドとプロセス・フォーム・フィールドとの間のマッピングを追加します。
First Name[String] = UD_FLAT_FIL_FIRSTNAME
Email ID[String] = UD_FLAT_FIL_MAILID
IT Resource Name[String] = UD_FLAT_FIL_SERVER
Last Name[String] = UD_FLAT_FIL_LASTNAME
Account ID [String] = UD_FLAT_FIL_ACCOUNTID <KEY>
<KEY>は、Account IDをキー・フィールドとして設定します。
構成を保存します。
リコンシリエーション属性マッピングの場合は、次のフィールド・フラグをコード・キーに追加できます。
TRUSTED: これは、アカウントのステータスを表すフィールドのリコンシリエーション属性マップで指定する必要があります。このフラグは、信頼できるリコンシリエーションにのみ指定する必要があります。これが指定されている場合、アカウントのステータスは「アクティブ」または「無効」のいずれかになります。それ以外の場合、ステータスは「有効」または「無効」のいずれかになります。たとえば、ラベルがStatusで、値が「アクティブ」または「無効」のいずれかである必要があるフィールドのコード・キーは、Status[TRUSTED]のようになります。
DATE: これは、リコンシリエーション属性マップにおいて、タイプがDateと見なされる必要があるフィールドに指定する必要があります。たとえば、ラベルがTodayで、値が日付形式で表示される必要があるフィールドのコード・キーは、Today[DATE]のようにする必要があります。
リコンシリエーション一致ルールでは、ユーザー一致の計算用の式を定義します。
Oracle Identity Manager Design Consoleにログインします。
「開発ツール」の下の「リコンシリエーション・ルール」フォームを開きます。
「ルールの追加」をクリックします。
リソース・オブジェクト「FLATFILERO」を選択します。
このルール要素を保存して追加します。
ユーザー・プロファイル・データの「ユーザー・ログイン」は、「アカウントID」リソース属性と同等です。
ルールを保存します。
フラット・ファイル・コネクタの準備は整っています。ユーザーは、Oracle Identity Managerにログインし、次の手順を使用してITリソース(ターゲット)を作成する必要があります。
タイプがFlat FileであるITリソースを作成します。
必要に応じて、ITリソース・パラメータを指定します。
必要に応じて、Lookup.FF.Configurationに構成パラメータを指定します。
Javaコネクタ・サーバーのSSLを有効化する手順は次のとおりです。
$CONNECTOR_SERVER_HOME/conf/ConnectorServer.propertiesを次のように編集します。
connectorserver.usessl=true
connectorserver.keyStore={full path to your keystore file}
connectorserver.keyStoreType=JKS (optionally you can set key store type, if
not set JSK is used by default)
次の手順でキー・ストア・パスワードを指定します。
ConnectorServer.propertiesにキー・ストア・パスワードを設定します。
ConnectorServer.propertiesにconnectorserver.promptKeyStorePassword=falseを設定します。
次のように、パスワードを設定します。
cd $CONNECTOR_SERVER/bin
UNIXの場合
connectorserver.sh /setKeyStorePassword thepassword
Windowsの場合
ConnectorServer.bat /setKeyStorePassword thepassword
このコマンドにより、ConnectorServer.propertiesの暗号化された値が「connectorserver.keyStorePassword」に設定されます。
コネクタ・サーバーがConnectorServer.propertiesにconnectorserver.promptKeyStorePassword=trueの設定を開始するたびに、キー・ストア・パスワードが要求されます。
次の例スニペットは、Javaコネクタ・サーバーに同梱のConnectorServer.propertiesを示しています。
. ## ## The port we are to run on ## connectorserver.port=8759 ## ## The bundle directory in which to find the bundles ## connectorserver.bundleDir=bundles . ## ## The bundle directory in which to find any libraries needed by bundles at runtime ## connectorserver.libDir=lib . ## ## Set to true to use SSL. ## NOTE: Check also the following settings which are related to SSL: ## connectorserver.promptKeyStorePassword ## connectorserver.keyStore ## connectorserver.keyStoreType ## connectorserver.keyStorePassword connectorserver.usessl=false . ## ## If set to true the user is prompted for key store password at startup. ## If set to false the key store password needs to be set with -setKeyStorePassword command first. ## connectorserver.promptKeyStorePassword=true . ## ## Full path to key store. ## connectorserver.keyStore=/tmp/KeyStore.jks . ## ## KeyStore type ## #connectorserver.keyStoreType=JKS . ## ## Encrypted password. Set this by using the -setKeyStorePassword flag. ## It is used only if connectorserver.promptKeyStorePassword is set to false. ## connectorserver.keyStorePassword= . ## ## Optionally specify a specific address to bind to ## #connectorserver.ifaddress=localhost . ## ## Secure hash of the gateway key. Set this by using the ## -setKey flag ## connectorserver.key=lmA6bMfENJGlIDbfrVtklXFK32s\= . ## ## Use standard JDK logging ## connectorserver.loggerClass=org.identityconnectors.common.logging.impl.JDKLogger