biz.netcentric.cq.tools.actool.dumpservice.impl.DumpserviceImpl.java Source code

Java tutorial

Introduction

Here is the source code for biz.netcentric.cq.tools.actool.dumpservice.impl.DumpserviceImpl.java

Source

/*
d * (C) Copyright 2015 Netcentric AG.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 */
package biz.netcentric.cq.tools.actool.dumpservice.impl;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;

import javax.jcr.AccessDeniedException;
import javax.jcr.ItemExistsException;
import javax.jcr.ItemNotFoundException;
import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.PathNotFoundException;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.UnsupportedRepositoryOperationException;
import javax.jcr.ValueFormatException;
import javax.jcr.lock.LockException;
import javax.jcr.nodetype.ConstraintViolationException;
import javax.jcr.nodetype.NoSuchNodeTypeException;
import javax.jcr.security.AccessControlEntry;
import javax.jcr.version.VersionException;
import javax.servlet.ServletOutputStream;

import org.apache.commons.lang.StringUtils;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Modified;
import org.apache.felix.scr.annotations.Properties;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.Service;
import org.apache.jackrabbit.api.JackrabbitSession;
import org.apache.jackrabbit.api.security.JackrabbitAccessControlEntry;
import org.apache.jackrabbit.api.security.user.Authorizable;
import org.apache.jackrabbit.api.security.user.Group;
import org.apache.jackrabbit.api.security.user.Query;
import org.apache.jackrabbit.api.security.user.QueryBuilder;
import org.apache.jackrabbit.api.security.user.QueryBuilder.Direction;
import org.apache.jackrabbit.api.security.user.User;
import org.apache.jackrabbit.api.security.user.UserManager;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.resource.ResourceResolverFactory;
import org.apache.sling.commons.osgi.PropertiesUtil;
import org.apache.sling.jcr.api.SlingRepository;
import org.osgi.service.component.ComponentContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import biz.netcentric.cq.tools.actool.comparators.AcePathComparator;
import biz.netcentric.cq.tools.actool.comparators.AcePermissionComparator;
import biz.netcentric.cq.tools.actool.comparators.AuthorizableBeanIDComparator;
import biz.netcentric.cq.tools.actool.comparators.JcrCreatedComparator;
import biz.netcentric.cq.tools.actool.configmodel.AceBean;
import biz.netcentric.cq.tools.actool.configmodel.AuthorizableConfigBean;
import biz.netcentric.cq.tools.actool.dumpservice.AcDumpElementYamlVisitor;
import biz.netcentric.cq.tools.actool.dumpservice.AceDumpData;
import biz.netcentric.cq.tools.actool.dumpservice.CompleteAcDump;
import biz.netcentric.cq.tools.actool.dumpservice.Dumpservice;
import biz.netcentric.cq.tools.actool.helper.AcHelper;
import biz.netcentric.cq.tools.actool.helper.AccessControlUtils;
import biz.netcentric.cq.tools.actool.helper.AceWrapper;
import biz.netcentric.cq.tools.actool.helper.AclBean;
import biz.netcentric.cq.tools.actool.helper.Constants;
import biz.netcentric.cq.tools.actool.helper.QueryHelper;
import biz.netcentric.cq.tools.actool.installationhistory.impl.HistoryUtils;

@Service
@Component(metatype = true, label = "AC Dump Service", description = "Service that creates dumps of the current AC configurations (groups&ACEs)")
@Properties({

        @Property(label = "Number of dumps to save", name = DumpserviceImpl.DUMP_SERVICE_NR_OF_SAVED_DUMPS, value = "5", description = "number of last dumps which get saved in CRX under /var/statistics/achistory"),
        @Property(label = "Include user ACEs in dumps", name = DumpserviceImpl.DUMP_INCLUDE_USERS, boolValue = false, description = "if selected, also user based ACEs (and their respective users) get added to dumps"),
        @Property(label = "AC query exclude paths", name = DumpserviceImpl.DUMP_SERVICE_EXCLUDE_PATHS_PATH, value = {
                "/home", "/jcr:system",
                "/tmp" }, description = "direct children of jcr:root which get excluded from all dumps (also from internal dumps)") })
public class DumpserviceImpl implements Dumpservice {

    private static final Logger LOG = LoggerFactory.getLogger(DumpserviceImpl.class);

    private static final String DUMP_FILE_EXTENSION = ".yaml";
    private static final String DUMP_NODE_PREFIX = "dump_";

