http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/SyncJobImpl.java ---------------------------------------------------------------------- diff --git a/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/SyncJobImpl.java b/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/SyncJobImpl.java new file mode 100644 index 0000000..a786b94 --- /dev/null +++ b/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/SyncJobImpl.java @@ -0,0 +1,219 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.syncope.core.provisioning.java.sync; + +import java.util.Map; +import org.apache.commons.lang3.StringUtils; +import org.apache.syncope.common.lib.mod.ReferenceMod; +import org.apache.syncope.common.lib.mod.RoleMod; +import org.apache.syncope.common.lib.types.SyncPolicySpec; +import org.apache.syncope.core.persistence.api.dao.NotFoundException; +import org.apache.syncope.core.persistence.api.entity.ExternalResource; +import org.apache.syncope.core.persistence.api.entity.SyncPolicy; +import org.apache.syncope.core.persistence.api.entity.role.RMapping; +import org.apache.syncope.core.persistence.api.entity.task.ProvisioningTask; +import org.apache.syncope.core.persistence.api.entity.task.SyncTask; +import org.apache.syncope.core.persistence.api.entity.user.UMapping; +import org.apache.syncope.core.provisioning.api.Connector; +import org.apache.syncope.core.provisioning.api.sync.ProvisioningProfile; +import org.apache.syncope.core.provisioning.api.sync.SyncActions; +import org.apache.syncope.core.misc.security.UnauthorizedRoleException; +import org.apache.syncope.core.misc.spring.ApplicationContextProvider; +import org.apache.syncope.core.provisioning.api.job.SyncJob; +import org.apache.syncope.core.provisioning.api.sync.RoleSyncResultHandler; +import org.apache.syncope.core.provisioning.api.sync.UserSyncResultHandler; +import org.apache.syncope.core.workflow.api.RoleWorkflowAdapter; +import org.identityconnectors.framework.common.objects.ObjectClass; +import org.identityconnectors.framework.common.objects.SyncToken; +import org.quartz.JobExecutionException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.support.AbstractBeanDefinition; + +/** + * Job for executing synchronization (from external resource) tasks. + * + * @see AbstractProvisioningJob + * @see SyncTask + */ +public class SyncJobImpl extends AbstractProvisioningJob<SyncTask, SyncActions> implements SyncJob { + + /** + * Role workflow adapter. + */ + @Autowired + private RoleWorkflowAdapter rwfAdapter; + + @Autowired + protected SyncUtilities syncUtilities; + + protected void setRoleOwners(final RoleSyncResultHandler rhandler) + throws UnauthorizedRoleException, NotFoundException { + + for (Map.Entry<Long, String> entry : rhandler.getRoleOwnerMap().entrySet()) { + RoleMod roleMod = new RoleMod(); + roleMod.setKey(entry.getKey()); + + if (StringUtils.isBlank(entry.getValue())) { + roleMod.setRoleOwner(null); + roleMod.setUserOwner(null); + } else { + Long userId = syncUtilities.findMatchingAttributableKey( + ObjectClass.ACCOUNT, + entry.getValue(), + rhandler.getProfile().getTask().getResource(), + rhandler.getProfile().getConnector()); + + if (userId == null) { + Long roleId = syncUtilities.findMatchingAttributableKey( + ObjectClass.GROUP, + entry.getValue(), + rhandler.getProfile().getTask().getResource(), + rhandler.getProfile().getConnector()); + + if (roleId != null) { + roleMod.setRoleOwner(new ReferenceMod(roleId)); + } + } else { + roleMod.setUserOwner(new ReferenceMod(userId)); + } + } + + rwfAdapter.update(roleMod); + } + } + + @Override + protected String executeWithSecurityContext( + final SyncTask syncTask, + final Connector connector, + final UMapping uMapping, + final RMapping rMapping, + final boolean dryRun) throws JobExecutionException { + + LOG.debug("Execute synchronization with token {}", syncTask.getResource().getUsyncToken()); + + final ProvisioningProfile<SyncTask, SyncActions> profile = new ProvisioningProfile<>(connector, syncTask); + if (actions != null) { + profile.getActions().addAll(actions); + } + profile.setDryRun(dryRun); + profile.setResAct(getSyncPolicySpec(syncTask).getConflictResolutionAction()); + + // Prepare handler for SyncDelta objects (users) + final UserSyncResultHandler uhandler = + (UserSyncResultHandler) ApplicationContextProvider.getApplicationContext().getBeanFactory(). + createBean(UserSyncResultHandlerImpl.class, AbstractBeanDefinition.AUTOWIRE_BY_NAME, false); + uhandler.setProfile(profile); + + // Prepare handler for SyncDelta objects (roles/groups) + final RoleSyncResultHandler rhandler = + (RoleSyncResultHandler) ApplicationContextProvider.getApplicationContext().getBeanFactory(). + createBean(RoleSyncResultHandlerImpl.class, AbstractBeanDefinition.AUTOWIRE_BY_NAME, false); + rhandler.setProfile(profile); + + if (actions != null && !profile.isDryRun()) { + for (SyncActions action : actions) { + action.beforeAll(profile); + } + } + + try { + SyncToken latestUSyncToken = null; + if (uMapping != null && !syncTask.isFullReconciliation()) { + latestUSyncToken = connector.getLatestSyncToken(ObjectClass.ACCOUNT); + } + SyncToken latestRSyncToken = null; + if (rMapping != null && !syncTask.isFullReconciliation()) { + latestRSyncToken = connector.getLatestSyncToken(ObjectClass.GROUP); + } + + if (syncTask.isFullReconciliation()) { + if (uMapping != null) { + connector.getAllObjects(ObjectClass.ACCOUNT, uhandler, + connector.getOperationOptions(uMapping.getItems())); + } + if (rMapping != null) { + connector.getAllObjects(ObjectClass.GROUP, rhandler, + connector.getOperationOptions(rMapping.getItems())); + } + } else { + if (uMapping != null) { + connector.sync(ObjectClass.ACCOUNT, syncTask.getResource().getUsyncToken(), uhandler, + connector.getOperationOptions(uMapping.getItems())); + } + if (rMapping != null) { + connector.sync(ObjectClass.GROUP, syncTask.getResource().getRsyncToken(), rhandler, + connector.getOperationOptions(rMapping.getItems())); + } + } + + if (!dryRun && !syncTask.isFullReconciliation()) { + try { + ExternalResource resource = resourceDAO.find(syncTask.getResource().getKey()); + if (uMapping != null) { + resource.setUsyncToken(latestUSyncToken); + } + if (rMapping != null) { + resource.setRsyncToken(latestRSyncToken); + } + resourceDAO.save(resource); + } catch (Exception e) { + throw new JobExecutionException("While updating SyncToken", e); + } + } + } catch (Throwable t) { + throw new JobExecutionException("While syncing on connector", t); + } + + try { + setRoleOwners(rhandler); + } catch (Exception e) { + LOG.error("While setting role owners", e); + } + + if (actions != null && !profile.isDryRun()) { + for (SyncActions action : actions) { + action.afterAll(profile); + } + } + + final String result = createReport(profile.getResults(), syncTask.getResource().getSyncTraceLevel(), dryRun); + + LOG.debug("Sync result: {}", result); + + return result; + } + + private SyncPolicySpec getSyncPolicySpec(final ProvisioningTask task) { + SyncPolicySpec syncPolicySpec; + + if (task instanceof SyncTask) { + final SyncPolicy syncPolicy = task.getResource().getSyncPolicy() == null + ? policyDAO.getGlobalSyncPolicy() + : task.getResource().getSyncPolicy(); + + syncPolicySpec = syncPolicy == null ? null : syncPolicy.getSpecification(SyncPolicySpec.class); + } else { + syncPolicySpec = null; + } + + // step required because the call <policy>.getSpecification() could return a null value + return syncPolicySpec == null ? new SyncPolicySpec() : syncPolicySpec; + } +}
http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/SyncUtilities.java ---------------------------------------------------------------------- diff --git a/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/SyncUtilities.java b/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/SyncUtilities.java new file mode 100644 index 0000000..90f8a13 --- /dev/null +++ b/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/SyncUtilities.java @@ -0,0 +1,414 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.syncope.core.provisioning.java.sync; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.apache.commons.lang3.StringUtils; +import org.apache.syncope.common.lib.types.AttributableType; +import org.apache.syncope.common.lib.types.MappingPurpose; +import org.apache.syncope.common.lib.types.SubjectType; +import org.apache.syncope.common.lib.types.SyncPolicySpec; +import org.apache.syncope.core.persistence.api.RoleEntitlementUtil; +import org.apache.syncope.core.persistence.api.attrvalue.validation.ParsingValidationException; +import org.apache.syncope.core.persistence.api.dao.EntitlementDAO; +import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO; +import org.apache.syncope.core.persistence.api.dao.PolicyDAO; +import org.apache.syncope.core.persistence.api.dao.RoleDAO; +import org.apache.syncope.core.persistence.api.dao.SubjectSearchDAO; +import org.apache.syncope.core.persistence.api.dao.UserDAO; +import org.apache.syncope.core.persistence.api.dao.search.AttributeCond; +import org.apache.syncope.core.persistence.api.dao.search.OrderByClause; +import org.apache.syncope.core.persistence.api.dao.search.SearchCond; +import org.apache.syncope.core.persistence.api.dao.search.SubjectCond; +import org.apache.syncope.core.persistence.api.entity.AttributableUtil; +import org.apache.syncope.core.persistence.api.entity.AttributableUtilFactory; +import org.apache.syncope.core.persistence.api.entity.ExternalResource; +import org.apache.syncope.core.persistence.api.entity.MappingItem; +import org.apache.syncope.core.persistence.api.entity.PlainAttrValue; +import org.apache.syncope.core.persistence.api.entity.PlainSchema; +import org.apache.syncope.core.persistence.api.entity.Subject; +import org.apache.syncope.core.persistence.api.entity.SyncPolicy; +import org.apache.syncope.core.persistence.api.entity.role.Role; +import org.apache.syncope.core.persistence.api.entity.task.ProvisioningTask; +import org.apache.syncope.core.persistence.api.entity.user.UDerAttr; +import org.apache.syncope.core.persistence.api.entity.user.UPlainAttr; +import org.apache.syncope.core.persistence.api.entity.user.UVirAttr; +import org.apache.syncope.core.persistence.api.entity.user.User; +import org.apache.syncope.core.provisioning.api.Connector; +import org.apache.syncope.core.provisioning.api.sync.SyncCorrelationRule; +import org.identityconnectors.framework.common.objects.Attribute; +import org.identityconnectors.framework.common.objects.AttributeUtil; +import org.identityconnectors.framework.common.objects.ConnectorObject; +import org.identityconnectors.framework.common.objects.Name; +import org.identityconnectors.framework.common.objects.ObjectClass; +import org.identityconnectors.framework.common.objects.OperationalAttributes; +import org.identityconnectors.framework.common.objects.filter.EqualsFilter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class SyncUtilities { + + /** + * Logger. + */ + protected static final Logger LOG = LoggerFactory.getLogger(SyncUtilities.class); + + /** + * Policy DAO. + */ + @Autowired + protected PolicyDAO policyDAO; + + /** + * Entitlement DAO. + */ + @Autowired + protected EntitlementDAO entitlementDAO; + + /** + * Schema DAO. + */ + @Autowired + protected PlainSchemaDAO plainSchemaDAO; + + /** + * User DAO. + */ + @Autowired + protected UserDAO userDAO; + + /** + * Role DAO. + */ + @Autowired + protected RoleDAO roleDAO; + + /** + * Search DAO. + */ + @Autowired + protected SubjectSearchDAO searchDAO; + + @Autowired + protected AttributableUtilFactory attrUtilFactory; + + public Long findMatchingAttributableKey( + final ObjectClass oclass, + final String name, + final ExternalResource resource, + final Connector connector) { + + Long result = null; + + final AttributableUtil attrUtil = attrUtilFactory.getInstance(oclass); + + final List<ConnectorObject> found = connector.search(oclass, + new EqualsFilter(new Name(name)), connector.getOperationOptions( + attrUtil.getMappingItems(resource, MappingPurpose.SYNCHRONIZATION))); + + if (found.isEmpty()) { + LOG.debug("No {} found on {} with __NAME__ {}", oclass, resource, name); + } else { + if (found.size() > 1) { + LOG.warn("More than one {} found on {} with __NAME__ {} - taking first only", oclass, resource, name); + } + + ConnectorObject connObj = found.iterator().next(); + try { + List<Long> subjectKeys = findExisting(connObj.getUid().getUidValue(), connObj, resource, attrUtil); + if (subjectKeys.isEmpty()) { + LOG.debug("No matching {} found for {}, aborting", attrUtil.getType(), connObj); + } else { + if (subjectKeys.size() > 1) { + LOG.warn("More than one {} found {} - taking first only", attrUtil.getType(), subjectKeys); + } + + result = subjectKeys.iterator().next(); + } + } catch (IllegalArgumentException e) { + LOG.warn(e.getMessage()); + } + } + + return result; + } + + private List<Long> findByAccountIdItem( + final String uid, final ExternalResource resource, final AttributableUtil attrUtil) { + final List<Long> result = new ArrayList<>(); + + final MappingItem accountIdItem = attrUtil.getAccountIdItem(resource); + switch (accountIdItem.getIntMappingType()) { + case UserPlainSchema: + case RolePlainSchema: + final PlainAttrValue value = attrUtil.newPlainAttrValue(); + + PlainSchema schema = plainSchemaDAO.find(accountIdItem.getIntAttrName(), attrUtil.plainSchemaClass()); + if (schema == null) { + value.setStringValue(uid); + } else { + try { + value.parseValue(schema, uid); + } catch (ParsingValidationException e) { + LOG.error("While parsing provided __UID__ {}", uid, e); + value.setStringValue(uid); + } + } + + List<? extends Subject<UPlainAttr, UDerAttr, UVirAttr>> users = + userDAO.findByAttrValue(accountIdItem.getIntAttrName(), value, attrUtil); + for (Subject<UPlainAttr, UDerAttr, UVirAttr> subject : users) { + result.add(subject.getKey()); + } + break; + + case UserDerivedSchema: + case RoleDerivedSchema: + users = userDAO.findByDerAttrValue(accountIdItem.getIntAttrName(), uid, attrUtil); + for (Subject<UPlainAttr, UDerAttr, UVirAttr> subject : users) { + result.add(subject.getKey()); + } + break; + + case Username: + User user = userDAO.find(uid); + if (user != null) { + result.add(user.getKey()); + } + break; + + case UserId: + user = userDAO.find(Long.parseLong(uid)); + if (user != null) { + result.add(user.getKey()); + } + break; + + case RoleName: + List<Role> roles = roleDAO.find(uid); + for (Role role : roles) { + result.add(role.getKey()); + } + break; + + case RoleId: + Role role = roleDAO.find(Long.parseLong(uid)); + if (role != null) { + result.add(role.getKey()); + } + break; + + default: + LOG.error("Invalid accountId type '{}'", accountIdItem.getIntMappingType()); + } + + return result; + } + + private List<Long> search(final SearchCond searchCond, final SubjectType type) { + final List<Long> result = new ArrayList<>(); + + List<Subject<?, ?, ?>> subjects = searchDAO.search( + RoleEntitlementUtil.getRoleKeys(entitlementDAO.findAll()), + searchCond, Collections.<OrderByClause>emptyList(), type); + for (Subject<?, ?, ?> subject : subjects) { + result.add(subject.getKey()); + } + + return result; + } + + private List<Long> findByCorrelationRule( + final ConnectorObject connObj, final SyncCorrelationRule rule, final SubjectType type) { + + return search(rule.getSearchCond(connObj), type); + } + + private List<Long> findByAttributableSearch( + final ConnectorObject connObj, + final List<String> altSearchSchemas, + final ExternalResource resource, + final AttributableUtil attrUtil) { + + // search for external attribute's name/value of each specified name + final Map<String, Attribute> extValues = new HashMap<>(); + + for (MappingItem item : attrUtil.getMappingItems(resource, MappingPurpose.SYNCHRONIZATION)) { + extValues.put(item.getIntAttrName(), connObj.getAttributeByName(item.getExtAttrName())); + } + + // search for user/role by attribute(s) specified in the policy + SearchCond searchCond = null; + + for (String schema : altSearchSchemas) { + Attribute value = extValues.get(schema); + + if (value == null) { + throw new IllegalArgumentException( + "Connector object does not contains the attributes to perform the search: " + schema); + } + + AttributeCond.Type type; + String expression = null; + + if (value.getValue() == null || value.getValue().isEmpty() + || (value.getValue().size() == 1 && value.getValue().get(0) == null)) { + + type = AttributeCond.Type.ISNULL; + } else { + type = AttributeCond.Type.EQ; + expression = value.getValue().size() > 1 + ? value.getValue().toString() + : value.getValue().get(0).toString(); + } + + SearchCond nodeCond; + // users: just id or username can be selected to be used + // roles: just id or name can be selected to be used + if ("key".equalsIgnoreCase(schema) + || "username".equalsIgnoreCase(schema) || "name".equalsIgnoreCase(schema)) { + + SubjectCond cond = new SubjectCond(); + cond.setSchema(schema); + cond.setType(type); + cond.setExpression(expression); + + nodeCond = SearchCond.getLeafCond(cond); + } else { + AttributeCond cond = new AttributeCond(); + cond.setSchema(schema); + cond.setType(type); + cond.setExpression(expression); + + nodeCond = SearchCond.getLeafCond(cond); + } + + searchCond = searchCond == null + ? nodeCond + : SearchCond.getAndCond(searchCond, nodeCond); + } + + return search(searchCond, SubjectType.valueOf(attrUtil.getType().name())); + } + + private SyncCorrelationRule getCorrelationRule(final AttributableType type, final SyncPolicySpec policySpec) { + String clazz; + + switch (type) { + case USER: + clazz = policySpec.getUserJavaRule(); + break; + case ROLE: + clazz = policySpec.getRoleJavaRule(); + break; + case MEMBERSHIP: + case CONFIGURATION: + default: + clazz = null; + } + + SyncCorrelationRule res = null; + + if (StringUtils.isNotBlank(clazz)) { + try { + res = (SyncCorrelationRule) Class.forName(clazz).newInstance(); + } catch (Exception e) { + LOG.error("Failure instantiating correlation rule class '{}'", clazz, e); + } + } + + return res; + } + + private List<String> getAltSearchSchemas(final AttributableType type, final SyncPolicySpec policySpec) { + List<String> result = Collections.emptyList(); + + switch (type) { + case USER: + result = policySpec.getuAltSearchSchemas(); + break; + case ROLE: + result = policySpec.getrAltSearchSchemas(); + break; + case MEMBERSHIP: + case CONFIGURATION: + default: + } + + return result; + } + + /** + * Find users / roles based on mapped uid value (or previous uid value, if updated). + * + * @param uid for finding by account id + * @param connObj for finding by attribute value + * @param resource external resource + * @param attrUtil attributable util + * @return list of matching users / roles + */ + public List<Long> findExisting( + final String uid, + final ConnectorObject connObj, + final ExternalResource resource, + final AttributableUtil attrUtil) { + + SyncPolicySpec syncPolicySpec = null; + if (resource.getSyncPolicy() == null) { + SyncPolicy globalSP = policyDAO.getGlobalSyncPolicy(); + if (globalSP != null) { + syncPolicySpec = globalSP.getSpecification(SyncPolicySpec.class); + } + } else { + syncPolicySpec = resource.getSyncPolicy().getSpecification(SyncPolicySpec.class); + } + + SyncCorrelationRule syncRule = null; + List<String> altSearchSchemas = null; + + if (syncPolicySpec != null) { + syncRule = getCorrelationRule(attrUtil.getType(), syncPolicySpec); + altSearchSchemas = getAltSearchSchemas(attrUtil.getType(), syncPolicySpec); + } + + return syncRule == null ? altSearchSchemas == null || altSearchSchemas.isEmpty() + ? findByAccountIdItem(uid, resource, attrUtil) + : findByAttributableSearch(connObj, altSearchSchemas, resource, attrUtil) + : findByCorrelationRule(connObj, syncRule, SubjectType.valueOf(attrUtil.getType().name())); + } + + public Boolean readEnabled(final ConnectorObject connectorObject, final ProvisioningTask task) { + Boolean enabled = null; + if (task.isSyncStatus()) { + Attribute status = AttributeUtil.find(OperationalAttributes.ENABLE_NAME, connectorObject.getAttributes()); + if (status != null && status.getValue() != null && !status.getValue().isEmpty()) { + enabled = (Boolean) status.getValue().get(0); + } + } + + return enabled; + } +} http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/UserPushResultHandlerImpl.java ---------------------------------------------------------------------- diff --git a/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/UserPushResultHandlerImpl.java b/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/UserPushResultHandlerImpl.java new file mode 100644 index 0000000..0c1ad8b --- /dev/null +++ b/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/UserPushResultHandlerImpl.java @@ -0,0 +1,160 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.syncope.core.provisioning.java.sync; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import org.apache.syncope.common.lib.mod.UserMod; +import org.apache.syncope.common.lib.to.AbstractSubjectTO; +import org.apache.syncope.common.lib.to.UserTO; +import org.apache.syncope.common.lib.types.PropagationByResource; +import org.apache.syncope.common.lib.types.ResourceOperation; +import org.apache.syncope.core.persistence.api.entity.Mapping; +import org.apache.syncope.core.persistence.api.entity.MappingItem; +import org.apache.syncope.core.persistence.api.entity.Subject; +import org.apache.syncope.core.persistence.api.entity.user.User; +import org.apache.syncope.core.provisioning.api.TimeoutException; +import org.apache.syncope.core.provisioning.api.sync.UserPushResultHandler; +import org.identityconnectors.framework.common.objects.ConnectorObject; +import org.identityconnectors.framework.common.objects.ObjectClass; +import org.identityconnectors.framework.common.objects.Uid; + +public class UserPushResultHandlerImpl extends AbstractPushResultHandler implements UserPushResultHandler { + + @Override + protected Subject<?, ?, ?> deprovision(final Subject<?, ?, ?> sbj) { + final UserTO before = userTransfer.getUserTO(sbj.getKey()); + + final List<String> noPropResources = new ArrayList<>(before.getResources()); + noPropResources.remove(profile.getTask().getResource().getKey()); + + taskExecutor.execute(propagationManager.getUserDeleteTaskIds(before.getKey(), + Collections.singleton(profile.getTask().getResource().getKey()), noPropResources)); + + return userDAO.authFetch(before.getKey()); + } + + @Override + protected Subject<?, ?, ?> provision(final Subject<?, ?, ?> sbj, final Boolean enabled) { + final UserTO before = userTransfer.getUserTO(sbj.getKey()); + + final List<String> noPropResources = new ArrayList<>(before.getResources()); + noPropResources.remove(profile.getTask().getResource().getKey()); + + final PropagationByResource propByRes = new PropagationByResource(); + propByRes.add(ResourceOperation.CREATE, profile.getTask().getResource().getKey()); + + taskExecutor.execute(propagationManager.getUserCreateTaskIds( + before.getKey(), + enabled, + propByRes, + null, + Collections.unmodifiableCollection(before.getVirAttrs()), + Collections.unmodifiableCollection(before.getMemberships()), + noPropResources)); + + return userDAO.authFetch(before.getKey()); + } + + @Override + protected Subject<?, ?, ?> link(final Subject<?, ?, ?> sbj, final Boolean unlink) { + final UserMod userMod = new UserMod(); + userMod.setKey(sbj.getKey()); + + if (unlink) { + userMod.getResourcesToRemove().add(profile.getTask().getResource().getKey()); + } else { + userMod.getResourcesToAdd().add(profile.getTask().getResource().getKey()); + } + + uwfAdapter.update(userMod); + + return userDAO.authFetch(userMod.getKey()); + } + + @Override + protected Subject<?, ?, ?> unassign(final Subject<?, ?, ?> sbj) { + final UserMod userMod = new UserMod(); + userMod.setKey(sbj.getKey()); + userMod.getResourcesToRemove().add(profile.getTask().getResource().getKey()); + uwfAdapter.update(userMod); + return deprovision(sbj); + } + + @Override + protected Subject<?, ?, ?> assign(final Subject<?, ?, ?> sbj, final Boolean enabled) { + final UserMod userMod = new UserMod(); + userMod.setKey(sbj.getKey()); + userMod.getResourcesToAdd().add(profile.getTask().getResource().getKey()); + uwfAdapter.update(userMod); + return provision(sbj, enabled); + } + + @Override + protected String getName(final Subject<?, ?, ?> subject) { + return User.class.cast(subject).getUsername(); + } + + @Override + protected AbstractSubjectTO getSubjectTO(final long key) { + try { + return userTransfer.getUserTO(key); + } catch (Exception e) { + LOG.warn("Error retrieving user {}", key, e); + return null; + } + } + + @Override + protected Subject<?, ?, ?> getSubject(final long key) { + try { + return userDAO.authFetch(key); + } catch (Exception e) { + LOG.warn("Error retrieving user {}", key, e); + return null; + } + } + + @Override + protected ConnectorObject getRemoteObject(final String accountId) { + ConnectorObject obj = null; + + try { + final Uid uid = new Uid(accountId); + + obj = profile.getConnector().getObject( + ObjectClass.ACCOUNT, + uid, + profile.getConnector().getOperationOptions(Collections.<MappingItem>emptySet())); + + } catch (TimeoutException toe) { + LOG.debug("Request timeout", toe); + throw toe; + } catch (RuntimeException ignore) { + LOG.debug("While resolving {}", accountId, ignore); + } + return obj; + } + + @Override + protected Mapping<?> getMapping() { + return profile.getTask().getResource().getUmapping(); + } +} http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/UserSyncResultHandlerImpl.java ---------------------------------------------------------------------- diff --git a/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/UserSyncResultHandlerImpl.java b/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/UserSyncResultHandlerImpl.java new file mode 100644 index 0000000..0f0d785 --- /dev/null +++ b/syncope620/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/UserSyncResultHandlerImpl.java @@ -0,0 +1,148 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.syncope.core.provisioning.java.sync; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import org.apache.syncope.common.lib.mod.AbstractSubjectMod; +import org.apache.syncope.common.lib.mod.UserMod; +import org.apache.syncope.common.lib.to.AbstractSubjectTO; +import org.apache.syncope.common.lib.to.PropagationStatus; +import org.apache.syncope.common.lib.to.UserTO; +import org.apache.syncope.common.lib.types.AttributableType; +import org.apache.syncope.core.persistence.api.entity.AttributableUtil; +import org.apache.syncope.core.provisioning.api.sync.ProvisioningResult; +import org.apache.syncope.core.provisioning.api.sync.UserSyncResultHandler; +import org.identityconnectors.framework.common.objects.SyncDelta; + +public class UserSyncResultHandlerImpl extends AbstractSyncResultHandler implements UserSyncResultHandler { + + @Override + protected AttributableUtil getAttributableUtil() { + return attrUtilFactory.getInstance(AttributableType.USER); + } + + @Override + protected String getName(final AbstractSubjectTO subjectTO) { + return UserTO.class.cast(subjectTO).getUsername(); + } + + @Override + protected AbstractSubjectTO getSubjectTO(final long key) { + try { + return userTransfer.getUserTO(key); + } catch (Exception e) { + LOG.warn("Error retrieving user {}", key, e); + return null; + } + } + + @Override + protected AbstractSubjectMod getSubjectMod( + final AbstractSubjectTO subjectTO, final SyncDelta delta) { + + return connObjectUtil.getAttributableMod( + subjectTO.getKey(), + delta.getObject(), + subjectTO, + profile.getTask(), + getAttributableUtil()); + } + + @Override + protected AbstractSubjectTO create( + final AbstractSubjectTO subjectTO, final SyncDelta delta, final ProvisioningResult result) { + + UserTO userTO = UserTO.class.cast(subjectTO); + + Boolean enabled = syncUtilities.readEnabled(delta.getObject(), profile.getTask()); + Map.Entry<Long, List<PropagationStatus>> created = userProvisioningManager.create(userTO, true, true, enabled, + Collections.singleton(profile.getTask().getResource().getKey())); + + userTO = userTransfer.getUserTO(created.getKey()); + + result.setId(created.getKey()); + + return userTO; + } + + @Override + protected AbstractSubjectTO link( + final AbstractSubjectTO before, + final ProvisioningResult result, + final boolean unlink) { + + final UserMod userMod = new UserMod(); + userMod.setKey(before.getKey()); + + if (unlink) { + userMod.getResourcesToRemove().add(profile.getTask().getResource().getKey()); + } else { + userMod.getResourcesToAdd().add(profile.getTask().getResource().getKey()); + } + + return userTransfer.getUserTO(uwfAdapter.update(userMod).getResult().getKey().getKey()); + } + + @Override + protected AbstractSubjectTO update( + final AbstractSubjectTO before, + final AbstractSubjectMod subjectMod, + final SyncDelta delta, + final ProvisioningResult result) { + + final UserMod userMod = UserMod.class.cast(subjectMod); + final Boolean enabled = syncUtilities.readEnabled(delta.getObject(), profile.getTask()); + + Map.Entry<Long, List<PropagationStatus>> updated = userProvisioningManager.update(userMod, before.getKey(), + result, enabled, Collections.singleton(profile.getTask().getResource().getKey())); + + return userTransfer.getUserTO(updated.getKey()); + } + + @Override + protected void deprovision( + final Long key, + final boolean unlink) { + + taskExecutor.execute( + propagationManager.getUserDeleteTaskIds(key, profile.getTask().getResource().getKey())); + + if (unlink) { + final UserMod userMod = new UserMod(); + userMod.setKey(key); + userMod.getResourcesToRemove().add(profile.getTask().getResource().getKey()); + } + } + + @Override + protected void delete(final Long key) { + try { + userProvisioningManager. + delete(key, Collections.<String>singleton(profile.getTask().getResource().getKey())); + } catch (Exception e) { + // A propagation failure doesn't imply a synchronization failure. + // The propagation exception status will be reported into the propagation task execution. + LOG.error("Could not propagate user " + key, e); + } + + uwfAdapter.delete(key); + } +} http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/provisioning-java/src/main/resources/connid.properties ---------------------------------------------------------------------- diff --git a/syncope620/core/provisioning-java/src/main/resources/connid.properties b/syncope620/core/provisioning-java/src/main/resources/connid.properties new file mode 100644 index 0000000..24d5c93 --- /dev/null +++ b/syncope620/core/provisioning-java/src/main/resources/connid.properties @@ -0,0 +1,17 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. +connid.locations=${connid.location} http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/provisioning-java/src/main/resources/mail.properties ---------------------------------------------------------------------- diff --git a/syncope620/core/provisioning-java/src/main/resources/mail.properties b/syncope620/core/provisioning-java/src/main/resources/mail.properties new file mode 100644 index 0000000..12f04e7 --- /dev/null +++ b/syncope620/core/provisioning-java/src/main/resources/mail.properties @@ -0,0 +1,25 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. +templates.directory=${conf.directory} +smtpHost=none.syncope.apache.org +smtpPort=25 +smtpUser= +smtpPassword= +smtpProtocol=smtp +smtpEncoding=UTF-8 +smtpConnectionTimeout=3000 +mailDebug=false http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/provisioning-java/src/main/resources/mailTemplates/confirmPasswordReset.html.vm ---------------------------------------------------------------------- diff --git a/syncope620/core/provisioning-java/src/main/resources/mailTemplates/confirmPasswordReset.html.vm b/syncope620/core/provisioning-java/src/main/resources/mailTemplates/confirmPasswordReset.html.vm new file mode 100644 index 0000000..90630ac --- /dev/null +++ b/syncope620/core/provisioning-java/src/main/resources/mailTemplates/confirmPasswordReset.html.vm @@ -0,0 +1,26 @@ +<!-- +Licensed to the Apache Software Foundation (ASF) under one +or more contributor license agreements. See the NOTICE file +distributed with this work for additional information +regarding copyright ownership. The ASF licenses this file +to you 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. +--> +<html> +<body> +<p>Hi,</br> +we are happy to inform you that the password request was execute successfully for your account.</p> + +<p>Best regards.</p> +</body> +</html> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/provisioning-java/src/main/resources/mailTemplates/confirmPasswordReset.txt.vm ---------------------------------------------------------------------- diff --git a/syncope620/core/provisioning-java/src/main/resources/mailTemplates/confirmPasswordReset.txt.vm b/syncope620/core/provisioning-java/src/main/resources/mailTemplates/confirmPasswordReset.txt.vm new file mode 100644 index 0000000..33f75dc --- /dev/null +++ b/syncope620/core/provisioning-java/src/main/resources/mailTemplates/confirmPasswordReset.txt.vm @@ -0,0 +1,20 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. +Hi, +we are happy to inform you that the password request was execute successfully for your account. + +Best regards. \ No newline at end of file http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/provisioning-java/src/main/resources/mailTemplates/optin.html.vm ---------------------------------------------------------------------- diff --git a/syncope620/core/provisioning-java/src/main/resources/mailTemplates/optin.html.vm b/syncope620/core/provisioning-java/src/main/resources/mailTemplates/optin.html.vm new file mode 100644 index 0000000..8240c7b --- /dev/null +++ b/syncope620/core/provisioning-java/src/main/resources/mailTemplates/optin.html.vm @@ -0,0 +1,72 @@ +<!-- +Licensed to the Apache Software Foundation (ASF) under one +or more contributor license agreements. See the NOTICE file +distributed with this work for additional information +regarding copyright ownership. The ASF licenses this file +to you 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. +--> +<html> +<body> +<h3>Hi $user.getPlainAttrMap().get("firstname").getValues().get(0) $user.getPlainAttrMap().get("surname").getValues().get(0), welcome to Syncope!</h3> + +<p> + Your username is $user.getUsername().<br/> + Your email address is $user.getPlainAttrMap().get("email").getValues().get(0). + Your email address inside a <a href="http://localhost/?email=$esc.url($user.getPlainAttrMap().get("email").getValues().get(0))">link</a>. +</p> + +<p> + This message was sent to the following recipients: +<ul> +#foreach($recipient in $recipients) + <li>$recipient.getPlainAttrMap().get("email").getValues().get(0)</li> +#end +</ul> + +because one of the following events occurred: +<ul> +#foreach($event in $events) + <li>$event</i> +#end +</ul> +</p> + +#if(!$user.getMemberships().isEmpty()) +You have been provided with the following roles: +<ul> +#foreach($membership in $user.getMemberships()) + <li>$membership.roleName</i> +#end +</ul> +#end + +#if(${output.class.simpleName} == "TaskExec") +Below you can read execution details of task $output.getTask().getClass().getSimpleName(), id $output.getId(). +Task Details: +<ul> +<li> +START DATE: $output.getStartDate() +</li> +<li> +MESSAGE:<br/> +$output.getMessage() +</li> +<li> +END DATE: $output.getEndDate() +</li> +</ul> +#end + +</body> +</html> http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/provisioning-java/src/main/resources/mailTemplates/optin.txt.vm ---------------------------------------------------------------------- diff --git a/syncope620/core/provisioning-java/src/main/resources/mailTemplates/optin.txt.vm b/syncope620/core/provisioning-java/src/main/resources/mailTemplates/optin.txt.vm new file mode 100644 index 0000000..fc8e398 --- /dev/null +++ b/syncope620/core/provisioning-java/src/main/resources/mailTemplates/optin.txt.vm @@ -0,0 +1,51 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. +Hi $user.getPlainAttrMap().get("firstname").getValues().get(0) $user.getPlainAttrMap().get("surname").getValues().get(0), welcome to Syncope! + +Your username is $user.getUsername(). +Your email address is $user.getPlainAttrMap().get("email").getValues().get(0). +Your email address inside a link: http://localhost/?email=$esc.url($user.getPlainAttrMap().get("email").getValues().get(0)) . + +This message was sent to the following recipients: +#foreach($recipient in $recipients) + * $recipient.getPlainAttrMap().get("surname").getValues().get(0) +#end + +because one of the following events occurred: +#foreach($event in $events) + * $event +#end + +#if(!$user.getMemberships().isEmpty()) +You have been provided with the following roles: +#foreach($membership in $user.getMemberships()) + * $membership.roleName +#end +#end + +#if(${output.class.simpleName} == "TaskExec") +Below you can read execution details of task $output.getTask().getClass().getSimpleName(), id $output.getId() + +Task Details: + + * START DATE: $output.getStartDate() + + * MESSAGE: +$output.getMessage() + + * END DATE: $output.getEndDate() +#end http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/provisioning-java/src/main/resources/mailTemplates/requestPasswordReset.html.vm ---------------------------------------------------------------------- diff --git a/syncope620/core/provisioning-java/src/main/resources/mailTemplates/requestPasswordReset.html.vm b/syncope620/core/provisioning-java/src/main/resources/mailTemplates/requestPasswordReset.html.vm new file mode 100644 index 0000000..6594c3f --- /dev/null +++ b/syncope620/core/provisioning-java/src/main/resources/mailTemplates/requestPasswordReset.html.vm @@ -0,0 +1,31 @@ +<!-- +Licensed to the Apache Software Foundation (ASF) under one +or more contributor license agreements. See the NOTICE file +distributed with this work for additional information +regarding copyright ownership. The ASF licenses this file +to you 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. +--> +<html> +<body> +<p>Hi, +a password reset was request for $user.getUsername().</p> + +<p>In order to complete this request, you need to visit this +<a href="http://localhost:9080/syncope-console/?pwdResetToken=$input.get(0)">link</a></p>. + +<p>If you did not request this reset, just ignore the present e-mail.</p> + +<p>Best regards.</p> +</body> +</html> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/provisioning-java/src/main/resources/mailTemplates/requestPasswordReset.txt.vm ---------------------------------------------------------------------- diff --git a/syncope620/core/provisioning-java/src/main/resources/mailTemplates/requestPasswordReset.txt.vm b/syncope620/core/provisioning-java/src/main/resources/mailTemplates/requestPasswordReset.txt.vm new file mode 100644 index 0000000..5ac028a --- /dev/null +++ b/syncope620/core/provisioning-java/src/main/resources/mailTemplates/requestPasswordReset.txt.vm @@ -0,0 +1,26 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. +Hi, +a password reset was request for $user.getUsername(). + +In order to complete this request, you need to visit this link: + +http://localhost:9080/syncope-console/?pwdResetToken=$input.get(0) + +If you did not request this reset, just ignore the present e-mail. + +Best regards. \ No newline at end of file http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/provisioning-java/src/main/resources/provisioning.properties ---------------------------------------------------------------------- diff --git a/syncope620/core/provisioning-java/src/main/resources/provisioning.properties b/syncope620/core/provisioning-java/src/main/resources/provisioning.properties new file mode 100644 index 0000000..af5deee --- /dev/null +++ b/syncope620/core/provisioning-java/src/main/resources/provisioning.properties @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. +userProvisioningManager=org.apache.syncope.core.provisioning.java.DefaultUserProvisioningManager +roleProvisioningManager=org.apache.syncope.core.provisioning.java.DefaultRoleProvisioningManager http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/provisioning-java/src/main/resources/provisioningContext.xml ---------------------------------------------------------------------- diff --git a/syncope620/core/provisioning-java/src/main/resources/provisioningContext.xml b/syncope620/core/provisioning-java/src/main/resources/provisioningContext.xml new file mode 100644 index 0000000..fa31a73 --- /dev/null +++ b/syncope620/core/provisioning-java/src/main/resources/provisioningContext.xml @@ -0,0 +1,110 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +Licensed to the Apache Software Foundation (ASF) under one +or more contributor license agreements. See the NOTICE file +distributed with this work for additional information +regarding copyright ownership. The ASF licenses this file +to you 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. +--> +<beans xmlns="http://www.springframework.org/schema/beans" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:context="http://www.springframework.org/schema/context" + xmlns:task="http://www.springframework.org/schema/task" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans.xsd + http://www.springframework.org/schema/context + http://www.springframework.org/schema/context/spring-context.xsd + http://www.springframework.org/schema/task + http://www.springframework.org/schema/task/spring-task.xsd"> + + <task:annotation-driven executor="connectorExecutor"/> + <task:executor id="connectorExecutor" pool-size="10"/> + + <bean class="${userProvisioningManager}"/> + <bean class="${roleProvisioningManager}"/> + + <bean id="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean" + lazy-init="false" depends-on="nonJPAdbInitializer"> + <property name="autoStartup" value="true"/> + <property name="applicationContextSchedulerContextKey" value="applicationContext"/> + <property name="waitForJobsToCompleteOnShutdown" value="true"/> + <property name="overwriteExistingJobs" value="true"/> + <property name="dataSource" ref="dataSource"/> + <property name="transactionManager" ref="transactionManager"/> + <property name="jobFactory"> + <bean class="org.apache.syncope.core.provisioning.java.job.SpringBeanJobFactory"/> + </property> + <property name="quartzProperties"> + <props> + <prop key="org.quartz.scheduler.idleWaitTime">${quartz.scheduler.idleWaitTime:30000}</prop> + + <prop key="org.quartz.jobStore.misfireThreshold">6000000</prop> + <prop key="org.quartz.jobStore.driverDelegateClass">${quartz.jobstore}</prop> + + <prop key="org.quartz.jobStore.isClustered">true</prop> + <prop key="org.quartz.jobStore.clusterCheckinInterval">20000</prop> + + <prop key="org.quartz.scheduler.instanceName">ClusteredScheduler</prop> + <prop key="org.quartz.scheduler.instanceId">AUTO</prop> + <prop key="org.quartz.scheduler.jmx.export">true</prop> + </props> + </property> + </bean> + + <bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl"> + <property name="defaultEncoding" value="${smtpEncoding}"/> + <property name="host" value="${smtpHost}"/> + <property name="port" value="${smtpPort}"/> + <property name="username" value="${smtpUser}"/> + <property name="password" value="${smtpPassword}"/> + <property name="protocol" value="${smtpProtocol}"/> + + <property name="javaMailProperties"> + <props> + <prop key="mail.smtp.connectiontimeout">${smtpConnectionTimeout}</prop> + <prop key="mail.debug">${mailDebug}</prop> + </props> + </property> + </bean> + + <bean class="org.apache.syncope.core.provisioning.java.propagation.PropagationManagerImpl"/> + <bean class="org.apache.syncope.core.provisioning.java.propagation.DefaultPropagationReporter" scope="prototype"/> + + <context:component-scan base-package="org.apache.syncope.core.misc"/> + <context:component-scan base-package="org.apache.syncope.core.provisioning.java"/> + + <bean id="virAttrCache" class="org.apache.syncope.core.provisioning.java.cache.MemoryVirAttrCache" scope="singleton"> + <constructor-arg value="60"/> + <constructor-arg value="5000"/> + </bean> + + <bean id="velocityResourceLoader" class="org.apache.syncope.core.misc.spring.ResourceWithFallbackLoader"> + <property name="primary" value="file:${templates.directory}/"/> + <property name="fallback" value="classpath:"/> + </bean> + <bean id="velocityEngine" class="org.apache.syncope.core.provisioning.java.notification.VelocityEngineFactoryBean"> + <property name="resourceLoader" ref="velocityResourceLoader"/> + </bean> + <bean id="velocityToolManager" class="org.apache.velocity.tools.ToolManager"> + <!-- autoConfigure --> + <constructor-arg index="0" value="true"/> + <!-- include default velocity tools --> + <constructor-arg index="1" value="true"/> + </bean> + + <bean id="connIdBundleManager" class="org.apache.syncope.core.provisioning.java.ConnIdBundleManagerImpl" scope="singleton"> + <property name="stringLocations" value="${connid.locations}"/> + </bean> + +</beans> http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/AbstractTest.java ---------------------------------------------------------------------- diff --git a/syncope620/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/AbstractTest.java b/syncope620/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/AbstractTest.java new file mode 100644 index 0000000..e26f238 --- /dev/null +++ b/syncope620/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/AbstractTest.java @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.syncope.core.provisioning.java; + +import org.junit.runner.RunWith; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(locations = { + "classpath:persistenceTest.xml", + "classpath:provisioningContext.xml", + "classpath:workflowContext.xml", + "classpath:provisioningTest.xml" +}) +public abstract class AbstractTest { +} http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/ConnectorManagerTest.java ---------------------------------------------------------------------- diff --git a/syncope620/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/ConnectorManagerTest.java b/syncope620/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/ConnectorManagerTest.java new file mode 100644 index 0000000..9ddc91d --- /dev/null +++ b/syncope620/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/ConnectorManagerTest.java @@ -0,0 +1,73 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.syncope.core.provisioning.java; + +import org.apache.syncope.core.provisioning.java.ConnectorManager; + +import static org.junit.Assert.assertEquals; + +import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO; +import org.apache.syncope.core.persistence.api.entity.ExternalResource; +import org.apache.syncope.core.provisioning.api.ConnIdBundleManager; +import org.apache.syncope.core.provisioning.api.Connector; +import org.apache.syncope.core.misc.spring.ApplicationContextProvider; +import org.junit.Before; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.util.ReflectionTestUtils; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +public class ConnectorManagerTest extends AbstractTest { + + private ConnectorManager connManager; + + @Autowired + private ConnIdBundleManager connIdBundleManager; + + @Autowired + private ExternalResourceDAO resourceDAO; + + @Before + public void before() { + connManager = new ConnectorManager(); + ReflectionTestUtils.setField(connManager, "connIdBundleManager", connIdBundleManager); + ReflectionTestUtils.setField(connManager, "resourceDAO", resourceDAO); + + // Remove any other connector instance bean set up by standard ConnectorManager.load() + connManager.unload(); + } + + @Test + public void load() { + connManager.load(); + + // only consider local connector bundles + int expected = 0; + for (ExternalResource resource : resourceDAO.findAll()) { + if (resource.getConnector().getLocation().startsWith("file")) { + expected++; + } + } + + assertEquals(expected, + ApplicationContextProvider.getApplicationContext(). + getBeanNamesForType(Connector.class, false, true).length); + } +} http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/ResourceDataBinderTest.java ---------------------------------------------------------------------- diff --git a/syncope620/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/ResourceDataBinderTest.java b/syncope620/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/ResourceDataBinderTest.java new file mode 100644 index 0000000..40764ab --- /dev/null +++ b/syncope620/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/ResourceDataBinderTest.java @@ -0,0 +1,152 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.syncope.core.provisioning.java.data; + +import org.apache.syncope.core.provisioning.java.AbstractTest; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; + +import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.IOException; +import java.io.StringWriter; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import org.apache.syncope.common.lib.to.MappingItemTO; +import org.apache.syncope.common.lib.to.MappingTO; +import org.apache.syncope.common.lib.to.ResourceTO; +import org.apache.syncope.common.lib.types.IntMappingType; +import org.apache.syncope.common.lib.types.MappingPurpose; +import org.apache.syncope.common.lib.types.PropagationMode; +import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO; +import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO; +import org.apache.syncope.core.persistence.api.entity.ExternalResource; +import org.apache.syncope.core.persistence.api.entity.MappingItem; +import org.apache.syncope.core.persistence.api.entity.user.UPlainSchema; +import org.apache.syncope.core.provisioning.api.data.ResourceDataBinder; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +public class ResourceDataBinderTest extends AbstractTest { + + @Autowired + private ExternalResourceDAO resourceDAO; + + @Autowired + private ResourceDataBinder resourceDataBinder; + + @Autowired + private PlainSchemaDAO plainSchemaDAO; + + @Test + public void databinding() throws IOException { + ExternalResource resource = resourceDAO.find("ws-target-resource-2"); + assertNotNull(resource); + + ResourceTO resourceTO = resourceDataBinder.getResourceTO(resource); + assertNotNull(resourceTO); + + ExternalResource fromto = resourceDataBinder.update(resource, resourceTO); + assertNotNull(fromto); + assertEquals(resource, fromto); + + ObjectMapper mapper = new ObjectMapper(); + + StringWriter writer = new StringWriter(); + mapper.writeValue(writer, resourceTO); + + assertEquals(resourceTO, mapper.readValue(writer.toString(), ResourceTO.class)); + + List<ResourceTO> resourceTOs = resourceDataBinder.getResourceTOs(resourceDAO.findAll()); + assertNotNull(resourceTOs); + assertFalse(resourceTOs.isEmpty()); + + writer = new StringWriter(); + mapper.writeValue(writer, resourceTOs); + + ResourceTO[] actual = mapper.readValue(writer.toString(), ResourceTO[].class); + assertEquals(resourceTOs, Arrays.asList(actual)); + } + + @Test + public void issue42() { + UPlainSchema userId = plainSchemaDAO.find("userId", UPlainSchema.class); + + Set<MappingItem> beforeUserIdMappings = new HashSet<>(); + for (ExternalResource res : resourceDAO.findAll()) { + if (res.getUmapping() != null) { + for (MappingItem mapItem : res.getUmapping().getItems()) { + if (userId.getKey().equals(mapItem.getIntAttrName())) { + beforeUserIdMappings.add(mapItem); + } + } + } + } + + ResourceTO resourceTO = new ResourceTO(); + resourceTO.setKey("resource-issue42"); + resourceTO.setConnectorId(100L); + resourceTO.setPropagationMode(PropagationMode.ONE_PHASE); + resourceTO.setEnforceMandatoryCondition(true); + + MappingTO mapping = new MappingTO(); + resourceTO.setUmapping(mapping); + + MappingItemTO item = new MappingItemTO(); + item.setIntAttrName("userId"); + item.setIntMappingType(IntMappingType.UserPlainSchema); + item.setExtAttrName("campo1"); + item.setAccountid(true); + item.setMandatoryCondition("false"); + item.setPurpose(MappingPurpose.BOTH); + mapping.setAccountIdItem(item); + + ExternalResource resource = resourceDataBinder.create(resourceTO); + resource = resourceDAO.save(resource); + assertNotNull(resource); + assertNotNull(resource.getUmapping()); + assertEquals(1, resource.getUmapping().getItems().size()); + + resourceDAO.flush(); + + ExternalResource actual = resourceDAO.find("resource-issue42"); + assertNotNull(actual); + assertEquals(resource, actual); + + userId = plainSchemaDAO.find("userId", UPlainSchema.class); + + Set<MappingItem> afterUserIdMappings = new HashSet<>(); + for (ExternalResource res : resourceDAO.findAll()) { + if (res.getUmapping() != null) { + for (MappingItem mapItem : res.getUmapping().getItems()) { + if (userId.getKey().equals(mapItem.getIntAttrName())) { + afterUserIdMappings.add(mapItem); + } + } + } + } + + assertEquals(beforeUserIdMappings.size(), afterUserIdMappings.size() - 1); + } +} http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/provisioning-java/src/test/resources/provisioningTest.xml ---------------------------------------------------------------------- diff --git a/syncope620/core/provisioning-java/src/test/resources/provisioningTest.xml b/syncope620/core/provisioning-java/src/test/resources/provisioningTest.xml new file mode 100644 index 0000000..b6621c3 --- /dev/null +++ b/syncope620/core/provisioning-java/src/test/resources/provisioningTest.xml @@ -0,0 +1,46 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +Licensed to the Apache Software Foundation (ASF) under one +or more contributor license agreements. See the NOTICE file +distributed with this work for additional information +regarding copyright ownership. The ASF licenses this file +to you 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. +--> +<beans xmlns="http://www.springframework.org/schema/beans" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans.xsd"> + + <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> + <property name="locations"> + <list> + <value>classpath:persistence.properties</value> + <value>classpath:security.properties</value> + <value>classpath:connid.properties</value> + <value>classpath:mail.properties</value> + <value>classpath:workflow.properties</value> + <value>classpath:provisioning.properties</value> + </list> + </property> + <property name="ignoreResourceNotFound" value="true"/> + <property name="ignoreUnresolvablePlaceholders" value="true"/> + </bean> + + <bean id="contentXML" class="org.apache.syncope.core.misc.spring.ResourceWithFallbackLoader"> + <property name="primary" value="file:${conf.directory}/content.xml"/> + <property name="fallback" value="classpath:content.xml"/> + </bean> + <bean class="org.apache.syncope.core.persistence.jpa.content.XMLContentLoader" init-method="load"/> + +</beans> http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/rest-cxf/pom.xml ---------------------------------------------------------------------- diff --git a/syncope620/core/rest-cxf/pom.xml b/syncope620/core/rest-cxf/pom.xml new file mode 100644 index 0000000..d9d06e0 --- /dev/null +++ b/syncope620/core/rest-cxf/pom.xml @@ -0,0 +1,140 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +Licensed to the Apache Software Foundation (ASF) under one +or more contributor license agreements. See the NOTICE file +distributed with this work for additional information +regarding copyright ownership. The ASF licenses this file +to you 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. +--> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.apache.syncope</groupId> + <artifactId>syncope-core</artifactId> + <version>2.0.0-SNAPSHOT</version> + </parent> + + <name>Apache Syncope Core REST CXF</name> + <description>Apache Syncope Core REST CXF</description> + <groupId>org.apache.syncope.core</groupId> + <artifactId>syncope-core-rest-cxf</artifactId> + <packaging>jar</packaging> + + <properties> + <rootpom.basedir>${basedir}/../..</rootpom.basedir> + </properties> + + <dependencies> + <dependency> + <groupId>javax.servlet</groupId> + <artifactId>javax.servlet-api</artifactId> + <scope>provided</scope> + </dependency> + + <dependency> + <groupId>org.apache.geronimo.specs</groupId> + <artifactId>geronimo-jpa_2.0_spec</artifactId> + </dependency> + + <dependency> + <groupId>org.springframework</groupId> + <artifactId>spring-orm</artifactId> + </dependency> + <dependency> + <groupId>org.springframework</groupId> + <artifactId>spring-web</artifactId> + </dependency> + <dependency> + <groupId>org.springframework.security</groupId> + <artifactId>spring-security-web</artifactId> + </dependency> + <dependency> + <groupId>org.springframework.security</groupId> + <artifactId>spring-security-config</artifactId> + </dependency> + + <dependency> + <groupId>com.fasterxml.jackson.jaxrs</groupId> + <artifactId>jackson-jaxrs-json-provider</artifactId> + </dependency> + <dependency> + <groupId>com.fasterxml.jackson.module</groupId> + <artifactId>jackson-module-afterburner</artifactId> + </dependency> + + <dependency> + <groupId>org.apache.cxf</groupId> + <artifactId>cxf-rt-frontend-jaxrs</artifactId> + </dependency> + <dependency> + <groupId>org.apache.cxf</groupId> + <artifactId>cxf-rt-rs-extension-providers</artifactId> + </dependency> + <dependency> + <groupId>org.apache.cxf</groupId> + <artifactId>cxf-rt-rs-extension-search</artifactId> + </dependency> + <dependency> + <groupId>org.apache.cxf</groupId> + <artifactId>cxf-rt-frontend-jaxws</artifactId> + </dependency> + <dependency> + <groupId>org.apache.cxf</groupId> + <artifactId>cxf-rt-rs-service-description</artifactId> + </dependency> + <dependency> + <groupId>org.apache.cxf</groupId> + <artifactId>cxf-rt-rs-client</artifactId> + </dependency> + + <dependency> + <groupId>org.apache.syncope.common</groupId> + <artifactId>syncope-common-rest-api</artifactId> + <version>${project.version}</version> + <classifier>javadoc</classifier> + </dependency> + <dependency> + <groupId>org.apache.syncope.core</groupId> + <artifactId>syncope-core-logic</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.apache.syncope.common</groupId> + <artifactId>syncope-common-rest-api</artifactId> + <version>${project.version}</version> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-checkstyle-plugin</artifactId> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-pmd-plugin</artifactId> + </plugin> + </plugins> + + <resources> + <resource> + <directory>src/main/resources</directory> + <filtering>true</filtering> + </resource> + </resources> + </build> +</project> http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/QueryResourceInfoComparator.java ---------------------------------------------------------------------- diff --git a/syncope620/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/QueryResourceInfoComparator.java b/syncope620/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/QueryResourceInfoComparator.java new file mode 100644 index 0000000..a45e275 --- /dev/null +++ b/syncope620/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/QueryResourceInfoComparator.java @@ -0,0 +1,114 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.syncope.core.rest.cxf; + +import java.util.List; +import java.util.Map; + +import org.apache.cxf.jaxrs.ext.ResourceComparator; +import org.apache.cxf.jaxrs.model.ClassResourceInfo; +import org.apache.cxf.jaxrs.model.OperationResourceInfo; +import org.apache.cxf.jaxrs.model.OperationResourceInfoComparator; +import org.apache.cxf.jaxrs.model.Parameter; +import org.apache.cxf.jaxrs.utils.JAXRSUtils; +import org.apache.cxf.message.Message; + +public class QueryResourceInfoComparator extends OperationResourceInfoComparator implements ResourceComparator { + + public QueryResourceInfoComparator() { + super(null, null); + } + + @Override + public int compare(final ClassResourceInfo cri1, final ClassResourceInfo cri2, final Message message) { + // Leave Class selection to CXF + return 0; + } + + @Override + public int compare(final OperationResourceInfo oper1, final OperationResourceInfo oper2, final Message message) { + // Check if CXF can make a decision + int cxfResult = super.compare(oper1, oper2); + if (cxfResult != 0) { + return cxfResult; + } + + int op1Counter = getMatchingRate(oper1, message); + int op2Counter = getMatchingRate(oper2, message); + + return op1Counter == op2Counter + ? 0 + : op1Counter < op2Counter + ? 1 + : -1; + } + + /** + * This method calculates a number indicating a good or bad match between values provided within the request and + * expected method parameters. A higher number means a better match. + * + * @param operation The operation to be rated, based on contained parameterInfo values. + * @param message A message containing query and header values from user request + * @return A positive or negative number, indicating a good match between query and method + */ + protected int getMatchingRate(final OperationResourceInfo operation, final Message message) { + List<Parameter> params = operation.getParameters(); + if (params == null || params.isEmpty()) { + return 0; + } + + // Get Request QueryParams + String query = (String) message.get(Message.QUERY_STRING); + String path = (String) message.get(Message.REQUEST_URI); + Map<String, List<String>> qParams = JAXRSUtils.getStructuredParams(query, "&", true, false); + Map<String, List<String>> mParams = JAXRSUtils.getMatrixParams(path, true); + // Get Request Headers + Map<?, ?> qHeader = (java.util.Map<?, ?>) message.get(Message.PROTOCOL_HEADERS); + + int rate = 0; + for (Parameter p : params) { + switch (p.getType()) { + case QUERY: + if (qParams.containsKey(p.getName())) { + rate += 2; + } else if (p.getDefaultValue() == null) { + rate -= 1; + } + break; + case MATRIX: + if (mParams.containsKey(p.getName())) { + rate += 2; + } else if (p.getDefaultValue() == null) { + rate -= 1; + } + break; + case HEADER: + if (qHeader.containsKey(p.getName())) { + rate += 2; + } else if (p.getDefaultValue() == null) { + rate -= 1; + } + break; + default: + break; + } + } + return rate; + } +}
