/*
* Copyright (c) 2010-2017 Evolveum
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
*
*/
package com.evolveum.midpoint.provisioning.ucf.impl.connid;
import java.io.FileNotFoundException;
import java.lang.reflect.InvocationTargetException;
import java.net.ConnectException;
import java.net.UnknownHostException;
import java.sql.SQLException;
import java.sql.SQLSyntaxErrorException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
import javax.naming.NameAlreadyBoundException;
import javax.naming.NoPermissionException;
import javax.naming.ServiceUnavailableException;
import javax.naming.directory.AttributeInUseException;
import javax.naming.directory.InvalidAttributeValueException;
import javax.naming.directory.NoSuchAttributeException;
import javax.naming.directory.SchemaViolationException;
import javax.xml.namespace.QName;
import com.evolveum.midpoint.util.exception.SystemException;
import com.evolveum.midpoint.util.logging.LoggingUtils;
import org.identityconnectors.common.security.GuardedByteArray;
import org.identityconnectors.common.security.GuardedString;
import org.identityconnectors.framework.common.exceptions.AlreadyExistsException;
import org.identityconnectors.framework.common.exceptions.ConfigurationException;
import org.identityconnectors.framework.common.exceptions.ConnectionBrokenException;
import org.identityconnectors.framework.common.exceptions.ConnectionFailedException;
import org.identityconnectors.framework.common.exceptions.ConnectorIOException;
import org.identityconnectors.framework.common.exceptions.ConnectorSecurityException;
import org.identityconnectors.framework.common.exceptions.InvalidCredentialException;
import org.identityconnectors.framework.common.exceptions.OperationTimeoutException;
import org.identityconnectors.framework.common.exceptions.PermissionDeniedException;
import org.identityconnectors.framework.common.exceptions.RetryableException;
import org.identityconnectors.framework.common.exceptions.UnknownUidException;
import org.identityconnectors.framework.common.objects.Attribute;
import org.identityconnectors.framework.common.objects.OperationOptions;
import org.identityconnectors.framework.common.objects.Uid;
import org.identityconnectors.framework.common.objects.filter.AttributeFilter;
import org.identityconnectors.framework.common.objects.filter.CompositeFilter;
import org.identityconnectors.framework.common.objects.filter.Filter;
import com.evolveum.midpoint.prism.PrismPropertyValue;
import com.evolveum.midpoint.prism.crypto.EncryptionException;
import com.evolveum.midpoint.prism.crypto.Protector;
import com.evolveum.midpoint.prism.xml.XsdTypeMapper;
import com.evolveum.midpoint.provisioning.ucf.api.GenericFrameworkException;
import com.evolveum.midpoint.schema.constants.SchemaConstants;
import com.evolveum.midpoint.schema.processor.ObjectClassComplexTypeDefinition;
import com.evolveum.midpoint.schema.processor.ResourceAttribute;
import com.evolveum.midpoint.schema.processor.ResourceAttributeDefinition;
import com.evolveum.midpoint.schema.processor.ResourceSchema;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.util.DebugUtil;
import com.evolveum.midpoint.util.PrettyPrinter;
import com.evolveum.midpoint.util.exception.CommunicationException;
import com.evolveum.midpoint.util.exception.ObjectAlreadyExistsException;
import com.evolveum.midpoint.util.exception.ObjectNotFoundException;
import com.evolveum.midpoint.util.exception.SchemaException;
import com.evolveum.midpoint.util.exception.SecurityViolationException;
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
import com.evolveum.prism.xml.ns._public.types_3.ProtectedByteArrayType;
import com.evolveum.prism.xml.ns._public.types_3.ProtectedStringType;
import org.identityconnectors.framework.impl.api.remote.RemoteWrappedException;
/**
* Set of utility methods that work around some of the ConnId and connector problems.
*
* @author Radovan Semancik
*
*/
public class ConnIdUtil {
private static final Trace LOGGER = TraceManager.getTrace(ConnIdUtil.class);
private static final String DOT_NET_EXCEPTION_PACKAGE_PLUS_DOT = "Org.IdentityConnectors.Framework.Common.Exceptions.";
private static final String JAVA_EXCEPTION_PACKAGE = AlreadyExistsException.class.getPackage().getName();
private static final String DOT_NET_ARGUMENT_EXCEPTION = "System.ArgumentException";
private static final String CONNECTIONS_EXCEPTION_CLASS_NAME = "CommunicationsException";
static Throwable processIcfException(Throwable icfException, ConnectorInstanceConnIdImpl conn,
OperationResult icfResult) {
return processIcfException(icfException, conn.getHumanReadableName(), icfResult);
}
/**
* Transform ConnId exception to something more usable.
*
* ICF throws exceptions that contains inner exceptions that cannot be
* reached by current classloader. Such inner exceptions may cause a lot
* of problems in upper layers, such as attempt to serialize/deserialize
* them. Therefore we cannot pass such exceptions to the upper layers.
* As Throwable is immutable and there is no good way how to copy it, we
* just cannot remove the "bad" exceptions from the inner exception stack.
* We need to do the brutal thing: remove all the ICF exceptions and do
* not pass then to upper layers. Try to save at least some information
* and "compress" the class names and messages of the inner ICF exceptions.
* The full exception with a stack trace is logged here, so the details are
* still in the log.
*
* WARNING: This is black magic. Really. Blame Sun Identity Connector
* Framework interface design.
*
* @param connIdException
* exception from the ConnId
* @param connIdResult
* OperationResult to record failure
* @return reasonable midPoint exception
*/
static Throwable processIcfException(Throwable connIdException, String desc,
OperationResult connIdResult) {
// Whole exception handling in this case is a black magic.
// ConnId does not define any checked exceptions so the developers are not
// guided towards good exception handling. Sun Identity Connector Framework (ConnId predecessor)
// haven't had any "best practice" for error reporting. Now there is some
// basic (runtime) exceptions and the connectors are getting somehow better. But this
// nightmarish code is still needed to support bad connectors.
if (connIdException == null) {
connIdResult.recordFatalError("Null exception while processing ConnId exception ");
throw new IllegalArgumentException("Null exception while processing ConnId exception ");
}
LOGGER.error("ConnId Exception {} in {}: {}", connIdException.getClass().getName(),
desc, connIdException.getMessage(), connIdException);
if (connIdException instanceof RemoteWrappedException) {
// brutal hack, for now
RemoteWrappedException remoteWrappedException = (RemoteWrappedException) connIdException;
String className = remoteWrappedException.getExceptionClass();
if (className == null) {
LOGGER.error("Remote ConnId exception without inner exception class name. Continuing with original one: {}", connIdException);
} else if (DOT_NET_ARGUMENT_EXCEPTION.equals(className) && remoteWrappedException.getMessage().contains("0x800708C5")) { // password too weak
connIdException = new SecurityViolationException(connIdException.getMessage(), connIdException);
} else {
if (className.startsWith(DOT_NET_EXCEPTION_PACKAGE_PLUS_DOT)) {
className = JAVA_EXCEPTION_PACKAGE + "." + className.substring(DOT_NET_EXCEPTION_PACKAGE_PLUS_DOT.length());
LOGGER.trace("Translated exception class: {}", className);
}
try {
connIdException = (Throwable) Class.forName(className).getConstructor(String.class, Throwable.class).newInstance(
remoteWrappedException.getMessage(), remoteWrappedException);
} catch (InstantiationException|IllegalAccessException|ClassNotFoundException|NoSuchMethodException|InvocationTargetException e) {
LoggingUtils.logException(LOGGER, "Couldn't unwrap remote ConnId exception, continuing with original one {}", e, connIdException);
}
}
}
if (connIdException instanceof NullPointerException && connIdException.getMessage() != null) {
// NPE with a message text is in fact not a NPE but an application exception
// this usually means that some parameter is missing
Exception newEx = new SchemaException(createMessageFromAllExceptions("Required attribute is missing",connIdException));
connIdResult.recordFatalError("Required attribute is missing: "+connIdException.getMessage(),newEx);
return newEx;
} else if (connIdException instanceof IllegalArgumentException) {
// Let's assume this must be a configuration problem
Exception newEx = new com.evolveum.midpoint.util.exception.ConfigurationException(createMessageFromInnermostException("Configuration error", connIdException));
connIdResult.recordFatalError("Configuration error: "+connIdException.getMessage(), newEx);
return newEx;
}
//fix of MiD-2645
//exception brought by the connector is java.lang.RuntimeException with cause=CommunicationsException
//this exception is to be analyzed here before the following if clause
if (connIdException.getCause() != null){
String exCauseClassName = connIdException.getCause().getClass().getSimpleName();
if (exCauseClassName.equals(CONNECTIONS_EXCEPTION_CLASS_NAME) ){
Exception newEx = new CommunicationException(createMessageFromAllExceptions("Connect error", connIdException));
connIdResult.recordFatalError("Connect error: " + connIdException.getMessage(), newEx);
return newEx;
}
}
if (connIdException.getClass().getPackage().equals(NullPointerException.class.getPackage())) {
// There are java.lang exceptions, they are safe to pass through
connIdResult.recordFatalError(connIdException);
return connIdException;
}
if (connIdException.getClass().getPackage().equals(SchemaException.class.getPackage())) {
// Common midPoint exceptions, pass through
connIdResult.recordFatalError(connIdException);
return connIdException;
}
if (connIdResult == null) {
throw new IllegalArgumentException(createMessageFromAllExceptions("Null parent result while processing ConnId exception",connIdException));
}
// Introspect the inner exceptions and look for known causes
Exception knownCause = lookForKnownCause(connIdException, connIdException, connIdResult);
if (knownCause != null) {
connIdResult.recordFatalError(knownCause);
return knownCause;
}
// ########
// TODO: handle javax.naming.NoPermissionException
// relevant message directly in the exception ("javax.naming.NoPermissionException([LDAP: error code 50 - The entry uid=idm,ou=Administrators,dc=example,dc=com cannot be modified due to insufficient access rights])
// Otherwise try few obvious things
if (connIdException instanceof IllegalArgumentException) {
// This is most likely missing attribute or similar schema thing
Exception newEx = new SchemaException(createMessageFromAllExceptions("Schema violation (most likely)", connIdException));
connIdResult.recordFatalError("Schema violation: "+connIdException.getMessage(), newEx);
return newEx;
} else if (connIdException instanceof ConfigurationException) {
Exception newEx = new com.evolveum.midpoint.util.exception.ConfigurationException(createMessageFromInnermostException("Configuration error", connIdException));
connIdResult.recordFatalError("Configuration error: "+connIdException.getMessage(), newEx);
return newEx;
} else if (connIdException instanceof AlreadyExistsException) {
Exception newEx = new ObjectAlreadyExistsException(createMessageFromAllExceptions(null, connIdException));
connIdResult.recordFatalError("Object already exists: "+connIdException.getMessage(), newEx);
return newEx;
} else if (connIdException instanceof PermissionDeniedException) {
Exception newEx = new SecurityViolationException(createMessageFromAllExceptions(null, connIdException));
connIdResult.recordFatalError("Security violation: "+connIdException.getMessage(), newEx);
return newEx;
} else if (connIdException instanceof ConnectionBrokenException) {
Exception newEx = new CommunicationException(createMessageFromAllExceptions("Connection broken", connIdException));
connIdResult.recordFatalError("Connection broken: "+connIdException.getMessage(), newEx);
return newEx;
} else if (connIdException instanceof ConnectionFailedException) {
Exception newEx = new CommunicationException(createMessageFromAllExceptions("Connection failed", connIdException));
connIdResult.recordFatalError("Connection failed: "+connIdException.getMessage(), newEx);
return newEx;
} else if (connIdException instanceof UnknownHostException) {
Exception newEx = new CommunicationException(createMessageFromAllExceptions("Unknown host", connIdException));
connIdResult.recordFatalError("Unknown host: "+connIdException.getMessage(), newEx);
return newEx;
} else if (connIdException instanceof ConnectorIOException) {
Exception newEx = new CommunicationException(createMessageFromAllExceptions("IO error", connIdException));
connIdResult.recordFatalError("IO error: "+connIdException.getMessage(), newEx);
return newEx;
} else if (connIdException instanceof InvalidCredentialException) {
Exception newEx = new GenericFrameworkException(createMessageFromAllExceptions("Invalid credentials", connIdException));
connIdResult.recordFatalError("Invalid credentials: "+connIdException.getMessage(), newEx);
return newEx;
} else if (connIdException instanceof OperationTimeoutException) {
Exception newEx = new CommunicationException(createMessageFromAllExceptions("Operation timed out", connIdException));
connIdResult.recordFatalError("Operation timed out: "+connIdException.getMessage(), newEx);
return newEx;
} else if (connIdException instanceof UnknownUidException) {
Exception newEx = new ObjectNotFoundException(createMessageFromAllExceptions(null, connIdException));
connIdResult.recordFatalError("Unknown UID: "+connIdException.getMessage(), newEx);
return newEx;
} else if (connIdException instanceof InvalidAttributeValueException) {
Exception newEx = new SchemaException(createMessageFromAllExceptions(null, connIdException));
connIdResult.recordFatalError("Schema violation: "+connIdException.getMessage(), newEx);
return newEx;
} else if (connIdException instanceof RetryableException) {
Exception newEx = new CommunicationException(createMessageFromAllExceptions(null, connIdException));
connIdResult.recordFatalError("Retryable errror: "+connIdException.getMessage(), newEx);
return newEx;
} else if (connIdException instanceof ConnectorSecurityException) {
// Note: connection refused is also packed inside
// ConnectorSecurityException. But that will get addressed by the
// lookForKnownCause(..) before
// Maybe we need special exception for security?
Exception newEx = new SecurityViolationException(createMessageFromAllExceptions("Security violation",connIdException));
connIdResult.recordFatalError(
"Security violation: " + connIdException.getMessage(), newEx);
return newEx;
}
// Fallback
Exception newEx = new GenericFrameworkException(createMessageFromAllExceptions(null,connIdException));
connIdResult.recordFatalError(newEx);
return newEx;
}
private static Exception lookForKnownCause(Throwable ex,
Throwable originalException, OperationResult parentResult) {
if (ex instanceof FileNotFoundException) {
//fix MID-2711 consider FileNotFoundException as CommunicationException
Exception newEx = new com.evolveum.midpoint.util.exception.CommunicationException(createMessageFromAllExceptions(null, ex));
parentResult.recordFatalError("File not found: "+ex.getMessage(), newEx);
return newEx;
} else if (ex instanceof NameAlreadyBoundException) {
// This is thrown by LDAP connector and may be also throw by similar
// connectors
Exception newEx = new ObjectAlreadyExistsException(createMessageFromAllExceptions(null, ex));
parentResult.recordFatalError("Object already exists: "+ex.getMessage(), newEx);
return newEx;
} else if (ex instanceof javax.naming.CommunicationException) {
// This is thrown by LDAP connector and may be also throw by similar
// connectors
Exception newEx = new CommunicationException(createMessageFromAllExceptions("Communication error", ex));
parentResult.recordFatalError("Communication error: "+ex.getMessage(), newEx);
return newEx;
} else if (ex instanceof ServiceUnavailableException) {
// In some cases (e.g. JDK 1.6.0_31) this is thrown by LDAP connector and may be also throw by similar
// connectors
Exception newEx = new CommunicationException(createMessageFromAllExceptions("Communication error", ex));
parentResult.recordFatalError("Communication error: "+ex.getMessage(), newEx);
return newEx;
} else if (ex instanceof ConnectionBrokenException) {
Exception newEx = new CommunicationException(createMessageFromAllExceptions("Communication error", ex));
parentResult.recordFatalError("Communication error: "+ex.getMessage(), newEx);
return newEx;
} else if (ex instanceof ConnectionFailedException) {
Exception newEx = new CommunicationException(createMessageFromAllExceptions("Communication error", ex));
parentResult.recordFatalError("Communication error: "+ex.getMessage(), newEx);
return newEx;
} else if (ex instanceof SchemaViolationException) {
// This is thrown by LDAP connector and may be also throw by similar
// connectors
Exception newEx = new SchemaException(createMessageFromAllExceptions("Schema violation", ex));
parentResult.recordFatalError("Schema violation: "+ex.getMessage(), newEx);
return newEx;
} else if (ex instanceof org.identityconnectors.framework.common.exceptions.InvalidAttributeValueException) {
Exception newEx = new SchemaException(createMessageFromAllExceptions("Invalid attribute", ex));
parentResult.recordFatalError("Invalid attribute: "+ex.getMessage(), newEx);
return newEx;
} else if (ex instanceof InvalidAttributeValueException) {
// This is thrown by LDAP connector and may be also throw by similar
// connectors
InvalidAttributeValueException e = (InvalidAttributeValueException) ex;
Exception newEx = null;
if (e.getExplanation().contains("unique attribute conflict")){
newEx = new ObjectAlreadyExistsException(createMessageFromAllExceptions("Invalid attribute", ex));
} else{
newEx = new SchemaException(createMessageFromAllExceptions("Invalid attribute", ex));
}
parentResult.recordFatalError("Invalid attribute: "+ex.getMessage(), newEx);
return newEx;
} else if (ex instanceof ConnectException) {
// Buried deep in many exceptions, usually connection refused or
// similar errors
// Note: needs to be after javax.naming.CommunicationException as the
// javax.naming exception has more info (e.g. hostname)
Exception newEx = new CommunicationException(createMessageFromAllExceptions("Connect error", ex));
parentResult.recordFatalError("Connect error: " + ex.getMessage(), newEx);
return newEx;
} else if (ex instanceof SQLSyntaxErrorException) {
// Buried deep in many exceptions, usually DB schema problems of
// DB-based connectors
Exception newEx = new SchemaException(createMessageFromAllExceptions("DB syntax error", ex));
parentResult.recordFatalError("DB syntax error: " + ex.getMessage(), newEx);
return newEx;
} else if (ex instanceof SQLException) {
// Buried deep in many exceptions, usually DB connection problems
Exception newEx = new GenericFrameworkException(createMessageFromAllExceptions("DB error", ex));
parentResult.recordFatalError("DB error: " + ex.getMessage(), newEx);
return newEx;
} else if (ex instanceof UnknownUidException) {
// Object not found
Exception newEx = new ObjectNotFoundException(createMessageFromAllExceptions(null,ex));
parentResult.recordFatalError("Object not found: "+ex.getMessage(), newEx);
return newEx;
} else if (ex instanceof NoPermissionException){
Exception newEx = new SecurityViolationException(createMessageFromAllExceptions(null,ex));
parentResult.recordFatalError("Object not found: "+ex.getMessage(), newEx);
return newEx;
} else if (ex instanceof AttributeInUseException) {
Exception newEx = new SchemaException(createMessageFromAllExceptions(null, ex));
parentResult.recordFatalError("Attribute in use: "+ex.getMessage(), newEx);
return newEx;
} else if (ex instanceof NoSuchAttributeException) {
Exception newEx = new SchemaException(createMessageFromAllExceptions(null, ex));
parentResult.recordFatalError("No such attribute: "+ex.getMessage(), newEx);
return newEx;
}
if (ex.getCause() == null) {
// found nothing
return null;
} else {
// Otherwise go one level deeper ...
return lookForKnownCause(ex.getCause(), originalException,
parentResult);
}
}
public static String dump(Set<Attribute> attributes) {
StringBuilder sb = new StringBuilder();
for (Attribute attr : attributes) {
DebugUtil.indentDebugDump(sb, 1);
sb.append(attr.getClass().getSimpleName()).append("(");
sb.append("name=").append(attr.getName());
List<Object> value = attr.getValue();
sb.append(", value=").append(value);
if (value != null && !value.isEmpty()) {
sb.append(", type=").append(value.iterator().next().getClass().getSimpleName());
}
sb.append(")\n");
}
return sb.toString();
}
public static Object dump(Filter filter) {
StringBuilder sb = new StringBuilder();
dump(filter, sb, 0);
return sb.toString();
}
private static void dump(Filter filter, StringBuilder sb, int indent) {
DebugUtil.indentDebugDump(sb, indent);
if (filter == null) {
sb.append("null");
return;
}
sb.append(filter.toString());
if (filter instanceof AttributeFilter) {
sb.append("(");
Attribute attribute = ((AttributeFilter)filter).getAttribute();
sb.append(attribute.getName());
sb.append(": ");
List<Object> value = attribute.getValue();
sb.append(value);
// if (value != null && !value.isEmpty()) {
// sb.append(" :").append(attribute.getValue().iterator().next().getClass().getSimpleName());
// }
sb.append(")");
}
if (filter instanceof CompositeFilter) {
for(Filter subfilter: ((CompositeFilter)filter).getFilters()) {
sb.append("\n");
dump(subfilter,sb,indent+1);
}
}
}
private static String createMessageFromAllExceptions(String prefix, Throwable ex) {
StringBuilder sb = new StringBuilder();
if (prefix != null) {
sb.append(prefix);
sb.append(": ");
}
addAllExceptionsToMessage(sb,ex);
return sb.toString();
}
private static void addAllExceptionsToMessage(StringBuilder sb, Throwable ex) {
sb.append(ex.getClass().getName());
sb.append("(");
// Make sure that no non-printable chars shall pass
// e.g. AD LDAP produces non-printable chars in the messages
if (ex.getMessage() == null) {
sb.append("null");
} else {
sb.append(ex.getMessage().replaceAll("\\p{C}", "?"));
}
sb.append(")");
if (ex.getCause() != null) {
sb.append("->");
addAllExceptionsToMessage(sb, ex.getCause());
}
}
private static String createMessageFromInnermostException(String prefix, Throwable ex) {
StringBuilder sb = new StringBuilder();
if (prefix != null) {
sb.append(prefix);
sb.append(": ");
}
addInnermostExceptionsToMessage(sb,ex);
return sb.toString();
}
private static void addInnermostExceptionsToMessage(StringBuilder sb, Throwable ex) {
if (ex.getCause() != null) {
addInnermostExceptionsToMessage(sb, ex.getCause());
} else {
sb.append(ex.getMessage());
}
}
public static ResourceAttributeDefinition<String> getUidDefinition(ObjectClassComplexTypeDefinition def, ResourceSchema schema) {
ObjectClassComplexTypeDefinition concreteObjectClassDefinition = getConcreteObjectClassDefinition(def, schema);
if (concreteObjectClassDefinition == null) {
return null;
} else {
return getUidDefinition(concreteObjectClassDefinition);
}
}
public static ObjectClassComplexTypeDefinition getConcreteObjectClassDefinition(ObjectClassComplexTypeDefinition def, ResourceSchema schema) {
if (def == null) {
// Return definition from any structural object class. If there is no specific object class definition then
// the UID definition must be the same in all structural object classes and that means that we can use
// definition from any structural object class.
for (ObjectClassComplexTypeDefinition objectClassDefinition: schema.getObjectClassDefinitions()) {
if (!objectClassDefinition.isAuxiliary()) {
return objectClassDefinition;
}
}
return null;
} else {
return def;
}
}
public static ResourceAttributeDefinition<String> getUidDefinition(ObjectClassComplexTypeDefinition def) {
Collection<? extends ResourceAttributeDefinition> primaryIdentifiers = def.getPrimaryIdentifiers();
if (primaryIdentifiers.size() > 1) {
throw new UnsupportedOperationException("Multiple primary identifiers are not supported");
}
if (primaryIdentifiers.size() == 1) {
return primaryIdentifiers.iterator().next();
} else {
// fallback, compatibility
return def.findAttributeDefinition(SchemaConstants.ICFS_UID);
}
}
public static ResourceAttributeDefinition<String> getNameDefinition(ObjectClassComplexTypeDefinition def) {
Collection<? extends ResourceAttributeDefinition> secondaryIdentifiers = def.getSecondaryIdentifiers();
if (secondaryIdentifiers.size() > 1) {
throw new UnsupportedOperationException("Multiple secondary identifiers are not supported");
}
if (secondaryIdentifiers.size() == 1) {
return secondaryIdentifiers.iterator().next();
} else {
// fallback, compatibility
return def.findAttributeDefinition(SchemaConstants.ICFS_NAME);
}
}
public static Collection<ResourceAttribute<?>> convertToIdentifiers(Uid uid,
ObjectClassComplexTypeDefinition ocDef, ResourceSchema resourceSchema) throws SchemaException {
ObjectClassComplexTypeDefinition concreteObjectClassDefinition = getConcreteObjectClassDefinition(ocDef, resourceSchema);
if (concreteObjectClassDefinition == null) {
throw new SchemaException("Concrete object class of "+ocDef+" cannot be found");
}
ResourceAttributeDefinition<String> uidDefinition = getUidDefinition(concreteObjectClassDefinition);
if (uidDefinition == null) {
throw new SchemaException("No definition for ConnId UID attribute found in definition "
+ ocDef);
}
Collection<ResourceAttribute<?>> identifiers = new ArrayList<ResourceAttribute<?>>(2);
ResourceAttribute<String> uidRoa = uidDefinition.instantiate();
uidRoa.setValue(new PrismPropertyValue<String>(uid.getUidValue()));
identifiers.add(uidRoa);
if (uid.getNameHint() != null) {
ResourceAttributeDefinition<String> nameDefinition = getNameDefinition(concreteObjectClassDefinition);
if (nameDefinition == null) {
throw new SchemaException("No definition for ConnId NAME attribute found in definition "
+ ocDef);
}
ResourceAttribute<String> nameRoa = nameDefinition.instantiate();
nameRoa.setValue(new PrismPropertyValue<String>(uid.getNameHintValue()));
identifiers.add(nameRoa);
}
return identifiers;
}
public static GuardedString toGuardedString(ProtectedStringType ps, String propertyName, Protector protector) {
if (ps == null || ps.isHashed()) {
return null;
}
if (!ps.isEncrypted()) {
if (ps.getClearValue() == null) {
return null;
}
LOGGER.warn("Using cleartext value for {}", propertyName);
return new GuardedString(ps.getClearValue().toCharArray());
}
try {
return new GuardedString(protector.decryptString(ps).toCharArray());
} catch (EncryptionException e) {
LOGGER.error("Unable to decrypt value of element {}: {}",
new Object[] { propertyName, e.getMessage(), e });
throw new SystemException("Unable to decrypt value of element " + propertyName + ": "
+ e.getMessage(), e);
} catch (RuntimeException e) {
// The ConnId will mask encryption exceptions into RuntimeException
throw new SystemException("Unable to encrypt value of element " + propertyName + ": "
+ e.getMessage(), e);
}
}
public static Object convertValueToIcf(Object value, Protector protector, QName propName) throws SchemaException {
if (value == null) {
return null;
}
if (value instanceof PrismPropertyValue) {
return convertValueToIcf(((PrismPropertyValue) value).getValue(), protector, propName);
}
if (value instanceof ProtectedStringType) {
ProtectedStringType ps = (ProtectedStringType) value;
return toGuardedString(ps, protector, propName.toString());
}
return value;
}
public static GuardedString toGuardedString(ProtectedStringType ps, Protector protector, String propertyName) {
if (ps == null) {
return null;
}
if (!protector.isEncrypted(ps)) {
if (ps.getClearValue() == null) {
return null;
}
// LOGGER.warn("Using cleartext value for {}", propertyName);
return new GuardedString(ps.getClearValue().toCharArray());
}
try {
return new GuardedString(protector.decryptString(ps).toCharArray());
} catch (EncryptionException e) {
// LOGGER.error("Unable to decrypt value of element {}: {}",
// new Object[] { propertyName, e.getMessage(), e });
throw new SystemException("Unable to decrypt value of element " + propertyName + ": "
+ e.getMessage(), e);
}
}
public static String dumpOptions(OperationOptions options) {
if (options == null) {
return "null";
}
StringBuilder sb = new StringBuilder();
sb.append("OperationOptions(");
Map<String, Object> map = options.getOptions();
if (map == null) {
sb.append("null");
} else {
for (Entry<String,Object> entry: map.entrySet()) {
sb.append(entry.getKey());
sb.append("=");
sb.append(PrettyPrinter.prettyPrint(entry.getValue()));
sb.append(",");
}
}
sb.append(")");
return sb.toString();
}
public static QName icfTypeToXsdType(Class<?> type, boolean isConfidential) {
// For arrays we are only interested in the component type
if (isMultivaluedType(type)) {
type = type.getComponentType();
}
QName propXsdType = null;
if (GuardedString.class.equals(type) ||
(String.class.equals(type) && isConfidential)) {
// GuardedString is a special case. It is a ICF-specific
// type
// implementing Potemkin-like security. Use a temporary
// "nonsense" type for now, so this will fail in tests and
// will be fixed later
// propXsdType = SchemaConstants.T_PROTECTED_STRING_TYPE;
propXsdType = ProtectedStringType.COMPLEX_TYPE;
} else if (GuardedByteArray.class.equals(type) ||
(Byte.class.equals(type) && isConfidential)) {
// GuardedString is a special case. It is a ICF-specific
// type
// implementing Potemkin-like security. Use a temporary
// "nonsense" type for now, so this will fail in tests and
// will be fixed later
// propXsdType = SchemaConstants.T_PROTECTED_BYTE_ARRAY_TYPE;
propXsdType = ProtectedByteArrayType.COMPLEX_TYPE;
} else {
propXsdType = XsdTypeMapper.toXsdType(type);
}
return propXsdType;
}
public static boolean isMultivaluedType(Class<?> type) {
// We consider arrays to be multi-valued
// ... unless it is byte[] or char[]
return type.isArray() && !type.equals(byte[].class) && !type.equals(char[].class);
}
}