    public final static int PRINCIPAL_BASED_SORTING = 1;
    public final static int PATH_BASED_SORTING = 2;

    public final static int DENY_ALLOW_ACL_SORTING = 1;
    public final static int NO_ACL_SORTING = 2;

    protected static final int NR_OF_DUMPS_TO_SAVE_DEFAULT = 5;
    static final String DUMP_SERVICE_EXCLUDE_PATHS_PATH = "DumpService.queryExcludePaths";
    static final String DUMP_SERVICE_NR_OF_SAVED_DUMPS = "DumpService.nrOfSavedDumps";
    static final String DUMP_INCLUDE_USERS = "DumpService.includeUsers";

    private String[] queryExcludePaths;
    private int nrOfSavedDumps;
    private boolean includeUsersInDumps = false;

    @Reference
    private SlingRepository repository;

    @Reference
    private ResourceResolverFactory resourceResolverFactory;

    @Activate
    public void activate(@SuppressWarnings("rawtypes") final Map properties, final ComponentContext context)
            throws Exception {
        updateProperties(properties, context);
    }

    @Modified
    public void modified(@SuppressWarnings("rawtypes") final Map properties, final ComponentContext context)
            throws Exception {
        updateProperties(properties, context);
    }

    private void updateProperties(final Map properties, final ComponentContext context) {
        queryExcludePaths = PropertiesUtil.toStringArray(properties.get(DUMP_SERVICE_EXCLUDE_PATHS_PATH), null);
        nrOfSavedDumps = PropertiesUtil.toInteger(properties.get(DUMP_SERVICE_NR_OF_SAVED_DUMPS),
                NR_OF_DUMPS_TO_SAVE_DEFAULT);
        includeUsersInDumps = PropertiesUtil.toBoolean(properties.get(DUMP_INCLUDE_USERS), false);

    }

    @Override
    public boolean isIncludeUsers() {
        return includeUsersInDumps;
    }

    @Override
    public String[] getQueryExcludePaths() {
        return queryExcludePaths;
    }

    @Override
    public String getCompletePathBasedDumpsAsString() {
        String dump = getCompleteDump(AcHelper.PATH_BASED_ORDER, AcHelper.ACE_ORDER_NONE);
        persistDump(dump);
        return dump;
    }

    @Override
    public String getCompletePrincipalBasedDumpsAsString() {
        String dump = getCompleteDump(AcHelper.PRINCIPAL_BASED_ORDER, AcHelper.ACE_ORDER_ACTOOL_BEST_PRACTICE);
        persistDump(dump);
        return dump;
    }

    private void persistDump(String dump) {
        Session session = null;
        try {
            session = repository.loginAdministrative(null);
            Node rootNode = HistoryUtils.getAcHistoryRootNode(session);
            createTransientDumpNode(dump, rootNode);
            session.save();
        } catch (RepositoryException e) {
            LOG.error("RepositoryException: {}", e);
        } finally {
            if (session != null) {
                session.logout();
            }
        }
    }

    private void createTransientDumpNode(String dump, Node rootNode)
            throws ItemExistsException, PathNotFoundException, NoSuchNodeTypeException, LockException,
            VersionException, ConstraintViolationException, RepositoryException, ValueFormatException {

        NodeIterator nodeIt = rootNode.getNodes();

        // TreeSet used here since only this type offers the methods first() and
        // last()
        TreeSet<Node> dumpNodes = new TreeSet<Node>(new JcrCreatedComparator());

        Node previousDumpNode = null;

        // get all dump nodes
        while (nodeIt.hasNext()) {
            Node currNode = nodeIt.nextNode();

            if (currNode.getName().startsWith(DUMP_NODE_PREFIX)) {
                dumpNodes.add(currNode);
            }
        }
        // try to get previous dump node
        if (!dumpNodes.isEmpty()) {
            previousDumpNode = dumpNodes.first();
        }
        // is limit of dump nodes to save reached?
        if (dumpNodes.size() > (nrOfSavedDumps - 1)) {
            Node oldestDumpNode = dumpNodes.last();
            oldestDumpNode.remove();
        }
        Node dumpNode = getNewDumpNode(dump, rootNode);

        // order the newest dump node as first child node of ac root node
        if (previousDumpNode != null) {
            rootNode.orderBefore(dumpNode.getName(), previousDumpNode.getName());
        }
    }

