syncope-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ilgro...@apache.org
Subject [6/7] syncope git commit: [SYNCOPE-676] Merge from 1_2_X
Date Tue, 07 Jul 2015 07:38:00 GMT
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));
 +        }
 +    }
 +}


Mime
View raw message