Skip to content

Commit

Permalink
Support for name hint in ConnId Uid. Improved handling of secondary i…
Browse files Browse the repository at this point in the history
…dentifier in provisioning. (MID-2926)
  • Loading branch information
semancik committed Oct 3, 2016
1 parent 9bde83a commit 34f4f3a
Show file tree
Hide file tree
Showing 23 changed files with 811 additions and 179 deletions.
4 changes: 2 additions & 2 deletions build-system/pom.xml
Expand Up @@ -80,7 +80,7 @@
<activiti-spring.version>5.19.0.2</activiti-spring.version>
<commons-email.version>1.3</commons-email.version>
<xmlsec.version>2.0.6</xmlsec.version>
<connid.version>1.4.2.16</connid.version>
<connid.version>1.4.3.0-SNAPSHOT</connid.version>
<jasper.version>6.1.1</jasper.version>
<derby.version>10.11.1.1</derby.version>
<wro4j.version>1.8.0</wro4j.version>
Expand Down Expand Up @@ -603,7 +603,7 @@
<dependency>
<groupId>com.evolveum.polygon</groupId>
<artifactId>connector-ldap</artifactId>
<version>1.4.2.18</version>
<version>1.4.3.0-SNAPSHOT</version>
<exclusions>
<exclusion> <!-- Version in dependency of org.apache.servicemix.bundles:org.apache.servicemix.bundles.dom4j conflicts with xalan. Can be removed when connector version is bumped beyond 1.4.2.17 -->
<groupId>xml-apis</groupId>
Expand Down
Expand Up @@ -59,6 +59,7 @@ public class DummyConfiguration extends AbstractConfiguration {
private Integer minPasswordLength = null;
private boolean addConnectorStateAttributes = false;
private boolean supportReturnDefaultAttributes = false; // used e.g. for livesync vs. auxiliary object classes test
private boolean requireNameHint = false;

/**
* Defines name of the dummy resource instance. There may be several dummy resource running in
Expand Down Expand Up @@ -345,6 +346,15 @@ public void setSupportReturnDefaultAttributes(boolean supportReturnDefaultAttrib
this.supportReturnDefaultAttributes = supportReturnDefaultAttributes;
}

@ConfigurationProperty
public boolean isRequireNameHint() {
return requireNameHint;
}

public void setRequireNameHint(boolean requireNameHint) {
this.requireNameHint = requireNameHint;
}

/**
* {@inheritDoc}
*/
Expand Down
Expand Up @@ -49,6 +49,7 @@
import org.identityconnectors.framework.common.objects.filter.EqualsFilter;
import org.identityconnectors.framework.common.objects.filter.Filter;
import org.identityconnectors.framework.common.objects.filter.FilterTranslator;
import org.identityconnectors.framework.common.objects.filter.FilterVisitor;
import org.identityconnectors.framework.common.objects.filter.GreaterThanFilter;
import org.identityconnectors.framework.common.objects.filter.GreaterThanOrEqualFilter;
import org.identityconnectors.framework.common.objects.filter.LessThanFilter;
Expand Down Expand Up @@ -289,6 +290,7 @@ public Uid create(final ObjectClass objectClass, final Set<Attribute> createAttr
public Uid update(ObjectClass objectClass, Uid uid, Set<Attribute> replaceAttributes, OperationOptions options) {
log.info("update::begin");
validate(objectClass);
validate(uid);

try {

Expand Down Expand Up @@ -514,6 +516,7 @@ public Uid update(ObjectClass objectClass, Uid uid, Set<Attribute> replaceAttrib
*/
public Uid addAttributeValues(ObjectClass objectClass, Uid uid, Set<Attribute> valuesToAdd, OperationOptions options) {
validate(objectClass);
validate(uid);

try {

Expand Down Expand Up @@ -705,6 +708,7 @@ public Uid addAttributeValues(ObjectClass objectClass, Uid uid, Set<Attribute> v
*/
public Uid removeAttributeValues(ObjectClass objectClass, Uid uid, Set<Attribute> valuesToRemove, OperationOptions options) {
validate(objectClass);
validate(uid);

try {

Expand Down Expand Up @@ -881,10 +885,12 @@ public Uid removeAttributeValues(ObjectClass objectClass, Uid uid, Set<Attribute
public void delete(final ObjectClass objectClass, final Uid uid, final OperationOptions options) {
log.info("delete::begin");
validate(objectClass);
validate(uid);

String id = uid.getUidValue();

try {

if (ObjectClass.ACCOUNT.is(objectClass.getObjectClassValue())) {
if (configuration.getUidMode().equals(DummyConfiguration.UID_MODE_NAME)) {
resource.deleteAccountByName(id);
Expand Down Expand Up @@ -1142,6 +1148,7 @@ public FilterTranslator<Filter> createFilterTranslator(ObjectClass objectClass,
public void executeQuery(ObjectClass objectClass, Filter query, ResultsHandler handler, OperationOptions options) {
log.info("executeQuery({0},{1},{2},{3})", objectClass, query, handler, options);
validate(objectClass);
validate(query);
notNull(handler, "Results handled object can't be null.");

Collection<String> attributesToGet = getAttrsToGet(options);
Expand Down Expand Up @@ -1442,7 +1449,11 @@ public void sync(ObjectClass objectClass, SyncToken token, SyncResultsHandler ha
if (configuration.getUidMode().equals(DummyConfiguration.UID_MODE_NAME)) {
uid = new Uid(delta.getObjectName());
} else if (configuration.getUidMode().equals(DummyConfiguration.UID_MODE_UUID)) {
uid = new Uid(delta.getObjectId());
if (nameHintChecksEnabled()) {
uid = new Uid(delta.getObjectId(), new Name(delta.getObjectName()));
} else {
uid = new Uid(delta.getObjectId());
}
} else {
throw new IllegalStateException("Unknown UID mode "+configuration.getUidMode());
}
Expand Down Expand Up @@ -1898,4 +1909,113 @@ private boolean attributesToGetHasAttribute(Collection<String> attributesToGet,
return attributesToGet.contains(attrName);
}

public void validate(ObjectClass oc) {
if (oc == null) {
throw new IllegalArgumentException("Object class must not be null.");
}
}

public void validate(Uid uid) {
if (uid == null) {
throw new IllegalArgumentException("Uid must not be null.");
}
if (nameHintChecksEnabled()) {
if (uid.getNameHint() == null) {
throw new IllegalArgumentException("Uid name hint must not be null.");
}
if (StringUtils.isBlank(uid.getNameHintValue())) {
throw new IllegalArgumentException("Uid name hint must not be empty.");
}
}
}

private void validate(Filter filter) {
if (filter == null) {
return;
}
if (nameHintChecksEnabled()) {
filter.accept(new FilterVisitor<String,String>() {

@Override
public String visitAndFilter(String p, AndFilter filter) {
return null;
}

@Override
public String visitContainsFilter(String p, ContainsFilter filter) {
return null;
}

@Override
public String visitContainsAllValuesFilter(String p, ContainsAllValuesFilter filter) {
return null;
}

@Override
public String visitEqualsFilter(String p, EqualsFilter filter) {
if (filter.getAttribute().is(Uid.NAME)) {
Uid uid = (Uid)filter.getAttribute();
if (uid.getNameHint() == null) {
throw new IllegalArgumentException("Uid name hint must not be null in filter "+filter);
}
if (StringUtils.isBlank(uid.getNameHintValue())) {
throw new IllegalArgumentException("Uid name hint must not be empty in filter "+filter);
}
}
return null;
}

@Override
public String visitExtendedFilter(String p, Filter filter) {
return null;
}

@Override
public String visitGreaterThanFilter(String p, GreaterThanFilter filter) {
return null;
}

@Override
public String visitGreaterThanOrEqualFilter(String p, GreaterThanOrEqualFilter filter) {
return null;
}

@Override
public String visitLessThanFilter(String p, LessThanFilter filter) {
return null;
}

@Override
public String visitLessThanOrEqualFilter(String p, LessThanOrEqualFilter filter) {
return null;
}

@Override
public String visitNotFilter(String p, NotFilter filter) {
return null;
}

@Override
public String visitOrFilter(String p, OrFilter filter) {
return null;
}

@Override
public String visitStartsWithFilter(String p, StartsWithFilter filter) {
return null;
}

@Override
public String visitEndsWithFilter(String p, EndsWithFilter filter) {
return null;
}

}, null);
}
}

private boolean nameHintChecksEnabled() {
return configuration.isRequireNameHint() && !resource.isDisableNameHintChecks();
}

}
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2010-2013 Evolveum
* Copyright (c) 2010-201ž Evolveum
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -58,12 +58,6 @@ public static <T> T getAttributeSingleValue(Set<Attribute> attributes, String at
return null;
}

public static void validate(ObjectClass oc) {
if (oc == null) {
throw new IllegalArgumentException("Object class must not be null.");
}
}

public static void notNull(Object object, String message) {
if (object == null) {
throw new IllegalArgumentException(message);
Expand Down
Expand Up @@ -110,6 +110,8 @@ public class DummyResource implements DebugDumpable {

private boolean generateAccountDescriptionOnCreate = false; // simulates volatile behavior (on create)
private boolean generateAccountDescriptionOnUpdate = false; // simulates volatile behavior (on update)

private boolean disableNameHintChecks = false;

// Following two properties are just copied from the connector
// configuration and can be checked later. They are otherwise
Expand Down Expand Up @@ -315,6 +317,14 @@ public void setGenerateAccountDescriptionOnUpdate(boolean generateAccountDescrip
this.generateAccountDescriptionOnUpdate = generateAccountDescriptionOnUpdate;
}

public boolean isDisableNameHintChecks() {
return disableNameHintChecks;
}

public void setDisableNameHintChecks(boolean disableNameHintChecks) {
this.disableNameHintChecks = disableNameHintChecks;
}

public Collection<String> getForbiddenNames() {
return forbiddenNames;
}
Expand Down
Expand Up @@ -460,13 +460,6 @@ public <T> void collectEntitlementsAsObjectOperationDelete(ProvisioningContext s

ObjectQuery query = createQuery(assocDefType, assocAttrDef, valueAttr);

// ObjectFilter filter = EqualsFilter.createEqual(new ItemPath(ShadowType.F_ATTRIBUTES), assocAttrDef, valueAttr.getValue());
// ObjectFilter filter = InFilter.createIn(new ItemPath(ShadowType.F_ATTRIBUTES, assocAttrDef.getName()), assocAttrDef, valueAttr.getValue());
// ObjectFilter filter = EqualsFilter.createEqual(new ItemPath(ShadowType.F_ATTRIBUTES, assocAttrDef.getName()), assocAttrDef, valueAttr.getValue());
// ObjectQuery query = ObjectQuery.createObjectQuery(filter);
// new ObjectQuery();
// query.setFilter(filter);

AttributesToReturn attributesToReturn = ProvisioningUtil.createAttributesToReturn(entitlementCtx);

SearchHierarchyConstraints searchHierarchyConstraints = null;
Expand All @@ -482,13 +475,15 @@ public <T> void collectEntitlementsAsObjectOperationDelete(ProvisioningContext s
ResultHandler<ShadowType> handler = new ResultHandler<ShadowType>() {
@Override
public boolean handle(PrismObject<ShadowType> entitlementShadow) {
Collection<? extends ResourceAttribute<?>> identifiers = ShadowUtil.getPrimaryIdentifiers(entitlementShadow);
ResourceObjectDiscriminator disc = new ResourceObjectDiscriminator(entitlementOcDef.getTypeName(), identifiers);
Collection<? extends ResourceAttribute<?>> primaryIdentifiers = ShadowUtil.getPrimaryIdentifiers(entitlementShadow);
ResourceObjectDiscriminator disc = new ResourceObjectDiscriminator(entitlementOcDef.getTypeName(), primaryIdentifiers);
ResourceObjectOperations operations = roMap.get(disc);
if (operations == null) {
operations = new ResourceObjectOperations();
roMap.put(disc, operations);
operations.setResourceObjectContext(entitlementCtx);
Collection<? extends ResourceAttribute<?>> allIdentifiers = ShadowUtil.getAllIdentifiers(entitlementShadow);
operations.setAllIdentifiers(allIdentifiers);
}

PropertyDelta<T> attributeDelta = null;
Expand Down Expand Up @@ -706,7 +701,7 @@ private <TV,TA> PrismObject<ShadowType> collectEntitlementAsObjectOperation(Prov
ResourceAttribute<TV> valueAttr = ShadowUtil.getAttribute(subjectShadow, valueAttrName);
if (valueAttr == null) {
if (!ShadowUtil.isFullShadow(subjectShadow)) {
Collection<ResourceAttribute<?>> subjectIdentifiers = ShadowUtil.getPrimaryIdentifiers(subjectShadow);
Collection<ResourceAttribute<?>> subjectIdentifiers = ShadowUtil.getAllIdentifiers(subjectShadow);
LOGGER.trace("Fetching {} ({})", subjectShadow, subjectIdentifiers);
subjectShadow = resourceObjectReferenceResolver.fetchResourceObject(subjectCtx, subjectIdentifiers, null, result);
subjectShadowAfter = subjectShadow;
Expand Down
Expand Up @@ -421,9 +421,8 @@ public int synchronize(ResourceShadowDiscriminator shadowCoordinates, Task task,
SchemaDebugUtil.prettyPrint(tokenProperty));
}

LOGGER.trace("Calling shadow cache to synchronize");
processedChanges = getShadowCache(Mode.STANDARD).synchronize(shadowCoordinates, tokenProperty, task, result);
LOGGER.trace("shadow cache to synchronized {} entries", processedChanges);
LOGGER.debug("Synchronization of {} done, token {}, {} changes", resource, tokenProperty, processedChanges);

} catch (ObjectNotFoundException e) {
ProvisioningUtil.recordFatalError(LOGGER, result, "Synchronization error: object not found: " + e.getMessage(), e);
Expand Down
Expand Up @@ -319,7 +319,7 @@ public void deleteResourceObject(ProvisioningContext ctx, PrismObject<ShadowType
LOGGER.trace("Deleting resource object {}", shadow);

Collection<? extends ResourceAttribute<?>> identifiers = ShadowUtil
.getPrimaryIdentifiers(shadow);
.getAllIdentifiers(shadow);

if (ProvisioningUtil.isProtectedShadow(ctx.getObjectClassDefinition(), shadow, matchingRuleRegistry)) {
LOGGER.error("Attempt to delete protected resource object " + ctx.getObjectClassDefinition() + ": "
Expand Down Expand Up @@ -392,7 +392,7 @@ public Collection<PropertyDelta<PrismPropertyValue>> modifyResourceObject(
RefinedObjectClassDefinition objectClassDefinition = ctx.getObjectClassDefinition();
Collection<Operation> operations = new ArrayList<Operation>();

Collection<? extends ResourceAttribute<?>> identifiers = ShadowUtil.getPrimaryIdentifiers(repoShadow);
Collection<? extends ResourceAttribute<?>> identifiers = ShadowUtil.getAllIdentifiers(repoShadow);


if (ProvisioningUtil.isProtectedShadow(ctx.getObjectClassDefinition(), repoShadow, matchingRuleRegistry)) {
Expand Down Expand Up @@ -1016,17 +1016,22 @@ private void executeEntitlements(ProvisioningContext subjectCtx,
for (Entry<ResourceObjectDiscriminator,ResourceObjectOperations> entry: roMap.entrySet()) {
ResourceObjectDiscriminator disc = entry.getKey();
ProvisioningContext entitlementCtx = entry.getValue().getResourceObjectContext();
Collection<? extends ResourceAttribute<?>> identifiers = disc.getIdentifiers();
Collection<Operation> operations = entry.getValue().getOperations();
Collection<? extends ResourceAttribute<?>> primaryIdentifiers = disc.getPrimaryIdentifiers();
ResourceObjectOperations resourceObjectOperations = entry.getValue();
Collection<? extends ResourceAttribute<?>> allIdentifiers = resourceObjectOperations.getAllIdentifiers();
if (allIdentifiers == null || allIdentifiers.isEmpty()) {
allIdentifiers = primaryIdentifiers;
}
Collection<Operation> operations = resourceObjectOperations.getOperations();

if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Excuting entitlement change identifiers={}:", identifiers, DebugUtil.debugDump(operations, 1));
LOGGER.trace("Excuting entitlement change identifiers={}:", allIdentifiers, DebugUtil.debugDump(operations, 1));
}

OperationResult result = parentResult.createMinorSubresult(OPERATION_MODIFY_ENTITLEMENT);
try {

executeModify(entitlementCtx, entry.getValue().getCurrentShadow(), identifiers, operations, result);
executeModify(entitlementCtx, entry.getValue().getCurrentShadow(), allIdentifiers, operations, result);

result.recordSuccess();

Expand All @@ -1040,6 +1045,10 @@ private void executeEntitlements(ProvisioningContext subjectCtx,
// properly record the operation in the result.
LOGGER.error("Error while modifying entitlement {} of {}: {}", entitlementCtx, subjectCtx, e.getMessage(), e);
result.recordFatalError(e);
} catch (RuntimeException | Error e) {
LOGGER.error("Error while modifying entitlement {} of {}: {}", entitlementCtx, subjectCtx, e.getMessage(), e);
result.recordFatalError(e);
throw e;
}

}
Expand Down

0 comments on commit 34f4f3a

Please sign in to comment.