    private Node getNewDumpNode(String dump, Node rootNode)
            throws ItemExistsException, PathNotFoundException, NoSuchNodeTypeException, LockException,
            VersionException, ConstraintViolationException, RepositoryException, ValueFormatException {
        // create necessary child node&properties
        long timestamp = System.currentTimeMillis();
        Node dumpNode = rootNode.addNode(DUMP_NODE_PREFIX + timestamp + DUMP_FILE_EXTENSION, "nt:file");

        Node dumpJcrContenNodet = dumpNode.addNode("jcr:content", "nt:resource");
        dumpJcrContenNodet.setProperty("jcr:mimeType", "text/plain");
        dumpJcrContenNodet.setProperty("jcr:encoding", "utf-8");
        dumpJcrContenNodet.setProperty("jcr:data", dump);
        return dumpNode;
    }

    /** returns the complete AC dump (groups&ACEs) as String in YAML format
     *
     * @param keyOrder either principals (AcHelper.PRINCIPAL_BASED_ORDER) or node paths (AcHelper.PATH_BASED_ORDER) as keys
     * @param aclOrderInMap specifies whether the allow and deny ACEs within an ACL should be divided in separate blocks (first deny then allow)
     * @return String containing complete AC dump */
    private String getCompleteDump(int keyOrder, int aclOrderInMap) {
        Session session = null;

        LOG.info("Starting to create dump for "
                + (keyOrder == AcHelper.PRINCIPAL_BASED_ORDER ? "PRINCIPAL_BASED_ORDER" : "PATH_BASED_ORDER"));
        try {

            AceDumpData aceDumpData = createAclDumpMap(keyOrder, AcHelper.ACE_ORDER_ACTOOL_BEST_PRACTICE, // this ORDER is important to keep the ORDER of denies with "keepOrder"
                    // attribute that is automatically added if needed
                    queryExcludePaths);
            Map<String, Set<AceBean>> aclDumpMap = aceDumpData.getAceDump();

            session = repository.loginAdministrative(null);
            Set<AuthorizableConfigBean> groupBeans = getGroupBeans(session);
            Set<User> usersFromACEs = getUsersFromAces(keyOrder, session, aclDumpMap);
            Set<AuthorizableConfigBean> userBeans = getUserBeans(usersFromACEs);

            String configurationDumpAsString = getConfigurationDumpAsString(aceDumpData, groupBeans, userBeans,
                    aclOrderInMap);
            return configurationDumpAsString;

        } catch (IllegalStateException e) {
            LOG.error("IllegalStateException in DumpServiceImpl: {}", e);
        } catch (IOException e) {
            LOG.error("IOException in AceServiceImpl: {}", e);
        } catch (RepositoryException e) {
            LOG.error("RepositoryException in AceServiceImpl: {}", e);
        } finally {
            if (session != null) {
                session.logout();
            }
        }
        return null;
    }

    /** get users from ACEs
     *
     * @param mapOrder
     * @param session
     * @param aclDumpMap
     * @return
     * @throws AccessDeniedException
     * @throws UnsupportedRepositoryOperationException
     * @throws RepositoryException */
    private Set<User> getUsersFromAces(int mapOrder, Session session, Map<String, Set<AceBean>> aclDumpMap)
            throws AccessDeniedException, UnsupportedRepositoryOperationException, RepositoryException {

        Set<User> usersFromACEs = new HashSet<User>();
        UserManager um = ((JackrabbitSession) session).getUserManager();

        // if we have a principal ordered ACE map, all authorizables are
        // contained in the keySet of the map
        if (mapOrder == PRINCIPAL_BASED_SORTING) {
            Set<String> userIds = new HashSet<String>();
            userIds = aclDumpMap.keySet();

            for (String id : userIds) {
                Authorizable authorizable = um.getAuthorizable(id);
                if (!authorizable.isGroup()) {
                    User user = (User) authorizable;
                    usersFromACEs.add(user);
                }
            }
            // if we have a path ordered ACE map, all authorizables are
            // contained in AceBean properties
        } else if (mapOrder == PATH_BASED_SORTING) {
            for (Map.Entry<String, Set<AceBean>> entry : aclDumpMap.entrySet()) {
                Set<AceBean> aceBeanSet = entry.getValue();

                for (AceBean aceBean : aceBeanSet) {
                    String principalId = aceBean.getPrincipalName();
                    Authorizable authorizable = um.getAuthorizable(principalId);
                    if (!authorizable.isGroup()) {
                        User user = (User) authorizable;
                        usersFromACEs.add(user);
                    }
                }
            }
        }
        return usersFromACEs;
    }

