http://git-wip-us.apache.org/repos/asf/syncope/blob/97607b16/core/logic/src/main/java/org/apache/syncope/core/logic/AnyObjectLogic.java ---------------------------------------------------------------------- diff --cc core/logic/src/main/java/org/apache/syncope/core/logic/AnyObjectLogic.java index 257bf42,0000000..05db8b5 mode 100644,000000..100644 --- a/core/logic/src/main/java/org/apache/syncope/core/logic/AnyObjectLogic.java +++ b/core/logic/src/main/java/org/apache/syncope/core/logic/AnyObjectLogic.java @@@ -1,342 -1,0 +1,344 @@@ +/* + * 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.logic; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; +import javax.annotation.Resource; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.collections4.Transformer; +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.syncope.common.lib.SyncopeClientException; +import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.common.lib.mod.AnyObjectMod; +import org.apache.syncope.common.lib.to.PropagationStatus; +import org.apache.syncope.common.lib.to.AnyObjectTO; +import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.apache.syncope.common.lib.types.ClientExceptionType; +import org.apache.syncope.common.lib.types.Entitlement; +import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO; +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.provisioning.api.AnyObjectProvisioningManager; +import org.apache.syncope.core.provisioning.api.data.AnyObjectDataBinder; +import org.apache.syncope.core.provisioning.api.propagation.PropagationManager; +import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecutor; +import org.apache.syncope.core.misc.security.AuthContextUtils; +import org.apache.syncope.core.misc.security.UnauthorizedException; +import org.apache.syncope.core.persistence.api.dao.AnySearchDAO; +import org.apache.syncope.core.persistence.api.dao.NotFoundException; +import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject; +import org.apache.syncope.core.provisioning.api.AnyTransformer; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.interceptor.TransactionInterceptor; + +/** + * Note that this controller does not extend {@link AbstractTransactionalLogic}, hence does not provide any + * Spring's Transactional logic at class level. + */ +@Component +public class AnyObjectLogic extends AbstractAnyLogic<AnyObjectTO, AnyObjectMod> { + + @Autowired + protected AnyObjectDAO anyObjectDAO; + + @Autowired + protected AnySearchDAO searchDAO; + + @Autowired + protected AnyObjectDataBinder binder; + + @Autowired + protected PropagationManager propagationManager; + + @Autowired + protected PropagationTaskExecutor taskExecutor; + + @Autowired + protected AnyTransformer attrTransformer; + + @Resource(name = "anonymousUser") + protected String anonymousUser; + + @Autowired + protected AnyObjectProvisioningManager provisioningManager; + + @PreAuthorize("hasRole('" + Entitlement.ANY_OBJECT_READ + "')") + @Transactional(readOnly = true) + @Override + public AnyObjectTO read(final Long anyObjectKey) { + return binder.getAnyObjectTO(anyObjectKey); + } + + @PreAuthorize("isAuthenticated()") + @Transactional(readOnly = true, rollbackFor = { Throwable.class }) + @Override + public int count(final List<String> realms) { + return anyObjectDAO.count(getEffectiveRealms(SyncopeConstants.FULL_ADMIN_REALMS, realms)); + } + + @PreAuthorize("hasRole('" + Entitlement.ANY_OBJECT_LIST + "')") + @Transactional(readOnly = true) + @Override + public List<AnyObjectTO> list( - final int page, final int size, final List<OrderByClause> orderBy, final List<String> realms) { ++ final int page, final int size, final List<OrderByClause> orderBy, ++ final List<String> realms, final boolean details) { + - return list(null, page, size, orderBy, realms); ++ return list(null, page, size, orderBy, realms, details); + } + + @PreAuthorize("hasRole('" + Entitlement.ANY_OBJECT_LIST + "')") + @Transactional(readOnly = true) + public List<AnyObjectTO> list(final String type, - final int page, final int size, final List<OrderByClause> orderBy, final List<String> realms) { ++ final int page, final int size, final List<OrderByClause> orderBy, ++ final List<String> realms, final boolean details) { + + Set<String> effectiveRealms = getEffectiveRealms(SyncopeConstants.FULL_ADMIN_REALMS, realms); + + return CollectionUtils.collect(StringUtils.isBlank(type) + ? anyObjectDAO.findAll(effectiveRealms, page, size, orderBy) + : anyObjectDAO.findAll(type, effectiveRealms, page, size, orderBy), + new Transformer<AnyObject, AnyObjectTO>() { + + @Override + public AnyObjectTO transform(final AnyObject input) { - return binder.getAnyObjectTO(input); ++ return binder.getAnyObjectTO(input, details); + } + }, new ArrayList<AnyObjectTO>()); + } + + @PreAuthorize("hasRole('" + Entitlement.ANY_OBJECT_SEARCH + "')") + @Transactional(readOnly = true, rollbackFor = { Throwable.class }) + @Override + public int searchCount(final SearchCond searchCondition, final List<String> realms) { + return searchDAO.count( + getEffectiveRealms(AuthContextUtils.getAuthorizations().get(Entitlement.ANY_OBJECT_SEARCH), realms), + searchCondition, AnyTypeKind.ANY_OBJECT); + } + + @PreAuthorize("hasRole('" + Entitlement.ANY_OBJECT_SEARCH + "')") + @Transactional(readOnly = true, rollbackFor = { Throwable.class }) + @Override + public List<AnyObjectTO> search(final SearchCond searchCondition, final int page, final int size, - final List<OrderByClause> orderBy, final List<String> realms) { ++ final List<OrderByClause> orderBy, final List<String> realms, final boolean details) { + + List<AnyObject> matchingAnyObjects = searchDAO.search( + getEffectiveRealms(AuthContextUtils.getAuthorizations().get(Entitlement.ANY_OBJECT_SEARCH), realms), + searchCondition, page, size, orderBy, AnyTypeKind.ANY_OBJECT); + return CollectionUtils.collect(matchingAnyObjects, new Transformer<AnyObject, AnyObjectTO>() { + + @Override + public AnyObjectTO transform(final AnyObject input) { - return binder.getAnyObjectTO(input); ++ return binder.getAnyObjectTO(input, details); + } + }, new ArrayList<AnyObjectTO>()); + } + + @PreAuthorize("hasRole('" + Entitlement.ANY_OBJECT_CREATE + "')") + @Override + public AnyObjectTO create(final AnyObjectTO anyObjectTO) { + if (anyObjectTO.getRealm() == null) { + throw SyncopeClientException.build(ClientExceptionType.InvalidRealm); + } + Set<String> effectiveRealms = getEffectiveRealms( + AuthContextUtils.getAuthorizations().get(Entitlement.ANY_OBJECT_CREATE), + Collections.singleton(anyObjectTO.getRealm())); + if (effectiveRealms.isEmpty()) { + throw new UnauthorizedException(AnyTypeKind.ANY_OBJECT, null); + } + + // Any transformation (if configured) + AnyObjectTO actual = attrTransformer.transform(anyObjectTO); + LOG.debug("Transformed: {}", actual); + + if (anyObjectTO.getType() == null) { + throw SyncopeClientException.build(ClientExceptionType.InvalidAnyType); + } + + /* + * Actual operations: workflow, propagation + */ + Map.Entry<Long, List<PropagationStatus>> created = provisioningManager.create(anyObjectTO); + AnyObjectTO savedTO = binder.getAnyObjectTO(created.getKey()); + savedTO.getPropagationStatusTOs().addAll(created.getValue()); + return savedTO; + } + + @PreAuthorize("hasRole('" + Entitlement.ANY_OBJECT_UPDATE + "')") + @Override + public AnyObjectTO update(final AnyObjectMod anyObjectMod) { + AnyObject anyObject = anyObjectDAO.authFind(anyObjectMod.getKey()); + if (anyObject == null) { + throw new NotFoundException("AnyObject with key " + anyObjectMod.getKey()); + } + Set<String> effectiveRealms = getEffectiveRealms( + AuthContextUtils.getAuthorizations().get(Entitlement.ANY_OBJECT_UPDATE), + Collections.singleton(anyObject.getRealm().getFullPath())); + if (effectiveRealms.isEmpty()) { + throw new UnauthorizedException(AnyTypeKind.ANY_OBJECT, anyObject.getKey()); + } + + // Any transformation (if configured) + AnyObjectMod actual = attrTransformer.transform(anyObjectMod); + LOG.debug("Transformed: {}", actual); + + Map.Entry<Long, List<PropagationStatus>> updated = provisioningManager.update(anyObjectMod); + + AnyObjectTO updatedTO = binder.getAnyObjectTO(updated.getKey()); + updatedTO.getPropagationStatusTOs().addAll(updated.getValue()); + return updatedTO; + } + + @PreAuthorize("hasRole('" + Entitlement.ANY_OBJECT_DELETE + "')") + @Override + public AnyObjectTO delete(final Long anyObjectKey) { + AnyObject anyObject = anyObjectDAO.authFind(anyObjectKey); + if (anyObject == null) { + throw new NotFoundException("AnyObject with key " + anyObjectKey); + } + Set<String> effectiveRealms = getEffectiveRealms( + AuthContextUtils.getAuthorizations().get(Entitlement.ANY_OBJECT_UPDATE), + Collections.singleton(anyObject.getRealm().getFullPath())); + if (effectiveRealms.isEmpty()) { + throw new UnauthorizedException(AnyTypeKind.ANY_OBJECT, anyObject.getKey()); + } + + List<PropagationStatus> statuses = provisioningManager.delete(anyObjectKey); + + AnyObjectTO anyObjectTO = new AnyObjectTO(); + anyObjectTO.setKey(anyObjectKey); + + anyObjectTO.getPropagationStatusTOs().addAll(statuses); + + return anyObjectTO; + } + + @PreAuthorize("hasRole('" + Entitlement.ANY_OBJECT_UPDATE + "')") + @Transactional(rollbackFor = { Throwable.class }) + @Override + public AnyObjectTO unlink(final Long anyObjectKey, final Collection<String> resources) { + AnyObjectMod anyObjectMod = new AnyObjectMod(); + anyObjectMod.setKey(anyObjectKey); + anyObjectMod.getResourcesToRemove().addAll(resources); + final Long updatedResult = provisioningManager.unlink(anyObjectMod); + + return binder.getAnyObjectTO(updatedResult); + } + + @PreAuthorize("hasRole('" + Entitlement.ANY_OBJECT_UPDATE + "')") + @Transactional(rollbackFor = { Throwable.class }) + @Override + public AnyObjectTO link(final Long anyObjectKey, final Collection<String> resources) { + AnyObjectMod anyObjectMod = new AnyObjectMod(); + anyObjectMod.setKey(anyObjectKey); + anyObjectMod.getResourcesToAdd().addAll(resources); + return binder.getAnyObjectTO(provisioningManager.link(anyObjectMod)); + } + + @PreAuthorize("hasRole('" + Entitlement.ANY_OBJECT_UPDATE + "')") + @Transactional(rollbackFor = { Throwable.class }) + @Override + public AnyObjectTO unassign(final Long anyObjectKey, final Collection<String> resources) { + AnyObjectMod anyObjectMod = new AnyObjectMod(); + anyObjectMod.setKey(anyObjectKey); + anyObjectMod.getResourcesToRemove().addAll(resources); + return update(anyObjectMod); + } + + @PreAuthorize("hasRole('" + Entitlement.ANY_OBJECT_UPDATE + "')") + @Transactional(rollbackFor = { Throwable.class }) + @Override + public AnyObjectTO assign(final Long anyObjectKey, final Collection<String> resources, + final boolean changePwd, final String password) { + + AnyObjectMod userMod = new AnyObjectMod(); + userMod.setKey(anyObjectKey); + userMod.getResourcesToAdd().addAll(resources); + return update(userMod); + } + + @PreAuthorize("hasRole('" + Entitlement.ANY_OBJECT_UPDATE + "')") + @Transactional(rollbackFor = { Throwable.class }) + @Override + public AnyObjectTO deprovision(final Long anyObjectKey, final Collection<String> resources) { + AnyObject anyObject = anyObjectDAO.authFind(anyObjectKey); + + List<PropagationStatus> statuses = provisioningManager.deprovision(anyObjectKey, resources); + - AnyObjectTO updatedTO = binder.getAnyObjectTO(anyObject); ++ AnyObjectTO updatedTO = binder.getAnyObjectTO(anyObject, true); + updatedTO.getPropagationStatusTOs().addAll(statuses); + return updatedTO; + } + + @PreAuthorize("hasRole('" + Entitlement.ANY_OBJECT_UPDATE + "')") + @Transactional(rollbackFor = { Throwable.class }) + @Override + public AnyObjectTO provision(final Long anyObjectKey, final Collection<String> resources, + final boolean changePwd, final String password) { + + AnyObjectTO original = binder.getAnyObjectTO(anyObjectKey); + + //trick: assign and retrieve propagation statuses ... + original.getPropagationStatusTOs().addAll( + assign(anyObjectKey, resources, changePwd, password).getPropagationStatusTOs()); + + // .... rollback. + TransactionInterceptor.currentTransactionStatus().setRollbackOnly(); + return original; + } + + @Override + protected AnyObjectTO resolveReference(final Method method, final Object... args) + throws UnresolvedReferenceException { + + Long key = null; + + if (ArrayUtils.isNotEmpty(args)) { + for (int i = 0; key == null && i < args.length; i++) { + if (args[i] instanceof Long) { + key = (Long) args[i]; + } else if (args[i] instanceof AnyObjectTO) { + key = ((AnyObjectTO) args[i]).getKey(); + } else if (args[i] instanceof AnyObjectMod) { + key = ((AnyObjectMod) args[i]).getKey(); + } + } + } + + if ((key != null) && !key.equals(0L)) { + try { + return binder.getAnyObjectTO(key); + } catch (Throwable ignore) { + LOG.debug("Unresolved reference", ignore); + throw new UnresolvedReferenceException(ignore); + } + } + + throw new UnresolvedReferenceException(); + } +}
http://git-wip-us.apache.org/repos/asf/syncope/blob/97607b16/core/logic/src/main/java/org/apache/syncope/core/logic/GroupLogic.java ---------------------------------------------------------------------- diff --cc core/logic/src/main/java/org/apache/syncope/core/logic/GroupLogic.java index 877a272,0000000..ff5cd64 mode 100644,000000..100644 --- a/core/logic/src/main/java/org/apache/syncope/core/logic/GroupLogic.java +++ b/core/logic/src/main/java/org/apache/syncope/core/logic/GroupLogic.java @@@ -1,357 -1,0 +1,358 @@@ +/* + * 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.logic; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; +import javax.annotation.Resource; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.collections4.Transformer; +import org.apache.commons.lang3.ArrayUtils; +import org.apache.syncope.common.lib.SyncopeClientException; +import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.common.lib.mod.GroupMod; +import org.apache.syncope.common.lib.to.PropagationStatus; +import org.apache.syncope.common.lib.to.GroupTO; +import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.apache.syncope.common.lib.types.ClientExceptionType; +import org.apache.syncope.common.lib.types.Entitlement; +import org.apache.syncope.core.misc.RealmUtils; +import org.apache.syncope.core.persistence.api.dao.GroupDAO; +import org.apache.syncope.core.persistence.api.dao.UserDAO; +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.entity.group.Group; +import org.apache.syncope.core.provisioning.api.GroupProvisioningManager; +import org.apache.syncope.core.provisioning.api.data.GroupDataBinder; +import org.apache.syncope.core.provisioning.api.propagation.PropagationManager; +import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecutor; +import org.apache.syncope.core.misc.security.AuthContextUtils; +import org.apache.syncope.core.misc.security.UnauthorizedException; +import org.apache.syncope.core.persistence.api.dao.AnySearchDAO; +import org.apache.syncope.core.persistence.api.dao.NotFoundException; +import org.apache.syncope.core.provisioning.api.AnyTransformer; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.interceptor.TransactionInterceptor; + +/** + * Note that this controller does not extend {@link AbstractTransactionalLogic}, hence does not provide any + * Spring's Transactional logic at class level. + */ +@Component +public class GroupLogic extends AbstractAnyLogic<GroupTO, GroupMod> { + + @Autowired + protected GroupDAO groupDAO; + + @Autowired + protected UserDAO userDAO; + + @Autowired + protected AnySearchDAO searchDAO; + + @Autowired + protected GroupDataBinder binder; + + @Autowired + protected PropagationManager propagationManager; + + @Autowired + protected PropagationTaskExecutor taskExecutor; + + @Autowired + protected AnyTransformer attrTransformer; + + @Resource(name = "anonymousUser") + protected String anonymousUser; + + @Autowired + protected GroupProvisioningManager provisioningManager; + + @PreAuthorize("hasRole('" + Entitlement.GROUP_READ + "')") + @Transactional(readOnly = true) + @Override + public GroupTO read(final Long groupKey) { + return binder.getGroupTO(groupKey); + } + + @PreAuthorize("isAuthenticated() and not(hasRole('" + Entitlement.ANONYMOUS + "'))") + @Transactional(readOnly = true) + public List<GroupTO> own() { + return CollectionUtils.collect( + userDAO.findAllGroups(userDAO.find(AuthContextUtils.getAuthenticatedUsername())), + new Transformer<Group, GroupTO>() { + + @Override + public GroupTO transform(final Group input) { - return binder.getGroupTO(input); ++ return binder.getGroupTO(input, true); + } + }, new ArrayList<GroupTO>()); + } + + @PreAuthorize("isAuthenticated()") + @Transactional(readOnly = true, rollbackFor = { Throwable.class }) + @Override + public int count(final List<String> realms) { + return groupDAO.count(getEffectiveRealms(SyncopeConstants.FULL_ADMIN_REALMS, realms)); + } + + @PreAuthorize("isAuthenticated()") + @Transactional(readOnly = true) + @Override + public List<GroupTO> list( - final int page, final int size, final List<OrderByClause> orderBy, final List<String> realms) { ++ final int page, final int size, final List<OrderByClause> orderBy, ++ final List<String> realms, final boolean details) { + + return CollectionUtils.collect(groupDAO.findAll( + getEffectiveRealms(SyncopeConstants.FULL_ADMIN_REALMS, realms), + page, size, orderBy), + new Transformer<Group, GroupTO>() { + + @Override + public GroupTO transform(final Group input) { - return binder.getGroupTO(input); ++ return binder.getGroupTO(input, details); + } + }, new ArrayList<GroupTO>()); + } + + @PreAuthorize("hasRole('" + Entitlement.GROUP_SEARCH + "')") + @Transactional(readOnly = true, rollbackFor = { Throwable.class }) + @Override + public int searchCount(final SearchCond searchCondition, final List<String> realms) { + return searchDAO.count( + getEffectiveRealms(AuthContextUtils.getAuthorizations().get(Entitlement.GROUP_SEARCH), realms), + searchCondition, AnyTypeKind.GROUP); + } + + @PreAuthorize("hasRole('" + Entitlement.GROUP_SEARCH + "')") + @Transactional(readOnly = true, rollbackFor = { Throwable.class }) + @Override + public List<GroupTO> search(final SearchCond searchCondition, final int page, final int size, - final List<OrderByClause> orderBy, final List<String> realms) { ++ final List<OrderByClause> orderBy, final List<String> realms, final boolean details) { + + final List<Group> matchingGroups = searchDAO.search( + getEffectiveRealms(AuthContextUtils.getAuthorizations().get(Entitlement.GROUP_SEARCH), realms), + searchCondition, page, size, orderBy, AnyTypeKind.GROUP); + return CollectionUtils.collect(matchingGroups, new Transformer<Group, GroupTO>() { + + @Override + public GroupTO transform(final Group input) { - return binder.getGroupTO(input); ++ return binder.getGroupTO(input, details); + } + }, new ArrayList<GroupTO>()); + } + + @PreAuthorize("hasRole('" + Entitlement.GROUP_CREATE + "')") + @Override + public GroupTO create(final GroupTO groupTO) { + if (groupTO.getRealm() == null) { + SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidRealm); + throw sce; + } + Set<String> effectiveRealms = getEffectiveRealms( + AuthContextUtils.getAuthorizations().get(Entitlement.GROUP_CREATE), + Collections.singleton(groupTO.getRealm())); + if (effectiveRealms.isEmpty()) { + throw new UnauthorizedException(AnyTypeKind.GROUP, null); + } + + // Any transformation (if configured) + GroupTO actual = attrTransformer.transform(groupTO); + LOG.debug("Transformed: {}", actual); + + /* + * Actual operations: workflow, propagation + */ + Map.Entry<Long, List<PropagationStatus>> created = provisioningManager.create(groupTO); + GroupTO savedTO = binder.getGroupTO(created.getKey()); + savedTO.getPropagationStatusTOs().addAll(created.getValue()); + return savedTO; + } + + @PreAuthorize("hasRole('" + Entitlement.GROUP_UPDATE + "')") + @Override + public GroupTO update(final GroupMod groupMod) { + Group group = groupDAO.authFind(groupMod.getKey()); + if (group == null) { + throw new NotFoundException("Group with key " + groupMod.getKey()); + } + Set<String> effectiveRealms = getEffectiveRealms( + AuthContextUtils.getAuthorizations().get(Entitlement.GROUP_UPDATE), + Collections.singleton(RealmUtils.getGroupOwnerRealm(group.getRealm().getFullPath(), group.getKey()))); + if (effectiveRealms.isEmpty()) { + throw new UnauthorizedException(AnyTypeKind.GROUP, group.getKey()); + } + + // Any transformation (if configured) + GroupMod actual = attrTransformer.transform(groupMod); + LOG.debug("Transformed: {}", actual); + + Map.Entry<Long, List<PropagationStatus>> updated = provisioningManager.update(groupMod); + + GroupTO updatedTO = binder.getGroupTO(updated.getKey()); + updatedTO.getPropagationStatusTOs().addAll(updated.getValue()); + return updatedTO; + } + + @PreAuthorize("hasRole('" + Entitlement.GROUP_DELETE + "')") + @Override + public GroupTO delete(final Long groupKey) { + Group group = groupDAO.authFind(groupKey); + if (group == null) { + throw new NotFoundException("Group with key " + groupKey); + } + Set<String> effectiveRealms = getEffectiveRealms( + AuthContextUtils.getAuthorizations().get(Entitlement.GROUP_DELETE), + Collections.singleton(RealmUtils.getGroupOwnerRealm(group.getRealm().getFullPath(), group.getKey()))); + if (effectiveRealms.isEmpty()) { + throw new UnauthorizedException(AnyTypeKind.GROUP, group.getKey()); + } + + List<Group> ownedGroups = groupDAO.findOwnedByGroup(groupKey); + if (!ownedGroups.isEmpty()) { + SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.GroupOwnership); + sce.getElements().addAll(CollectionUtils.collect(ownedGroups, new Transformer<Group, String>() { + + @Override + public String transform(final Group group) { + return group.getKey() + " " + group.getName(); + } + }, new ArrayList<String>())); + throw sce; + } + + List<PropagationStatus> statuses = provisioningManager.delete(groupKey); + + GroupTO groupTO = new GroupTO(); + groupTO.setKey(groupKey); + + groupTO.getPropagationStatusTOs().addAll(statuses); + + return groupTO; + } + + @PreAuthorize("hasRole('" + Entitlement.GROUP_UPDATE + "')") + @Transactional(rollbackFor = { Throwable.class }) + @Override + public GroupTO unlink(final Long groupKey, final Collection<String> resources) { + final GroupMod groupMod = new GroupMod(); + groupMod.setKey(groupKey); + groupMod.getResourcesToRemove().addAll(resources); + final Long updatedResult = provisioningManager.unlink(groupMod); + + return binder.getGroupTO(updatedResult); + } + + @PreAuthorize("hasRole('" + Entitlement.GROUP_UPDATE + "')") + @Transactional(rollbackFor = { Throwable.class }) + @Override + public GroupTO link(final Long groupKey, final Collection<String> resources) { + final GroupMod groupMod = new GroupMod(); + groupMod.setKey(groupKey); + groupMod.getResourcesToAdd().addAll(resources); + return binder.getGroupTO(provisioningManager.link(groupMod)); + } + + @PreAuthorize("hasRole('" + Entitlement.GROUP_UPDATE + "')") + @Transactional(rollbackFor = { Throwable.class }) + @Override + public GroupTO unassign(final Long groupKey, final Collection<String> resources) { + final GroupMod groupMod = new GroupMod(); + groupMod.setKey(groupKey); + groupMod.getResourcesToRemove().addAll(resources); + return update(groupMod); + } + + @PreAuthorize("hasRole('" + Entitlement.GROUP_UPDATE + "')") + @Transactional(rollbackFor = { Throwable.class }) + @Override + public GroupTO assign( + final Long groupKey, final Collection<String> resources, final boolean changePwd, final String password) { + + final GroupMod userMod = new GroupMod(); + userMod.setKey(groupKey); + userMod.getResourcesToAdd().addAll(resources); + return update(userMod); + } + + @PreAuthorize("hasRole('" + Entitlement.GROUP_UPDATE + "')") + @Transactional(rollbackFor = { Throwable.class }) + @Override + public GroupTO deprovision(final Long groupKey, final Collection<String> resources) { + final Group group = groupDAO.authFind(groupKey); + + List<PropagationStatus> statuses = provisioningManager.deprovision(groupKey, resources); + - GroupTO updatedTO = binder.getGroupTO(group); ++ GroupTO updatedTO = binder.getGroupTO(group, true); + updatedTO.getPropagationStatusTOs().addAll(statuses); + return updatedTO; + } + + @PreAuthorize("hasRole('" + Entitlement.GROUP_UPDATE + "')") + @Transactional(rollbackFor = { Throwable.class }) + @Override + public GroupTO provision( + final Long groupKey, final Collection<String> resources, final boolean changePwd, final String password) { + GroupTO original = binder.getGroupTO(groupKey); + + //trick: assign and retrieve propagation statuses ... + original.getPropagationStatusTOs().addAll( + assign(groupKey, resources, changePwd, password).getPropagationStatusTOs()); + + // .... rollback. + TransactionInterceptor.currentTransactionStatus().setRollbackOnly(); + return original; + } + + @Override + protected GroupTO resolveReference(final Method method, final Object... args) throws UnresolvedReferenceException { + Long key = null; + + if (ArrayUtils.isNotEmpty(args)) { + for (int i = 0; key == null && i < args.length; i++) { + if (args[i] instanceof Long) { + key = (Long) args[i]; + } else if (args[i] instanceof GroupTO) { + key = ((GroupTO) args[i]).getKey(); + } else if (args[i] instanceof GroupMod) { + key = ((GroupMod) args[i]).getKey(); + } + } + } + + if ((key != null) && !key.equals(0L)) { + try { + return binder.getGroupTO(key); + } catch (Throwable ignore) { + LOG.debug("Unresolved reference", ignore); + throw new UnresolvedReferenceException(ignore); + } + } + + throw new UnresolvedReferenceException(); + } +} http://git-wip-us.apache.org/repos/asf/syncope/blob/97607b16/core/logic/src/main/java/org/apache/syncope/core/logic/UserLogic.java ---------------------------------------------------------------------- diff --cc core/logic/src/main/java/org/apache/syncope/core/logic/UserLogic.java index 28e5cab,0000000..3aec28c mode 100644,000000..100644 --- a/core/logic/src/main/java/org/apache/syncope/core/logic/UserLogic.java +++ b/core/logic/src/main/java/org/apache/syncope/core/logic/UserLogic.java @@@ -1,465 -1,0 +1,466 @@@ +/* + * 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.logic; + +import java.lang.reflect.Method; +import java.security.AccessControlException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.collections4.Transformer; +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.apache.commons.lang3.tuple.Pair; +import org.apache.syncope.common.lib.SyncopeClientException; +import org.apache.syncope.common.lib.mod.StatusMod; +import org.apache.syncope.common.lib.mod.UserMod; +import org.apache.syncope.common.lib.to.PropagationStatus; +import org.apache.syncope.common.lib.to.UserTO; +import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.apache.syncope.common.lib.types.ClientExceptionType; +import org.apache.syncope.common.lib.types.Entitlement; +import org.apache.syncope.core.persistence.api.dao.NotFoundException; +import org.apache.syncope.core.persistence.api.dao.GroupDAO; +import org.apache.syncope.core.persistence.api.dao.UserDAO; +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.entity.group.Group; +import org.apache.syncope.core.persistence.api.entity.user.User; +import org.apache.syncope.core.provisioning.api.UserProvisioningManager; +import org.apache.syncope.core.provisioning.api.data.UserDataBinder; +import org.apache.syncope.core.provisioning.api.propagation.PropagationManager; +import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecutor; +import org.apache.syncope.core.misc.security.AuthContextUtils; +import org.apache.syncope.core.misc.security.UnauthorizedException; +import org.apache.syncope.core.misc.serialization.POJOHelper; +import org.apache.syncope.core.persistence.api.dao.AnySearchDAO; +import org.apache.syncope.core.provisioning.api.AnyTransformer; +import org.apache.syncope.core.provisioning.api.VirAttrHandler; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.interceptor.TransactionInterceptor; + +/** + * Note that this controller does not extend {@link AbstractTransactionalLogic}, hence does not provide any + * Spring's Transactional logic at class level. + */ +@Component +public class UserLogic extends AbstractAnyLogic<UserTO, UserMod> { + + @Autowired + protected UserDAO userDAO; + + @Autowired + protected GroupDAO groupDAO; + + @Autowired + protected AnySearchDAO searchDAO; + + @Autowired + protected UserDataBinder binder; + + @Autowired + protected VirAttrHandler virtAttrHandler; + + @Autowired + protected PropagationManager propagationManager; + + @Autowired + protected PropagationTaskExecutor taskExecutor; + + @Autowired + protected AnyTransformer anyTransformer; + + @Autowired + protected UserProvisioningManager provisioningManager; + + @Autowired + protected SyncopeLogic syncopeLogic; + + @PreAuthorize("hasRole('" + Entitlement.USER_READ + "')") + public String getUsername(final Long key) { + return binder.getUserTO(key).getUsername(); + } + + @PreAuthorize("hasRole('" + Entitlement.USER_READ + "')") + public Long getKey(final String username) { + return binder.getUserTO(username).getKey(); + } + + @PreAuthorize("hasRole('" + Entitlement.USER_LIST + "')") + @Transactional(readOnly = true, rollbackFor = { Throwable.class }) + @Override + public int count(final List<String> realms) { + return userDAO.count( + getEffectiveRealms(AuthContextUtils.getAuthorizations().get(Entitlement.USER_LIST), realms)); + } + + @PreAuthorize("hasRole('" + Entitlement.USER_LIST + "')") + @Transactional(readOnly = true, rollbackFor = { Throwable.class }) + @Override + public List<UserTO> list( - final int page, final int size, final List<OrderByClause> orderBy, final List<String> realms) { ++ final int page, final int size, final List<OrderByClause> orderBy, ++ final List<String> realms, final boolean details) { + + return CollectionUtils.collect(userDAO.findAll( + getEffectiveRealms(AuthContextUtils.getAuthorizations().get(Entitlement.USER_LIST), realms), + page, size, orderBy), + new Transformer<User, UserTO>() { + + @Override + public UserTO transform(final User input) { - return binder.getUserTO(input); ++ return binder.getUserTO(input, details); + } + }, new ArrayList<UserTO>()); + } + + @PreAuthorize("isAuthenticated()") + @Transactional(readOnly = true) + public Pair<String, UserTO> readSelf() { + return ImmutablePair.of( + POJOHelper.serialize(AuthContextUtils.getAuthorizations()), + binder.getAuthenticatedUserTO()); + } + + @PreAuthorize("hasRole('" + Entitlement.USER_READ + "')") + @Transactional(readOnly = true) + @Override + public UserTO read(final Long key) { + return binder.getUserTO(key); + } + + @PreAuthorize("hasRole('" + Entitlement.USER_SEARCH + "')") + @Transactional(readOnly = true, rollbackFor = { Throwable.class }) + @Override + public int searchCount(final SearchCond searchCondition, final List<String> realms) { + return searchDAO.count( + getEffectiveRealms(AuthContextUtils.getAuthorizations().get(Entitlement.USER_SEARCH), realms), + searchCondition, AnyTypeKind.USER); + } + + @PreAuthorize("hasRole('" + Entitlement.USER_SEARCH + "')") + @Transactional(readOnly = true, rollbackFor = { Throwable.class }) + @Override + public List<UserTO> search(final SearchCond searchCondition, final int page, final int size, - final List<OrderByClause> orderBy, final List<String> realms) { ++ final List<OrderByClause> orderBy, final List<String> realms, final boolean details) { + + List<User> matchingUsers = searchDAO.search( + getEffectiveRealms(AuthContextUtils.getAuthorizations().get(Entitlement.USER_SEARCH), realms), + searchCondition, page, size, orderBy, AnyTypeKind.USER); + return CollectionUtils.collect(matchingUsers, new Transformer<User, UserTO>() { + + @Override + public UserTO transform(final User input) { - return binder.getUserTO(input); ++ return binder.getUserTO(input, details); + } + }, new ArrayList<UserTO>()); + } + + @PreAuthorize("isAnonymous() or hasRole('" + Entitlement.ANONYMOUS + "')") + public UserTO createSelf(final UserTO userTO, final boolean storePassword) { + return doCreate(userTO, storePassword); + } + + @PreAuthorize("hasRole('" + Entitlement.USER_CREATE + "')") + @Override + public UserTO create(final UserTO userTO) { + return create(userTO, true); + } + + @PreAuthorize("hasRole('" + Entitlement.USER_CREATE + "')") + public UserTO create(final UserTO userTO, final boolean storePassword) { + if (userTO.getRealm() == null) { + SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidRealm); + throw sce; + } + Set<String> effectiveRealms = getEffectiveRealms( + AuthContextUtils.getAuthorizations().get(Entitlement.USER_CREATE), + Collections.singleton(userTO.getRealm())); + if (effectiveRealms.isEmpty()) { + throw new UnauthorizedException(AnyTypeKind.USER, null); + } + + return doCreate(userTO, storePassword); + } + + protected UserTO doCreate(final UserTO userTO, final boolean storePassword) { + // Any transformation (if configured) + UserTO actual = anyTransformer.transform(userTO); + LOG.debug("Transformed: {}", actual); + + Map.Entry<Long, List<PropagationStatus>> created = provisioningManager.create(actual, storePassword); + + UserTO savedTO = binder.getUserTO(created.getKey()); + savedTO.getPropagationStatusTOs().addAll(created.getValue()); + return savedTO; + } + + @PreAuthorize("isAuthenticated() and not(hasRole('" + Entitlement.ANONYMOUS + "'))") + public UserTO updateSelf(final UserMod userMod) { + UserTO userTO = binder.getAuthenticatedUserTO(); + + if (userTO.getKey() != userMod.getKey()) { + throw new AccessControlException("Not allowed for user with key " + userMod.getKey()); + } + + return update(userMod); + } + + @PreAuthorize("hasRole('" + Entitlement.USER_UPDATE + "')") + @Override + public UserTO update(final UserMod userMod) { + // Any transformation (if configured) + UserMod actual = anyTransformer.transform(userMod); + LOG.debug("Transformed: {}", actual); + + Map.Entry<Long, List<PropagationStatus>> updated = provisioningManager.update(actual); + + UserTO updatedTO = binder.getUserTO(updated.getKey()); + updatedTO.getPropagationStatusTOs().addAll(updated.getValue()); + return updatedTO; + } + + protected Map.Entry<Long, List<PropagationStatus>> setStatusOnWfAdapter(final User user, + final StatusMod statusMod) { + Map.Entry<Long, List<PropagationStatus>> updated; + + switch (statusMod.getType()) { + case SUSPEND: + updated = provisioningManager.suspend(user, statusMod); + break; + + case REACTIVATE: + updated = provisioningManager.reactivate(user, statusMod); + break; + + case ACTIVATE: + default: + updated = provisioningManager.activate(user, statusMod); + break; + + } + + return updated; + } + + @PreAuthorize("hasRole('" + Entitlement.USER_UPDATE + "')") + @Transactional(rollbackFor = { Throwable.class }) + public UserTO status(final StatusMod statusMod) { + User user = userDAO.authFind(statusMod.getKey()); + + Map.Entry<Long, List<PropagationStatus>> updated = setStatusOnWfAdapter(user, statusMod); + final UserTO savedTO = binder.getUserTO(updated.getKey()); + savedTO.getPropagationStatusTOs().addAll(updated.getValue()); + return savedTO; + } + + @PreAuthorize("isAnonymous() or hasRole('" + Entitlement.ANONYMOUS + "')") + @Transactional + public void requestPasswordReset(final String username, final String securityAnswer) { + if (username == null) { + throw new NotFoundException("Null username"); + } + + User user = userDAO.find(username); + if (user == null) { + throw new NotFoundException("User " + username); + } + + if (syncopeLogic.isPwdResetRequiringSecurityQuestions() + && (securityAnswer == null || !securityAnswer.equals(user.getSecurityAnswer()))) { + + throw SyncopeClientException.build(ClientExceptionType.InvalidSecurityAnswer); + } + + provisioningManager.requestPasswordReset(user.getKey()); + } + + @PreAuthorize("isAnonymous() or hasRole('" + Entitlement.ANONYMOUS + "')") + @Transactional + public void confirmPasswordReset(final String token, final String password) { + User user = userDAO.findByToken(token); + if (user == null) { + throw new NotFoundException("User with token " + token); + } + provisioningManager.confirmPasswordReset(user, token, password); + } + + @PreAuthorize("isAuthenticated() and not(hasRole('" + Entitlement.ANONYMOUS + "'))") + public UserTO deleteSelf() { + UserTO userTO = binder.getAuthenticatedUserTO(); + + return delete(userTO.getKey()); + } + + @PreAuthorize("hasRole('" + Entitlement.USER_DELETE + "')") + @Override + public UserTO delete(final Long key) { + List<Group> ownedGroups = groupDAO.findOwnedByUser(key); + if (!ownedGroups.isEmpty()) { + SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.GroupOwnership); + sce.getElements().addAll(CollectionUtils.collect(ownedGroups, new Transformer<Group, String>() { + + @Override + public String transform(final Group group) { + return group.getKey() + " " + group.getName(); + } + }, new ArrayList<String>())); + throw sce; + } + + List<PropagationStatus> statuses = provisioningManager.delete(key); + + final UserTO deletedTO; + User deleted = userDAO.find(key); + if (deleted == null) { + deletedTO = new UserTO(); + deletedTO.setKey(key); + } else { + deletedTO = binder.getUserTO(key); + } + deletedTO.getPropagationStatusTOs().addAll(statuses); + + return deletedTO; + } + + @PreAuthorize("hasRole('" + Entitlement.USER_UPDATE + "')") + @Transactional(rollbackFor = { Throwable.class }) + @Override + public UserTO unlink(final Long key, final Collection<String> resources) { + final UserMod userMod = new UserMod(); + userMod.setKey(key); + userMod.getResourcesToRemove().addAll(resources); + Long updatedKey = provisioningManager.unlink(userMod); + + return binder.getUserTO(updatedKey); + } + + @PreAuthorize("hasRole('" + Entitlement.USER_UPDATE + "')") + @Transactional(rollbackFor = { Throwable.class }) + @Override + public UserTO link(final Long key, final Collection<String> resources) { + final UserMod userMod = new UserMod(); + userMod.setKey(key); + userMod.getResourcesToAdd().addAll(resources); + return binder.getUserTO(provisioningManager.link(userMod)); + } + + @PreAuthorize("hasRole('" + Entitlement.USER_UPDATE + "')") + @Transactional(rollbackFor = { Throwable.class }) + @Override + public UserTO unassign(final Long key, final Collection<String> resources) { + final UserMod userMod = new UserMod(); + userMod.setKey(key); + userMod.getResourcesToRemove().addAll(resources); + return update(userMod); + } + + @PreAuthorize("hasRole('" + Entitlement.USER_UPDATE + "')") + @Transactional(rollbackFor = { Throwable.class }) + @Override + public UserTO assign( + final Long key, + final Collection<String> resources, + final boolean changepwd, + final String password) { + + final UserMod userMod = new UserMod(); + userMod.setKey(key); + userMod.getResourcesToAdd().addAll(resources); + + if (changepwd) { + StatusMod statusMod = new StatusMod(); + statusMod.setOnSyncope(false); + statusMod.getResourceNames().addAll(resources); + userMod.setPwdPropRequest(statusMod); + userMod.setPassword(password); + } + + return update(userMod); + } + + @PreAuthorize("hasRole('" + Entitlement.USER_UPDATE + "')") + @Transactional(rollbackFor = { Throwable.class }) + @Override + public UserTO deprovision(final Long key, final Collection<String> resources) { - final User user = userDAO.authFind(key); ++ User user = userDAO.authFind(key); + + List<PropagationStatus> statuses = provisioningManager.deprovision(key, resources); + - final UserTO updatedUserTO = binder.getUserTO(user); ++ final UserTO updatedUserTO = binder.getUserTO(user, true); + updatedUserTO.getPropagationStatusTOs().addAll(statuses); + return updatedUserTO; + } + + @PreAuthorize("hasRole('" + Entitlement.USER_UPDATE + "')") + @Transactional(readOnly = true) + @Override + public UserTO provision( + final Long key, + final Collection<String> resources, + final boolean changePwd, + final String password) { + + final UserTO original = binder.getUserTO(key); + + //trick: assign and retrieve propagation statuses ... + original.getPropagationStatusTOs().addAll( + assign(key, resources, changePwd, password).getPropagationStatusTOs()); + + // .... rollback. + TransactionInterceptor.currentTransactionStatus().setRollbackOnly(); + return original; + } + + @Override + protected UserTO resolveReference(final Method method, final Object... args) throws UnresolvedReferenceException { + Object key = null; + + if (!"confirmPasswordReset".equals(method.getName()) && ArrayUtils.isNotEmpty(args)) { + for (int i = 0; key == null && i < args.length; i++) { + if (args[i] instanceof Long) { + key = (Long) args[i]; + } else if (args[i] instanceof String) { + key = (String) args[i]; + } else if (args[i] instanceof UserTO) { + key = ((UserTO) args[i]).getKey(); + } else if (args[i] instanceof UserMod) { + key = ((UserMod) args[i]).getKey(); + } + } + } + + if ((key != null) && !key.equals(0L)) { + try { + return key instanceof Long ? binder.getUserTO((Long) key) : binder.getUserTO((String) key); + } catch (Throwable ignore) { + LOG.debug("Unresolved reference", ignore); + throw new UnresolvedReferenceException(ignore); + } + } + + throw new UnresolvedReferenceException(); + } +} http://git-wip-us.apache.org/repos/asf/syncope/blob/97607b16/core/logic/src/main/java/org/apache/syncope/core/logic/report/GroupReportlet.java ---------------------------------------------------------------------- diff --cc core/logic/src/main/java/org/apache/syncope/core/logic/report/GroupReportlet.java index 72bfb33,0000000..5b89421 mode 100644,000000..100644 --- a/core/logic/src/main/java/org/apache/syncope/core/logic/report/GroupReportlet.java +++ b/core/logic/src/main/java/org/apache/syncope/core/logic/report/GroupReportlet.java @@@ -1,305 -1,0 +1,305 @@@ +/* + * 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.logic.report; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import org.apache.commons.lang3.StringUtils; +import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.common.lib.report.GroupReportletConf; +import org.apache.syncope.common.lib.report.GroupReportletConf.Feature; +import org.apache.syncope.common.lib.to.AnyTO; +import org.apache.syncope.common.lib.to.AttrTO; +import org.apache.syncope.common.lib.to.GroupTO; +import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.apache.syncope.core.persistence.api.dao.GroupDAO; +import org.apache.syncope.core.persistence.api.dao.search.OrderByClause; +import org.apache.syncope.core.persistence.api.entity.group.Group; +import org.apache.syncope.core.misc.search.SearchCondConverter; +import org.apache.syncope.core.persistence.api.dao.AnySearchDAO; +import org.apache.syncope.core.persistence.api.entity.user.UMembership; +import org.apache.syncope.core.provisioning.api.data.GroupDataBinder; +import org.springframework.beans.factory.annotation.Autowired; +import org.xml.sax.ContentHandler; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.AttributesImpl; + +@ReportletConfClass(GroupReportletConf.class) +public class GroupReportlet extends AbstractReportlet<GroupReportletConf> { + + private static final int PAGE_SIZE = 10; + + @Autowired + private GroupDAO groupDAO; + + @Autowired + private AnySearchDAO searchDAO; + + @Autowired + private GroupDataBinder groupDataBinder; + + private List<Group> getPagedGroups(final int page) { + List<Group> result; + + if (StringUtils.isBlank(conf.getMatchingCond())) { + result = groupDAO.findAll(SyncopeConstants.FULL_ADMIN_REALMS, page, PAGE_SIZE); + } else { + result = searchDAO.search(SyncopeConstants.FULL_ADMIN_REALMS, + SearchCondConverter.convert(conf.getMatchingCond()), + page, PAGE_SIZE, Collections.<OrderByClause>emptyList(), AnyTypeKind.GROUP); + } + + return result; + } + + private int count() { + return StringUtils.isBlank(conf.getMatchingCond()) + ? groupDAO.count(SyncopeConstants.FULL_ADMIN_REALMS) + : searchDAO.count(SyncopeConstants.FULL_ADMIN_REALMS, + SearchCondConverter.convert(conf.getMatchingCond()), AnyTypeKind.GROUP); + } + + private void doExtractResources(final ContentHandler handler, final AnyTO anyTO) + throws SAXException { + + if (anyTO.getResources().isEmpty()) { + LOG.debug("No resources found for {}[{}]", anyTO.getClass().getSimpleName(), anyTO.getKey()); + } else { + AttributesImpl atts = new AttributesImpl(); + handler.startElement("", "", "resources", null); + + for (String resourceName : anyTO.getResources()) { + atts.clear(); + + atts.addAttribute("", "", ReportXMLConst.ATTR_NAME, ReportXMLConst.XSD_STRING, resourceName); + handler.startElement("", "", "resource", atts); + handler.endElement("", "", "resource"); + } + + handler.endElement("", "", "resources"); + } + } + + private void doExtractAttributes(final ContentHandler handler, final AnyTO anyTO, + final Collection<String> attrs, final Collection<String> derAttrs, final Collection<String> virAttrs) + throws SAXException { + + AttributesImpl atts = new AttributesImpl(); + if (!attrs.isEmpty()) { + Map<String, AttrTO> attrMap = anyTO.getPlainAttrMap(); + + handler.startElement("", "", "attributes", null); + for (String attrName : attrs) { + atts.clear(); + + atts.addAttribute("", "", ReportXMLConst.ATTR_NAME, ReportXMLConst.XSD_STRING, attrName); + handler.startElement("", "", "attribute", atts); + + if (attrMap.containsKey(attrName)) { + for (String value : attrMap.get(attrName).getValues()) { + handler.startElement("", "", "value", null); + handler.characters(value.toCharArray(), 0, value.length()); + handler.endElement("", "", "value"); + } + } else { + LOG.debug("{} not found for {}[{}]", attrName, + anyTO.getClass().getSimpleName(), anyTO.getKey()); + } + + handler.endElement("", "", "attribute"); + } + handler.endElement("", "", "attributes"); + } + + if (!derAttrs.isEmpty()) { + Map<String, AttrTO> derAttrMap = anyTO.getDerAttrMap(); + + handler.startElement("", "", "derivedAttributes", null); + for (String attrName : derAttrs) { + atts.clear(); + + atts.addAttribute("", "", ReportXMLConst.ATTR_NAME, ReportXMLConst.XSD_STRING, attrName); + handler.startElement("", "", "derivedAttribute", atts); + + if (derAttrMap.containsKey(attrName)) { + for (String value : derAttrMap.get(attrName).getValues()) { + handler.startElement("", "", "value", null); + handler.characters(value.toCharArray(), 0, value.length()); + handler.endElement("", "", "value"); + } + } else { + LOG.debug("{} not found for {}[{}]", attrName, + anyTO.getClass().getSimpleName(), anyTO.getKey()); + } + + handler.endElement("", "", "derivedAttribute"); + } + handler.endElement("", "", "derivedAttributes"); + } + + if (!virAttrs.isEmpty()) { + Map<String, AttrTO> virAttrMap = anyTO.getVirAttrMap(); + + handler.startElement("", "", "virtualAttributes", null); + for (String attrName : virAttrs) { + atts.clear(); + + atts.addAttribute("", "", ReportXMLConst.ATTR_NAME, ReportXMLConst.XSD_STRING, attrName); + handler.startElement("", "", "virtualAttribute", atts); + + if (virAttrMap.containsKey(attrName)) { + for (String value : virAttrMap.get(attrName).getValues()) { + handler.startElement("", "", "value", null); + handler.characters(value.toCharArray(), 0, value.length()); + handler.endElement("", "", "value"); + } + } else { + LOG.debug("{} not found for {}[{}]", attrName, + anyTO.getClass().getSimpleName(), anyTO.getKey()); + } + + handler.endElement("", "", "virtualAttribute"); + } + handler.endElement("", "", "virtualAttributes"); + } + } + + private void doExtract(final ContentHandler handler, final List<Group> groups) throws SAXException { + AttributesImpl atts = new AttributesImpl(); + for (Group group : groups) { + atts.clear(); + + for (Feature feature : conf.getFeatures()) { + String type = null; + String value = null; + switch (feature) { + case key: + type = ReportXMLConst.XSD_LONG; + value = String.valueOf(group.getKey()); + break; + + case name: + type = ReportXMLConst.XSD_STRING; + value = String.valueOf(group.getName()); + break; + + case groupOwner: + type = ReportXMLConst.XSD_LONG; + value = String.valueOf(group.getGroupOwner()); + break; + + case userOwner: + type = ReportXMLConst.XSD_LONG; + value = String.valueOf(group.getUserOwner()); + break; + + default: + } + + if (type != null && value != null) { + atts.addAttribute("", "", feature.name(), type, value); + } + } + + handler.startElement("", "", "group", atts); + + // Using GroupTO for attribute values, since the conversion logic of + // values to String is already encapsulated there - GroupTO groupTO = groupDataBinder.getGroupTO(group); ++ GroupTO groupTO = groupDataBinder.getGroupTO(group, true); + + doExtractAttributes(handler, groupTO, conf.getPlainAttrs(), conf.getDerAttrs(), conf.getVirAttrs()); + + // to get resources associated to a group + if (conf.getFeatures().contains(Feature.resources)) { + doExtractResources(handler, groupTO); + } + //to get users asscoiated to a group is preferred GroupDAO to GroupTO + if (conf.getFeatures().contains(Feature.users)) { + handler.startElement("", "", "users", null); + + for (UMembership memb : groupDAO.findUMemberships(group)) { + atts.clear(); + + atts.addAttribute("", "", "key", ReportXMLConst.XSD_LONG, + String.valueOf(memb.getLeftEnd().getKey())); + atts.addAttribute("", "", "username", ReportXMLConst.XSD_STRING, + String.valueOf(memb.getLeftEnd().getUsername())); + + handler.startElement("", "", "user", atts); + handler.endElement("", "", "user"); + } + + handler.endElement("", "", "users"); + } + + handler.endElement("", "", "group"); + } + } + + private void doExtractConf(final ContentHandler handler) throws SAXException { + if (conf == null) { + LOG.debug("Report configuration is not present"); + } + + AttributesImpl atts = new AttributesImpl(); + handler.startElement("", "", "configurations", null); + handler.startElement("", "", "groupAttributes", atts); + + for (Feature feature : conf.getFeatures()) { + atts.clear(); + handler.startElement("", "", "feature", atts); + handler.characters(feature.name().toCharArray(), 0, feature.name().length()); + handler.endElement("", "", "feature"); + } + + for (String attr : conf.getPlainAttrs()) { + atts.clear(); + handler.startElement("", "", "attribute", atts); + handler.characters(attr.toCharArray(), 0, attr.length()); + handler.endElement("", "", "attribute"); + } + + for (String derAttr : conf.getDerAttrs()) { + atts.clear(); + handler.startElement("", "", "derAttribute", atts); + handler.characters(derAttr.toCharArray(), 0, derAttr.length()); + handler.endElement("", "", "derAttribute"); + } + + for (String virAttr : conf.getVirAttrs()) { + atts.clear(); + handler.startElement("", "", "virAttribute", atts); + handler.characters(virAttr.toCharArray(), 0, virAttr.length()); + handler.endElement("", "", "virAttribute"); + } + + handler.endElement("", "", "groupAttributes"); + handler.endElement("", "", "configurations"); + } + + @Override + protected void doExtract(final ContentHandler handler) throws SAXException { + doExtractConf(handler); + for (int i = 1; i <= (count() / PAGE_SIZE) + 1; i++) { + doExtract(handler, getPagedGroups(i)); + } + } +} http://git-wip-us.apache.org/repos/asf/syncope/blob/97607b16/core/logic/src/main/java/org/apache/syncope/core/logic/report/UserReportlet.java ---------------------------------------------------------------------- diff --cc core/logic/src/main/java/org/apache/syncope/core/logic/report/UserReportlet.java index b05c4e6,0000000..b626dd1 mode 100644,000000..100644 --- a/core/logic/src/main/java/org/apache/syncope/core/logic/report/UserReportlet.java +++ b/core/logic/src/main/java/org/apache/syncope/core/logic/report/UserReportlet.java @@@ -1,378 -1,0 +1,379 @@@ +/* + * 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.logic.report; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import org.apache.commons.lang3.StringUtils; +import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.common.lib.report.UserReportletConf; +import org.apache.syncope.common.lib.report.UserReportletConf.Feature; +import org.apache.syncope.common.lib.to.AnyTO; +import org.apache.syncope.common.lib.to.AttrTO; +import org.apache.syncope.common.lib.to.MembershipTO; +import org.apache.syncope.common.lib.to.RelationshipTO; +import org.apache.syncope.common.lib.to.UserTO; +import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.apache.syncope.core.persistence.api.dao.UserDAO; +import org.apache.syncope.core.persistence.api.dao.search.OrderByClause; +import org.apache.syncope.core.persistence.api.entity.user.User; +import org.apache.syncope.core.misc.search.SearchCondConverter; +import org.apache.syncope.core.misc.DataFormat; +import org.apache.syncope.core.persistence.api.dao.AnySearchDAO; +import org.apache.syncope.core.persistence.api.entity.user.UMembership; +import org.apache.syncope.core.persistence.api.entity.user.URelationship; +import org.apache.syncope.core.provisioning.api.data.AnyObjectDataBinder; +import org.apache.syncope.core.provisioning.api.data.GroupDataBinder; +import org.apache.syncope.core.provisioning.api.data.UserDataBinder; +import org.springframework.beans.factory.annotation.Autowired; +import org.xml.sax.ContentHandler; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.AttributesImpl; + +@ReportletConfClass(UserReportletConf.class) +public class UserReportlet extends AbstractReportlet<UserReportletConf> { + + private static final int PAGE_SIZE = 10; + + @Autowired + private UserDAO userDAO; + + @Autowired + private AnySearchDAO searchDAO; + + @Autowired + private UserDataBinder userDataBinder; + + @Autowired + private GroupDataBinder groupDataBinder; + + @Autowired + private AnyObjectDataBinder anyObjectDataBinder; + + private List<User> getPagedUsers(final int page) { + List<User> result; + + if (StringUtils.isBlank(conf.getMatchingCond())) { + result = userDAO.findAll(SyncopeConstants.FULL_ADMIN_REALMS, page, PAGE_SIZE); + } else { + result = searchDAO.search(SyncopeConstants.FULL_ADMIN_REALMS, + SearchCondConverter.convert(conf.getMatchingCond()), + page, PAGE_SIZE, Collections.<OrderByClause>emptyList(), AnyTypeKind.USER); + } + + return result; + } + + private int count() { + return StringUtils.isBlank(conf.getMatchingCond()) + ? userDAO.count(SyncopeConstants.FULL_ADMIN_REALMS) + : searchDAO.count(SyncopeConstants.FULL_ADMIN_REALMS, + SearchCondConverter.convert(conf.getMatchingCond()), AnyTypeKind.USER); + } + + private void doExtractResources(final ContentHandler handler, final AnyTO anyTO) + throws SAXException { + + if (anyTO.getResources().isEmpty()) { + LOG.debug("No resources found for {}[{}]", anyTO.getClass().getSimpleName(), anyTO.getKey()); + } else { + AttributesImpl atts = new AttributesImpl(); + handler.startElement("", "", "resources", null); + + for (String resourceName : anyTO.getResources()) { + atts.clear(); + + atts.addAttribute("", "", ReportXMLConst.ATTR_NAME, ReportXMLConst.XSD_STRING, resourceName); + handler.startElement("", "", "resource", atts); + handler.endElement("", "", "resource"); + } + + handler.endElement("", "", "resources"); + } + } + + private void doExtractAttributes(final ContentHandler handler, final AnyTO anyTO, + final Collection<String> attrs, final Collection<String> derAttrs, final Collection<String> virAttrs) + throws SAXException { + + AttributesImpl atts = new AttributesImpl(); + if (!attrs.isEmpty()) { + Map<String, AttrTO> attrMap = anyTO.getPlainAttrMap(); + + handler.startElement("", "", "attributes", null); + for (String attrName : attrs) { + atts.clear(); + + atts.addAttribute("", "", ReportXMLConst.ATTR_NAME, ReportXMLConst.XSD_STRING, attrName); + handler.startElement("", "", "attribute", atts); + + if (attrMap.containsKey(attrName)) { + for (String value : attrMap.get(attrName).getValues()) { + handler.startElement("", "", "value", null); + handler.characters(value.toCharArray(), 0, value.length()); + handler.endElement("", "", "value"); + } + } else { + LOG.debug("{} not found for {}[{}]", attrName, + anyTO.getClass().getSimpleName(), anyTO.getKey()); + } + + handler.endElement("", "", "attribute"); + } + handler.endElement("", "", "attributes"); + } + + if (!derAttrs.isEmpty()) { + Map<String, AttrTO> derAttrMap = anyTO.getDerAttrMap(); + + handler.startElement("", "", "derivedAttributes", null); + for (String attrName : derAttrs) { + atts.clear(); + + atts.addAttribute("", "", ReportXMLConst.ATTR_NAME, ReportXMLConst.XSD_STRING, attrName); + handler.startElement("", "", "derivedAttribute", atts); + + if (derAttrMap.containsKey(attrName)) { + for (String value : derAttrMap.get(attrName).getValues()) { + handler.startElement("", "", "value", null); + handler.characters(value.toCharArray(), 0, value.length()); + handler.endElement("", "", "value"); + } + } else { + LOG.debug("{} not found for {}[{}]", attrName, + anyTO.getClass().getSimpleName(), anyTO.getKey()); + } + + handler.endElement("", "", "derivedAttribute"); + } + handler.endElement("", "", "derivedAttributes"); + } + + if (!virAttrs.isEmpty()) { + Map<String, AttrTO> virAttrMap = anyTO.getVirAttrMap(); + + handler.startElement("", "", "virtualAttributes", null); + for (String attrName : virAttrs) { + atts.clear(); + + atts.addAttribute("", "", ReportXMLConst.ATTR_NAME, ReportXMLConst.XSD_STRING, attrName); + handler.startElement("", "", "virtualAttribute", atts); + + if (virAttrMap.containsKey(attrName)) { + for (String value : virAttrMap.get(attrName).getValues()) { + handler.startElement("", "", "value", null); + handler.characters(value.toCharArray(), 0, value.length()); + handler.endElement("", "", "value"); + } + } else { + LOG.debug("{} not found for {}[{}]", attrName, + anyTO.getClass().getSimpleName(), anyTO.getKey()); + } + + handler.endElement("", "", "virtualAttribute"); + } + handler.endElement("", "", "virtualAttributes"); + } + } + + private void doExtract(final ContentHandler handler, final List<User> users) throws SAXException { + AttributesImpl atts = new AttributesImpl(); + for (User user : users) { + atts.clear(); + + for (Feature feature : conf.getFeatures()) { + String type = null; + String value = null; + switch (feature) { + case key: + type = ReportXMLConst.XSD_LONG; + value = String.valueOf(user.getKey()); + break; + + case username: + type = ReportXMLConst.XSD_STRING; + value = user.getUsername(); + break; + + case workflowId: + type = ReportXMLConst.XSD_LONG; + value = String.valueOf(user.getWorkflowId()); + break; + + case status: + type = ReportXMLConst.XSD_STRING; + value = user.getStatus(); + break; + + case creationDate: + type = ReportXMLConst.XSD_DATETIME; + value = user.getCreationDate() == null + ? "" + : DataFormat.format(user.getCreationDate()); + break; + + case lastLoginDate: + type = ReportXMLConst.XSD_DATETIME; + value = user.getLastLoginDate() == null + ? "" + : DataFormat.format(user.getLastLoginDate()); + break; + + case changePwdDate: + type = ReportXMLConst.XSD_DATETIME; + value = user.getChangePwdDate() == null + ? "" + : DataFormat.format(user.getChangePwdDate()); + break; + + case passwordHistorySize: + type = ReportXMLConst.XSD_INT; + value = String.valueOf(user.getPasswordHistory().size()); + break; + + case failedLoginCount: + type = ReportXMLConst.XSD_INT; + value = String.valueOf(user.getFailedLogins()); + break; + + default: + } + + if (type != null && value != null) { + atts.addAttribute("", "", feature.name(), type, value); + } + } + + handler.startElement("", "", "user", atts); + + // Using UserTO for attribute values, since the conversion logic of + // values to String is already encapsulated there - UserTO userTO = userDataBinder.getUserTO(user); ++ UserTO userTO = userDataBinder.getUserTO(user, true); + + doExtractAttributes(handler, userTO, conf.getPlainAttrs(), conf.getDerAttrs(), conf.getVirAttrs()); + + if (conf.getFeatures().contains(Feature.relationships)) { + handler.startElement("", "", "relationships", null); + + for (RelationshipTO rel : userTO.getRelationships()) { + atts.clear(); + + atts.addAttribute("", "", "anyObjectKey", + ReportXMLConst.XSD_LONG, String.valueOf(rel.getRightKey())); + handler.startElement("", "", "relationship", atts); + + if (conf.getFeatures().contains(Feature.resources)) { + URelationship actualRel = user.getRelationship(rel.getRightKey()); + if (actualRel == null) { + LOG.warn("Unexpected: cannot find relationship for any object {} for user {}", + rel.getRightKey(), user); + } else { - doExtractResources(handler, anyObjectDataBinder.getAnyObjectTO(actualRel.getRightEnd())); ++ doExtractResources( ++ handler, anyObjectDataBinder.getAnyObjectTO(actualRel.getRightEnd(), true)); + } + } + + handler.endElement("", "", "relationship"); + } + + handler.endElement("", "", "relationships"); + } + if (conf.getFeatures().contains(Feature.memberships)) { + handler.startElement("", "", "memberships", null); + + for (MembershipTO memb : userTO.getMemberships()) { + atts.clear(); + + atts.addAttribute("", "", "groupKey", + ReportXMLConst.XSD_LONG, String.valueOf(memb.getRightKey())); + atts.addAttribute("", "", "groupName", ReportXMLConst.XSD_STRING, String. + valueOf(memb.getGroupName())); + handler.startElement("", "", "membership", atts); + + if (conf.getFeatures().contains(Feature.resources)) { + UMembership actualMemb = user.getMembership(memb.getRightKey()); + if (actualMemb == null) { + LOG.warn("Unexpected: cannot find membership for group {} for user {}", + memb.getRightKey(), user); + } else { - doExtractResources(handler, groupDataBinder.getGroupTO(actualMemb.getRightEnd())); ++ doExtractResources(handler, groupDataBinder.getGroupTO(actualMemb.getRightEnd(), true)); + } + } + + handler.endElement("", "", "membership"); + } + + handler.endElement("", "", "memberships"); + } + + if (conf.getFeatures().contains(Feature.resources)) { + doExtractResources(handler, userTO); + } + + handler.endElement("", "", "user"); + } + } + + private void doExtractConf(final ContentHandler handler) throws SAXException { + AttributesImpl atts = new AttributesImpl(); + handler.startElement("", "", "configurations", null); + handler.startElement("", "", "userAttributes", atts); + + for (Feature feature : conf.getFeatures()) { + atts.clear(); + handler.startElement("", "", "feature", atts); + handler.characters(feature.name().toCharArray(), 0, feature.name().length()); + handler.endElement("", "", "feature"); + } + + for (String attr : conf.getPlainAttrs()) { + atts.clear(); + handler.startElement("", "", "attribute", atts); + handler.characters(attr.toCharArray(), 0, attr.length()); + handler.endElement("", "", "attribute"); + } + + for (String derAttr : conf.getDerAttrs()) { + atts.clear(); + handler.startElement("", "", "derAttribute", atts); + handler.characters(derAttr.toCharArray(), 0, derAttr.length()); + handler.endElement("", "", "derAttribute"); + } + + for (String virAttr : conf.getVirAttrs()) { + atts.clear(); + handler.startElement("", "", "virAttribute", atts); + handler.characters(virAttr.toCharArray(), 0, virAttr.length()); + handler.endElement("", "", "virAttribute"); + } + + handler.endElement("", "", "userAttributes"); + handler.endElement("", "", "configurations"); + } + + @Override + protected void doExtract(final ContentHandler handler) throws SAXException { + doExtractConf(handler); + for (int i = 1; i <= (count() / PAGE_SIZE) + 1; i++) { + doExtract(handler, getPagedUsers(i)); + } + } +}