    @Override
    public String getConfigurationDumpAsString(AceDumpData aceDumpData, final Set<AuthorizableConfigBean> groupSet,
            final Set<AuthorizableConfigBean> userSet, final int mapOrder) throws IOException {
        StringBuilder sb = new StringBuilder(20000);

        // add creation date as first line
        String dumpComment = "Dump created: " + new Date();

        new CompleteAcDump(aceDumpData, groupSet, userSet, mapOrder, dumpComment, this)
                .accept(new AcDumpElementYamlVisitor(mapOrder, sb));
        return sb.toString();
    }

    @Override
    public Set<AclBean> getACLDumpBeans(final Session session) throws RepositoryException {

        List<String> excludeNodesList = Arrays.asList(queryExcludePaths);

        // check excludePaths for existence
        for (String path : excludeNodesList) {
            try {
                if (!session.itemExists(path)) {
                    LOG.error(
                            "Query exclude path: {} doesn't exist in repository! AccessControl installation aborted! Check exclude paths in OSGi configuration of AceService!",
                            path);
                    throw new IllegalArgumentException("Query exclude path: " + path
                            + " doesn't exist in repository! AccessControl installation aborted! Check exclude paths in OSGi configuration of AceService!");
                }
            } catch (RepositoryException e) {
                LOG.error("RepositoryException: {}", e);
                throw e;
            }
        }

        Set<Node> resultNodeSet = QueryHelper.getRepPolicyNodes(session, excludeNodesList);
        Set<AclBean> accessControBeanSet = new LinkedHashSet<AclBean>();

        // assemble big query result set using the query results of the child
        // paths of jcr:root node
        for (Node node : resultNodeSet) {
            try {
                accessControBeanSet.add(
                        new AclBean(AccessControlUtils.getAccessControlList(session, node.getParent().getPath()),
                                node.getParent().getPath()));
            } catch (AccessDeniedException e) {
                LOG.error("AccessDeniedException: {}", e);
            } catch (ItemNotFoundException e) {
                LOG.error("ItemNotFoundException: {}", e);
            } catch (RepositoryException e) {
                LOG.error("RepositoryException: {}", e);
            }
        }
        return accessControBeanSet;
    }

    public AceDumpData createAclDumpMap(final int keyOrder, final int aclOrdering, final String[] excludePaths)
            throws ValueFormatException, IllegalArgumentException, IllegalStateException, RepositoryException {
        return createAclDumpMap(keyOrder, aclOrdering, excludePaths, includeUsersInDumps);
    }

    /** returns a Map with holds either principal or path based ACE data
     *
     * @param request
     * @param keyOrder either principals (AceHelper.PRINCIPAL_BASED_ORDERING) or node paths (AceHelper.PATH_BASED_ORDERING) as keys
     * @param aclOrdering specifies whether the allow and deny ACEs within an ACL should be divided in separate blocks (first deny then
     *            allow)
     * @param isFilterACEs
     * @param isIncludeUsers
     * @return
     * @throws RepositoryException */
    @Override
    public AceDumpData createAclDumpMap(final int keyOrder, final int aclOrdering, final String[] excludePaths,
            final boolean isIncludeUsers) throws RepositoryException {
        Session session = null;
        try {
            session = repository.loginAdministrative(null);

            AceDumpData aceDumpData = new AceDumpData();
            UserManager um = ((JackrabbitSession) session).getUserManager();
            Map<String, Set<AceBean>> aceMap = null;
            Map<String, Set<AceBean>> legacyAceMap = new TreeMap<String, Set<AceBean>>();

            if (keyOrder == AcHelper.PRINCIPAL_BASED_ORDER) { // principal based
                aceMap = new TreeMap<String, Set<AceBean>>();
            } else if (keyOrder == AcHelper.PATH_BASED_ORDER) { // path based
                aceMap = new TreeMap<String, Set<AceBean>>();
            }

            Set<AclBean> aclBeanSet = getACLDumpBeans(session);

            // build a set containing all ACE found in the original order
            for (AclBean aclBean : aclBeanSet) {

                if (aclBean.getAcl() == null) {
                    continue;
                }

                boolean allowExistsInListEarlier = false;
                for (AccessControlEntry ace : aclBean.getAcl().getAccessControlEntries()) {
                    if (!(ace instanceof JackrabbitAccessControlEntry)) {
                        throw new IllegalStateException("AC entry is not a JackrabbitAccessControlEntry: " + ace);
                    }
                    AceWrapper tmpBean = new AceWrapper((JackrabbitAccessControlEntry) ace, aclBean.getJcrPath());
                    AceBean tmpAceBean = AcHelper.getAceBean(tmpBean);

                    // sets keepOrder true if ACE deny entries are found that are not at top of list
                    if (tmpAceBean.isAllow()) {
                        allowExistsInListEarlier = true;
                    } else {
                        if (allowExistsInListEarlier && !tmpAceBean.isAllow()) {
                            tmpAceBean.setKeepOrder(true);
                        }
                    }

                    Authorizable authorizable = um.getAuthorizable(tmpAceBean.getPrincipalName());

                    // if this group exists under home
                    if (authorizable != null) {
                        if (authorizable.isGroup() || isIncludeUsers) {
                            addBeanToMap(keyOrder, aclOrdering, aceMap, tmpAceBean);
                        }
                    }
                    // otherwise put in map holding legacy ACEs
                    else {
                        addBeanToMap(keyOrder, aclOrdering, legacyAceMap, tmpAceBean);
                    }

                }
            }

            aceDumpData.setAceDump(aceMap);
            aceDumpData.setLegacyAceDump(legacyAceMap);

            return aceDumpData;

        } finally {
            if (session != null) {
                session.logout();
            }
        }

    }

    private void addBeanToMap(final int keyOrder, final int aclOrdering, Map<String, Set<AceBean>> aceMap,
            AceBean aceBean) {
        if (keyOrder == AcHelper.PRINCIPAL_BASED_ORDER) {
            String principalName = aceBean.getPrincipalName();
            if (!aceMap.containsKey(principalName)) {
                Set<AceBean> aceSet = getNewAceSet(aclOrdering);
                aceSet.add(aceBean);
                aceMap.put(principalName, aceSet);
            } else {
                aceMap.get(principalName).add(aceBean);
            }
        } else if (keyOrder == AcHelper.PATH_BASED_ORDER) {
            String jcrPath = aceBean.getJcrPath();
            if (!aceMap.containsKey(jcrPath)) {
                Set<AceBean> aceSet = getNewAceSet(aclOrdering);
                aceSet.add(aceBean);
                aceMap.put(jcrPath, aceSet);
            } else {
                aceMap.get(jcrPath).add(aceBean);
            }
        }
    }

    /** removes the name of the group node itself (groupID) from the intermediate path
     *
     * @param intermediatePath
     * @param groupID
     * @return corrected path if groupID was found at the end of the intermediatePath, otherwise original path */
    private String getIntermediatePath(String intermediatePath, final String groupID) {
        int index = StringUtils.lastIndexOf(intermediatePath, "/" + groupID);
        if (index != -1) {
            intermediatePath = intermediatePath.replace(intermediatePath.substring(index), "");
        }
        return intermediatePath;
    }

    public void returnAuthorizableDumpAsFile(final SlingHttpServletResponse response,
            Set<AuthorizableConfigBean> authorizableSet) throws IOException {
        String mimetype = "application/octet-stream";
        response.setContentType(mimetype);
        ServletOutputStream outStream = null;
        try {
            outStream = response.getOutputStream();
        } catch (IOException e) {
            LOG.error("Exception in AuthorizableDumpUtils: {}", e);
        }

        String fileName = "Authorizable_Dump_" + new Date(System.currentTimeMillis());
        response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");

        try {
            writeAuthorizableConfigToStream(authorizableSet, outStream);
        } catch (IOException e) {
            LOG.error("Exception in AuthorizableDumpUtils: {}", e);
        }
        outStream.close();
    }

    public void writeAuthorizableConfigToStream(Set<AuthorizableConfigBean> authorizableSet,
            ServletOutputStream outStream) throws IOException {

        outStream.println("- " + Constants.GROUP_CONFIGURATION_KEY + ":");
        outStream.println();

        for (AuthorizableConfigBean bean : authorizableSet) {
            outStream.println(AcHelper.getBlankString(AcDumpElementYamlVisitor.DUMP_INDENTATION_KEY) + "- "
                    + bean.getPrincipalID() + ":");
            outStream.println();
            outStream.println(
                    AcHelper.getBlankString(AcDumpElementYamlVisitor.DUMP_INDENTATION_FIRST_PROPERTY) + "- name: ");
            outStream.println(AcHelper.getBlankString(AcDumpElementYamlVisitor.DUMP_INDENTATION_PROPERTY)
                    + "memberOf: " + bean.getMemberOfString());
            outStream.println(AcHelper.getBlankString(AcDumpElementYamlVisitor.DUMP_INDENTATION_PROPERTY) + "path: "
                    + bean.getPath());
            outStream.println(AcHelper.getBlankString(AcDumpElementYamlVisitor.DUMP_INDENTATION_PROPERTY)
                    + "isGroup: " + "'" + bean.isGroup() + "'");
            outStream.println();
        }
    }

    /** method that returns the data of users contained in a set as AuthorizableConfigBeans
     *
     * @param usersFromACEs set holding users
     * @return set holding AuthorizableConfigBeans */
    public Set<AuthorizableConfigBean> getUserBeans(Set<User> usersFromACEs)
            throws RepositoryException, UnsupportedRepositoryOperationException {

        Set<AuthorizableConfigBean> userBeans = new TreeSet<AuthorizableConfigBean>(
                new AuthorizableBeanIDComparator());

        // add found users from ACEs to set of authorizables
        if (!usersFromACEs.isEmpty()) {
            for (User user : usersFromACEs) {
                AuthorizableConfigBean newBean = new AuthorizableConfigBean();
                newBean.setPrincipalID(user.getID());
                String intermediatePath = getIntermediatePath(user.getPath(), user.getID());
                newBean.setPath(intermediatePath);
                newBean.setIsGroup(false);
                new HashSet<Authorizable>();
                addDeclaredMembers(user, newBean);
                userBeans.add(newBean);
            }
        }
        return userBeans;
    }

    /** method that fetches all groups from /home and returns their data encapsulated in AuthorizableConfigBeans
     *
     * @param session session with sufficient rights to read group informations
     * @return set holding AuthorizableConfigBeans */
    @Override
    public Set<AuthorizableConfigBean> getGroupBeans(Session session)
            throws AccessDeniedException, UnsupportedRepositoryOperationException, RepositoryException {
        JackrabbitSession js = (JackrabbitSession) session;
        UserManager userManager = js.getUserManager();

        Set<AuthorizableConfigBean> groupBeans = new LinkedHashSet<AuthorizableConfigBean>();

        Iterator<Authorizable> result = userManager.findAuthorizables(new Query() {
            @Override
            public void build(QueryBuilder builder) {
                builder.setSortOrder("@rep:principalName", Direction.ASCENDING);
                builder.setSelector(org.apache.jackrabbit.api.security.user.Group.class);
            }
        });

        while (result.hasNext()) {
            Group group = (Group) result.next();
            if (group != null) {
                AuthorizableConfigBean bean = new AuthorizableConfigBean();
                bean.setPrincipalID(group.getID());
                addDeclaredMembers(group, bean);
                bean.setIsGroup(group.isGroup());
                bean.setPath(getIntermediatePath(group.getPath(), group.getID()));

                groupBeans.add(bean);
            } else {
                LOG.debug("group is null !");
            }
        }
        return groupBeans;

    }

    private void addDeclaredMembers(Authorizable authorizable, AuthorizableConfigBean bean)
            throws RepositoryException {
        Iterator<Group> it = authorizable.declaredMemberOf();
        List<String> memberOfList = new ArrayList<String>();

        while (it.hasNext()) {
            memberOfList.add(it.next().getID());
        }
        bean.setMemberOf(memberOfList.toArray(new String[memberOfList.size()]));
    }

    private Set<AceBean> getNewAceSet(final int aclOrdering) {
        Set<AceBean> aceSet = null;

        if (aclOrdering == AcHelper.ACE_ORDER_NONE) {
            aceSet = new LinkedHashSet<AceBean>();
        } else if (aclOrdering == AcHelper.ACE_ORDER_ACTOOL_BEST_PRACTICE) {
            aceSet = new TreeSet<AceBean>(new AcePermissionComparator());
        } else if (aclOrdering == AcHelper.ACE_ORDER_ALPHABETICAL) {
            aceSet = new TreeSet<AceBean>(new AcePathComparator());
        }
        return aceSet;
    }

}