Skip to content
LdapGroupDAO.java 33.6 KiB
Newer Older
/*
 ************************************************************************
 *******************  CANADIAN ASTRONOMY DATA CENTRE  *******************
 **************  CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES  **************
 *
 *  (c) 2014.                            (c) 2014.
 *  Government of Canada                 Gouvernement du Canada
 *  National Research Council            Conseil national de recherches
 *  Ottawa, Canada, K1A 0R6              Ottawa, Canada, K1A 0R6
 *  All rights reserved                  Tous droits réservés
 *
 *  NRC disclaims any warranties,        Le CNRC dénie toute garantie
 *  expressed, implied, or               énoncée, implicite ou légale,
 *  statutory, of any kind with          de quelque nature que ce
 *  respect to the software,             soit, concernant le logiciel,
 *  including without limitation         y compris sans restriction
 *  any warranty of merchantability      toute garantie de valeur
 *  or fitness for a particular          marchande ou de pertinence
 *  purpose. NRC shall not be            pour un usage particulier.
 *  liable in any event for any          Le CNRC ne pourra en aucun cas
 *  damages, whether direct or           être tenu responsable de tout
 *  indirect, special or general,        dommage, direct ou indirect,
 *  consequential or incidental,         particulier ou général,
 *  arising from the use of the          accessoire ou fortuit, résultant
 *  software.  Neither the name          de l'utilisation du logiciel. Ni
 *  of the National Research             le nom du Conseil National de
 *  Council of Canada nor the            Recherches du Canada ni les noms
 *  names of its contributors may        de ses  participants ne peuvent
 *  be used to endorse or promote        être utilisés pour approuver ou
 *  products derived from this           promouvoir les produits dérivés
 *  software without specific prior      de ce logiciel sans autorisation
 *  written permission.                  préalable et particulière
 *                                       par écrit.
 *
 *  This file is part of the             Ce fichier fait partie du projet
 *  OpenCADC project.                    OpenCADC.
 *
 *  OpenCADC is free software:           OpenCADC est un logiciel libre ;
 *  you can redistribute it and/or       vous pouvez le redistribuer ou le
 *  modify it under the terms of         modifier suivant les termes de
 *  the GNU Affero General Public        la “GNU Affero General Public
 *  License as published by the          License” telle que publiée
 *  Free Software Foundation,            par la Free Software Foundation
 *  either version 3 of the              : soit la version 3 de cette
 *  License, or (at your option)         licence, soit (à votre gré)
 *  any later version.                   toute version ultérieure.
 *
 *  OpenCADC is distributed in the       OpenCADC est distribué
 *  hope that it will be useful,         dans l’espoir qu’il vous
 *  but WITHOUT ANY WARRANTY;            sera utile, mais SANS AUCUNE
 *  without even the implied             GARANTIE : sans même la garantie
 *  warranty of MERCHANTABILITY          implicite de COMMERCIALISABILITÉ
 *  or FITNESS FOR A PARTICULAR          ni d’ADÉQUATION À UN OBJECTIF
 *  PURPOSE.  See the GNU Affero         PARTICULIER. Consultez la Licence
 *  General Public License for           Générale Publique GNU Affero
 *  more details.                        pour plus de détails.
 *
 *  You should have received             Vous devriez avoir reçu une
 *  a copy of the GNU Affero             copie de la Licence Générale
 *  General Public License along         Publique GNU Affero avec
 *  with OpenCADC.  If not, see          OpenCADC ; si ce n’est
 *  <http://www.gnu.org/licenses/>.      pas le cas, consultez :
 *                                       <http://www.gnu.org/licenses/>.
 *
 *  $Revision: 4 $
 *
 ************************************************************************
 */
package ca.nrc.cadc.ac.server.ldap;

import java.lang.reflect.Field;
import java.security.AccessControlException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;

import org.apache.log4j.Logger;

import com.unboundid.ldap.sdk.AddRequest;
import com.unboundid.ldap.sdk.Attribute;
import com.unboundid.ldap.sdk.DN;
Jeff Burke's avatar
Jeff Burke committed
import com.unboundid.ldap.sdk.Filter;
import com.unboundid.ldap.sdk.LDAPException;
Brian Major's avatar
Brian Major committed
import com.unboundid.ldap.sdk.LDAPInterface;
import com.unboundid.ldap.sdk.LDAPResult;
import com.unboundid.ldap.sdk.LDAPSearchException;
import com.unboundid.ldap.sdk.Modification;
import com.unboundid.ldap.sdk.ModificationType;
import com.unboundid.ldap.sdk.ModifyRequest;
import com.unboundid.ldap.sdk.ResultCode;
import com.unboundid.ldap.sdk.SearchRequest;
import com.unboundid.ldap.sdk.SearchResult;
import com.unboundid.ldap.sdk.SearchResultEntry;
import com.unboundid.ldap.sdk.SearchResultListener;
import com.unboundid.ldap.sdk.SearchResultReference;
import com.unboundid.ldap.sdk.SearchScope;

import ca.nrc.cadc.ac.ActivatedGroup;
import ca.nrc.cadc.ac.Group;
import ca.nrc.cadc.ac.GroupAlreadyExistsException;
import ca.nrc.cadc.ac.GroupNotFoundException;
import ca.nrc.cadc.ac.GroupURI;
import ca.nrc.cadc.ac.User;
import ca.nrc.cadc.ac.UserNotFoundException;
import ca.nrc.cadc.ac.server.GroupDetailSelector;
import ca.nrc.cadc.auth.DNPrincipal;
import ca.nrc.cadc.net.TransientException;
import ca.nrc.cadc.profiler.Profiler;
import ca.nrc.cadc.reg.Standards;
import ca.nrc.cadc.reg.client.LocalAuthority;
import ca.nrc.cadc.util.StringUtil;

public class LdapGroupDAO extends LdapDAO
{
    private static final Logger logger = Logger.getLogger(LdapGroupDAO.class);
    // LDAP Group attributes
    protected static final String LDAP_CN = "cn";
    protected static final String LDAP_DESCRIPTION = "description";
    protected static final String LDAP_ENTRYDN = "entrydn";
    protected static final String LDAP_GROUP_OF_UNIQUE_NAMES = "groupofuniquenames";
    protected static final String LDAP_INET_USER = "inetuser";
    protected static final String LDAP_MODIFY_TIMESTAMP = "modifytimestamp";
    protected static final String LDAP_NSACCOUNTLOCK = "nsaccountlock";
    protected static final String LDAP_OBJECT_CLASS = "objectClass";
    protected static final String LDAP_OWNER = "owner";
    protected static final String LDAP_UNIQUE_MEMBER = "uniquemember";

    private static final String[] PUB_GROUP_ATTRS = new String[]
                    LDAP_ENTRYDN, LDAP_CN
    private static final String[] GROUP_ATTRS = new String[]
                    LDAP_ENTRYDN, LDAP_CN, LDAP_NSACCOUNTLOCK, LDAP_OWNER,
                    LDAP_MODIFY_TIMESTAMP, LDAP_DESCRIPTION
    private static final String[] GROUP_AND_MEMBER_ATTRS = new String[]
                    LDAP_ENTRYDN, LDAP_CN, LDAP_NSACCOUNTLOCK, LDAP_OWNER,
                    LDAP_MODIFY_TIMESTAMP, LDAP_DESCRIPTION, LDAP_UNIQUE_MEMBER
    // this gets filled by the LdapgroupPersistence
    GroupDetailSelector searchDetailSelector;
    public LdapGroupDAO(LdapConnections connections, LdapUserDAO userPersist)
Brian Major's avatar
Brian Major committed
        super(connections);
        if (userPersist == null)
        {
Jeff Burke's avatar
Jeff Burke committed
            throw new IllegalArgumentException(
                    "User persistence instance required");
Patrick Dowler's avatar
Patrick Dowler committed
        this.userDAO = userPersist;
Jeff Burke's avatar
Jeff Burke committed
    /**
     * Persists a group.
Jeff Burke's avatar
Jeff Burke committed
     * @param group The group to create
     * @throws GroupAlreadyExistsException If a group with the same ID already
Jeff Burke's avatar
Jeff Burke committed
     *                                     exists.
     * @throws TransientException          If an temporary, unexpected problem occurred.
     * @throws UserNotFoundException       If owner or a member not valid user.
     * @throws GroupNotFoundException
Jeff Burke's avatar
Jeff Burke committed
     */
    public void addGroup(final Group group)
            throws GroupAlreadyExistsException, TransientException,
                   UserNotFoundException, AccessControlException,
                   GroupNotFoundException
        if (!group.getProperties().isEmpty())
        {
            throw new UnsupportedOperationException(
                    "Support for groups properties not available");
        }
Patrick Dowler's avatar
Patrick Dowler committed
            Set<DNPrincipal> ds = group.getOwner().getIdentities(DNPrincipal.class);
            if (ds.isEmpty())
                throw new RuntimeException("BUG: User does not have an internal DNPrincipal");
            DNPrincipal dnp = ds.iterator().next();
            DN ownerDN = new DN(dnp.getName());
                // add group to groups tree
                LDAPResult result = addGroup(getGroupDN(group.getID().getName()),
                                             group.getID().getName(), ownerDN,
                                             group.description,
                                             group.getUserMembers(),
                                             group.getGroupMembers());
                LdapDAO.checkLdapResult(result.getResultCode());
                // add group to admin groups tree
                result = addGroup(getAdminGroupDN(group.getID().getName()),
                                  group.getID().getName(), ownerDN,
                                  group.description,
                                  group.getUserAdmins(),
                                  group.getGroupAdmins());
                LdapDAO.checkLdapResult(result.getResultCode());
        catch (LDAPException e)
        {
            logger.debug("addGroup Exception: " + e, e);
            LdapDAO.checkLdapResult(e.getResultCode());
Adrian Damian's avatar
Adrian Damian committed
            throw new RuntimeException("Unexpected LDAP exception", e);
    private LDAPResult addGroup(final DN groupDN, final String groupID,
                                final DN ownerDN, final String description,
                                final Set<Group> groups)
            throws UserNotFoundException, LDAPException, TransientException,
                   AccessControlException, GroupNotFoundException
    {
        // add new group
        List<Attribute> attributes = new ArrayList<Attribute>();
        Attribute ownerAttribute = new Attribute(LDAP_OWNER, ownerDN.toNormalizedString());
        attributes.add(ownerAttribute);
        attributes.add(new Attribute(LDAP_OBJECT_CLASS, LDAP_GROUP_OF_UNIQUE_NAMES));
        attributes.add(new Attribute(LDAP_OBJECT_CLASS, LDAP_INET_USER));
        attributes.add(new Attribute(LDAP_CN, groupID));
        if (StringUtil.hasText(description))
            attributes.add(new Attribute(LDAP_DESCRIPTION, description));
        }

        List<String> members = new ArrayList<String>();
Patrick Dowler's avatar
Patrick Dowler committed
            DN memberDN = this.userDAO.getUserDN(userMember);
            members.add(memberDN.toNormalizedString());
        }
        for (Group groupMember : groups)
        {
            final String groupMemberID = groupMember.getID().getName();
Dustin Jenkins's avatar
Dustin Jenkins committed
            if (!checkGroupExists(groupMemberID))
Dustin Jenkins's avatar
Dustin Jenkins committed
                throw new GroupNotFoundException(groupMemberID);
Dustin Jenkins's avatar
Dustin Jenkins committed
            DN memberDN = getGroupDN(groupMemberID);
            members.add(memberDN.toNormalizedString());
        }
        if (!members.isEmpty())
        {
            attributes.add(
                new Attribute(LDAP_UNIQUE_MEMBER,
                              (String[]) members.toArray(new String[members.size()])));
        }

        AddRequest addRequest = new AddRequest(groupDN, attributes);

Brian Major's avatar
Brian Major committed
        return getReadWriteConnection().add(addRequest);
    /**
     * Checks whether group name available for the user or already in use.
     * @param group
     * @return activated group or null if group does not exists
     * @throws AccessControlException
     * @throws UserNotFoundException
     * @throws GroupNotFoundException
     * @throws TransientException
     * @throws GroupAlreadyExistsException
    private boolean reactivateGroup(final Group group)
            throws AccessControlException, UserNotFoundException,
                   TransientException, GroupAlreadyExistsException
Adrian Damian's avatar
Adrian Damian committed
        try
            // check group name exists
            Filter filter = Filter.createEqualityFilter(LDAP_CN, group.getID().getName());
Adrian Damian's avatar
Adrian Damian committed

            DN groupDN = getGroupDN(group.getID().getName());
            SearchRequest searchRequest =
                    new SearchRequest(groupDN.toNormalizedString(), SearchScope.BASE,
                                      filter, new String[]{LDAP_NSACCOUNTLOCK});
            SearchResultEntry searchResult =
                    getReadWriteConnection().searchForEntry(searchRequest);
            if (searchResult == null)
Adrian Damian's avatar
Adrian Damian committed
            {
            if (searchResult.getAttributeValue(LDAP_NSACCOUNTLOCK) == null)
Adrian Damian's avatar
Adrian Damian committed
            {
Patrick Dowler's avatar
Patrick Dowler committed
                throw new GroupAlreadyExistsException("Group already exists " + group.getID());
Adrian Damian's avatar
Adrian Damian committed
            }

            // activate group
Adrian Damian's avatar
Adrian Damian committed
            {
            catch (GroupNotFoundException e)
Adrian Damian's avatar
Adrian Damian committed
            {
                throw new RuntimeException(
                        "BUG: group to modify does not exist" + group.getID());
Adrian Damian's avatar
Adrian Damian committed
        catch (LDAPException e)
            logger.debug("reactivateGroup Exception: " + e, e);
            LdapDAO.checkLdapResult(e.getResultCode());
Adrian Damian's avatar
Adrian Damian committed
            throw new RuntimeException("Unexpected LDAP exception", e);
    /**
     * Get all group names.
     * @return A collection of strings
     * @throws TransientException If an temporary, unexpected problem occurred.
     */
    public Collection<String> getGroupNames()
                    .createNOTFilter(Filter.createPresenceFilter(LDAP_NSACCOUNTLOCK));
            filter = Filter.createANDFilter(filter, Filter.create("(cn=*)"));
            final List<String> groupNames = new LinkedList<String>();
            SearchRequest searchRequest = new SearchRequest(
                    new SearchResultListener()
                    {
                        long t1 = System.currentTimeMillis();
                        public void searchEntryReturned(SearchResultEntry sre)
                        {
                            String gname = sre.getAttributeValue(LDAP_CN);
                            long t2 = System.currentTimeMillis();
                            long dt = t2 - t1;
                            if (groupNames.size() == 1)
                            {
                                logger.debug("first row: " + dt + "ms");
                                t1 = t2;
                            if ((groupNames.size() % 100) == 0)
                                logger.debug("found: " + groupNames.size() +
                                             " " + dt + "ms");
                                t1 = t2;
                            }
                        }

                        public void searchReferenceReturned(SearchResultReference srr)
                        {
                            throw new UnsupportedOperationException("Not supported yet.");
                    }, config.getGroupsDN(), SearchScope.ONE, filter, PUB_GROUP_ATTRS);
            SearchResult searchResult = null;
            try
            {
Jeff Burke's avatar
Jeff Burke committed
                Profiler profiler = new Profiler(LdapGroupDAO.class);
Brian Major's avatar
Brian Major committed
                LDAPInterface con = getReadOnlyConnection();
                profiler.checkpoint("getGroupNames.getConnection");
                searchResult = con.search(searchRequest);
                profiler.checkpoint("getGroupNames.search");
            }
            catch (LDAPSearchException e)
            {
                logger.debug("Could not find groups root", e);
                LdapDAO.checkLdapResult(e.getResultCode());
                if (e.getResultCode() == ResultCode.NO_SUCH_OBJECT)
                {
                    throw new IllegalStateException("Could not find groups root");
                throw new IllegalStateException("unexpected failure", e);

            LdapDAO.checkLdapResult(searchResult.getResultCode());
Jeff Burke's avatar
Jeff Burke committed
//            profiler.checkpoint("checkLdapResult");
            return groupNames;
        }
        catch (LDAPException e1)
        {
            logger.debug("getGroupNames Exception: " + e1, e1);
            LdapDAO.checkLdapResult(e1.getResultCode());
            throw new IllegalStateException("Unexpected exception: " +
                                            e1.getMatchedDN(), e1);
Jeff Burke's avatar
Jeff Burke committed
    /**
     * @param groupID The Group unique ID.
     * @return A Group instance
Jeff Burke's avatar
Jeff Burke committed
     * @throws GroupNotFoundException If the group was not found.
     * @throws TransientException     If an temporary, unexpected problem occurred.
Jeff Burke's avatar
Jeff Burke committed
     */
    public Group getGroup(final String groupID, boolean complete)
            throws GroupNotFoundException, TransientException,
                   AccessControlException
        String[] attrs = GROUP_ATTRS;
        if (complete)
            attrs = GROUP_AND_MEMBER_ATTRS;
        Group group = getGroup(getGroupDN(groupID), groupID, attrs);
        if (complete)
        {
            Group adminGroup = getGroup(getAdminGroupDN(groupID), null, GROUP_AND_MEMBER_ATTRS);
            group.getGroupAdmins().addAll(adminGroup.getGroupMembers());
            group.getUserAdmins().addAll(adminGroup.getUserMembers());
        }
        return group;
    }
    // groupID is here so exceptions and logging have plain groupID instead of DN
    private Group getGroup(final DN groupDN, final String xgroupID, String[] attributes)
            throws GroupNotFoundException, TransientException,
                   AccessControlException
        logger.debug("getGroup: " + groupDN + " attrs: " + attributes.length);
        String loggableGroupID = xgroupID;
        if (loggableGroupID == null)
            // member or admin group: same name, internal tree
            loggableGroupID = groupDN.toString();
            Filter filter = Filter.createNOTFilter(Filter.createPresenceFilter(LDAP_NSACCOUNTLOCK));
                    Filter.createEqualityFilter(LDAP_ENTRYDN, groupDN.toNormalizedString()));

            SearchRequest searchRequest =
                    new SearchRequest(groupDN.toNormalizedString(),
                                      SearchScope.BASE, filter, attributes);
            SearchResultEntry searchEntry = getReadOnlyConnection()
                    .searchForEntry(searchRequest);
                String msg = "Group not found " + loggableGroupID + " cause: null";
                logger.debug(msg);
                throw new GroupNotFoundException(loggableGroupID);
            Group ldapGroup = createGroupFromSearchResult(searchEntry, attributes);
            if (searchEntry.getAttributeValues(LDAP_UNIQUE_MEMBER) != null)
                for (String member : searchEntry
                        .getAttributeValues(LDAP_UNIQUE_MEMBER))
                    DN memberDN = new DN(member);
                    if (memberDN.isDescendantOf(config.getUsersDN(), false))
                            user = userDAO.getUser(new DNPrincipal(member));
                            // ignore as we do not cleanup deleted users
                            // from groups they belong to
                    else if (memberDN.isDescendantOf(config.getGroupsDN(), false))
                            ldapGroup.getGroupMembers()
                                    .add(getGroup(memberDN, null, PUB_GROUP_ATTRS));
                        catch (GroupNotFoundException e)
                            // ignore as we are not cleaning up
                            // deleted groups from the group members
                                "BUG: unknown member DN type: " + memberDN);
            return ldapGroup;
        }
        catch (LDAPException e1)
        {
            logger.debug("getGroup Exception: " + e1, e1);
            LdapDAO.checkLdapResult(e1.getResultCode());
            throw new RuntimeException("BUG: checkLdapResult didn't throw an exception");
Jeff Burke's avatar
Jeff Burke committed
    /**
     * Modify the given group.
     *
Adrian Damian's avatar
Adrian Damian committed
     * @param group The group to update. It must be an existing group
Jeff Burke's avatar
Jeff Burke committed
     * @return The newly updated group.
     * @throws GroupNotFoundException If the group was not found.
     * @throws TransientException     If an temporary, unexpected problem occurred.
Jeff Burke's avatar
Jeff Burke committed
     * @throws AccessControlException If the operation is not permitted.
     * @throws UserNotFoundException  If owner or group members not valid users.
Jeff Burke's avatar
Jeff Burke committed
     */
    public Group modifyGroup(final Group group)
            throws GroupNotFoundException, TransientException,
                   AccessControlException, UserNotFoundException
        String groupID = group.getID().getName();
        getGroup(getGroupDN(groupID), groupID, PUB_GROUP_ATTRS);//group must exists first
Jeff Burke's avatar
Jeff Burke committed
    }
    private Group modifyGroup(final Group group, boolean withActivate)
            throws UserNotFoundException, TransientException,
                   AccessControlException, GroupNotFoundException
Jeff Burke's avatar
Jeff Burke committed
    {
Adrian Damian's avatar
Adrian Damian committed
        if (!group.getProperties().isEmpty())
Jeff Burke's avatar
Jeff Burke committed
            throw new UnsupportedOperationException(
                    "Support for groups properties not available");
Adrian Damian's avatar
Adrian Damian committed
        List<Modification> mods = new ArrayList<Modification>();
        List<Modification> adminMods = new ArrayList<Modification>();
        if (withActivate)
            mods.add(new Modification(ModificationType.DELETE, LDAP_NSACCOUNTLOCK));
            adminMods.add(new Modification(ModificationType.DELETE, LDAP_NSACCOUNTLOCK));
        if (StringUtil.hasText(group.description))
            mods.add(new Modification(ModificationType.REPLACE, LDAP_DESCRIPTION,
Adrian Damian's avatar
Adrian Damian committed
        else
            mods.add(new Modification(ModificationType.REPLACE, LDAP_DESCRIPTION));
            Set<String> newMembers = new HashSet<String>();
            for (User member : group.getUserMembers())
Patrick Dowler's avatar
Patrick Dowler committed
                DN memberDN = userDAO.getUserDN(member);
                newMembers.add(memberDN.toNormalizedString());
            for (Group gr : group.getGroupMembers())
                if (!checkGroupExists(gr.getID().getName()))
                    throw new GroupNotFoundException(gr.getID().getName());
                DN grDN = getGroupDN(gr.getID().getName());
                newMembers.add(grDN.toNormalizedString());

            Set<String> newAdmins = new HashSet<String>();
            for (User member : group.getUserAdmins())
Patrick Dowler's avatar
Patrick Dowler committed
                DN memberDN = userDAO.getUserDN(member);
                if (!checkGroupExists(gr.getID().getName()))
                    throw new GroupNotFoundException(gr.getID().getName());
                DN grDN = getGroupDN(gr.getID().getName());
Adrian Damian's avatar
Adrian Damian committed

            adminMods.add(
                new Modification(ModificationType.REPLACE, LDAP_UNIQUE_MEMBER,
                                 (String[]) newAdmins.toArray(new String[newAdmins.size()])));
                    new ModifyRequest(getAdminGroupDN(group.getID().getName()), adminMods);

            LdapDAO.checkLdapResult(
                getReadWriteConnection().modify(adminModify).getResultCode());
            mods.add(
                new Modification(ModificationType.REPLACE, LDAP_UNIQUE_MEMBER,
                                 (String[]) newMembers.toArray(new String[newMembers.size()])));
Adrian Damian's avatar
Adrian Damian committed

                new ModifyRequest(getGroupDN(group.getID().getName()), mods);
            LdapDAO.checkLdapResult(
                getReadWriteConnection().modify(modifyRequest).getResultCode());
        }
        catch (LDAPException e1)
        {
            logger.debug("Modify Exception: " + e1, e1);
            LdapDAO.checkLdapResult(e1.getResultCode());
Adrian Damian's avatar
Adrian Damian committed
            if (withActivate)
            {
                return new ActivatedGroup(getGroup(group.getID().getName(), true));
Adrian Damian's avatar
Adrian Damian committed
            }
            else
            {
                return getGroup(group.getID().getName(), true);
Adrian Damian's avatar
Adrian Damian committed
            }
        }
        catch (GroupNotFoundException e)
        {
            throw new RuntimeException("BUG: modified group not found (" +
                                        group.getID() + ")");
Jeff Burke's avatar
Jeff Burke committed
    /**
     * @param groupID The group to delete
     * @throws GroupNotFoundException If the group was not found.
     * @throws TransientException     If an temporary, unexpected problem occurred.
Jeff Burke's avatar
Jeff Burke committed
     */
    public void deleteGroup(final String groupID)
            throws GroupNotFoundException, TransientException,
                   AccessControlException
        deleteGroup(getGroupDN(groupID), groupID, false);
        deleteGroup(getAdminGroupDN(groupID), groupID, true);
    }

    private void deleteGroup(final DN groupDN, final String groupID,
                             final boolean isAdmin)
            throws GroupNotFoundException, TransientException,
                   AccessControlException
        ModifyRequest clearMembers = new ModifyRequest(groupDN,
                new Modification(ModificationType.DELETE, LDAP_UNIQUE_MEMBER));
            logger.debug("clearMembers " + groupDN);
            LDAPResult result = getReadWriteConnection().modify(clearMembers);
            LdapDAO.checkLdapResult(result.getResultCode(), true);
            logger.debug("clear members fail: " + e1, e1);
            LdapDAO.checkLdapResult(e1.getResultCode(), true);
        ModifyRequest deleteGroup = new ModifyRequest(groupDN,
                new Modification(ModificationType.ADD, LDAP_NSACCOUNTLOCK, "true"));
            logger.debug("deleteGroup " + groupDN);
            LDAPResult result = getReadWriteConnection().modify(deleteGroup);
            LdapDAO.checkLdapResult(result.getResultCode());
            logger.debug("delete group fail: " + e1, e1);
            LdapDAO.checkLdapResult(e1.getResultCode());
            Group g = getGroup(getGroupDN(groupID), null, GROUP_ATTRS);
            throw new RuntimeException("BUG: group not deleted " + g.getID());
        }
        catch (GroupNotFoundException ignore)
        {
Patrick Dowler's avatar
Patrick Dowler committed
    public Collection<Group> getOwnerGroups(final DNPrincipal owner, final String groupID)
            throws TransientException, AccessControlException
Jeff Burke's avatar
Jeff Burke committed
    {
        Collection<Group> ret = new ArrayList<Group>();
Jeff Burke's avatar
Jeff Burke committed
        try
Patrick Dowler's avatar
Patrick Dowler committed
            DN userDN = new DN(owner.getName());
            Filter filter = Filter.createNOTFilter(Filter.createPresenceFilter(LDAP_NSACCOUNTLOCK));
            filter = Filter.createANDFilter(filter,
                    Filter.createEqualityFilter(LDAP_OWNER, userDN.toNormalizedString()));
Jeff Burke's avatar
Jeff Burke committed
            if (groupID != null)
            {
                filter = Filter.createANDFilter(filter,
                    Filter.createEqualityFilter(LDAP_ENTRYDN, groupDN.toNormalizedString()));
Jeff Burke's avatar
Jeff Burke committed
            }
            SearchRequest searchRequest = new SearchRequest(
                    config.getGroupsDN(), SearchScope.SUB, filter, GROUP_ATTRS);
            SearchResult results = getReadOnlyConnection().search(searchRequest);
Jeff Burke's avatar
Jeff Burke committed
            for (SearchResultEntry result : results.getSearchEntries())
            {
                ret.add(createGroupFromSearchResult(result, GROUP_ATTRS));
Jeff Burke's avatar
Jeff Burke committed
            }
        }
        catch (LDAPException e1)
        {
            logger.debug("getOwnerGroups Exception: " + e1, e1);
            LdapDAO.checkLdapResult(e1.getResultCode());
Jeff Burke's avatar
Jeff Burke committed
        }
        return ret;
    private Group createGroupFromSearchResult(SearchResultEntry result, String[] attributes)
            throws LDAPException, TransientException
        if (result.getAttribute(LDAP_NSACCOUNTLOCK) != null)
            throw new RuntimeException("BUG: found group with nsaccountlock set: " +
                                        result.getAttributeValue(LDAP_ENTRYDN));
        }
        String entryDN = result.getAttributeValue(LDAP_ENTRYDN);
        String groupName = result.getAttributeValue(LDAP_CN);
        LocalAuthority localAuthority = new LocalAuthority();
        URI gmsServiceID = localAuthority.getServiceURI(Standards.GMS_GROUPS_01.toString());
        if (attributes == PUB_GROUP_ATTRS)
        {
            GroupURI groupID = new GroupURI(gmsServiceID.toString() + "?" + groupName);
            return new Group(groupID);
        }
        String ownerDN = result.getAttributeValue(LDAP_OWNER);
        if (ownerDN == null)
        {
            throw new AccessControlException(groupName);
        }
        try
        {
            User owner = userDAO.getUser(new DNPrincipal(ownerDN));
            GroupURI groupID = new GroupURI(gmsServiceID.toString() + "?" + groupName);
            Group group = new Group(groupID);
            setField(group, owner, LDAP_OWNER);
            if (result.hasAttribute(LDAP_DESCRIPTION))
                group.description = result.getAttributeValue(LDAP_DESCRIPTION);
            if (result.hasAttribute(LDAP_MODIFY_TIMESTAMP))
                group.lastModified = result.getAttributeValueAsDate(LDAP_MODIFY_TIMESTAMP);
        catch (UserNotFoundException ex)
            throw new RuntimeException("Invalid state: owner does not exist: " +
                                        ownerDN + " group: " + entryDN);
Jeff Burke's avatar
Jeff Burke committed
    }
     * @return the Distinguished Name of the group
    protected DN getGroupDN(final String groupID) throws TransientException
    {
        try
        {
            return new DN("cn=" + groupID + "," + config.getGroupsDN());
        }
        catch (LDAPException e)
        {
            logger.debug("getGroupDN Exception: " + e, e);
            LdapDAO.checkLdapResult(e.getResultCode());
        }
        throw new IllegalArgumentException(groupID + " not a valid group ID");
    }
     * @return the Distinguished Name of the admin group
    protected DN getAdminGroupDN(final String groupID) throws TransientException
    {
        try
        {
            return new DN("cn=" + groupID + "," + config.getAdminGroupsDN());
        }
        catch (LDAPException e)
        {
            logger.debug("getAdminGroupDN Exception: " + e, e);
            LdapDAO.checkLdapResult(e.getResultCode());
        }
        throw new IllegalArgumentException(groupID + " not a valid group ID");
    }
    private boolean checkGroupExists(String groupID)
            throws LDAPException, TransientException
            Group g = getGroup(getGroupDN(groupID), groupID, PUB_GROUP_ATTRS);
        catch (GroupNotFoundException ex)
    // set private field using reflection
    private void setField(Object object, Object value, String name)
    {
        try
        {
            Field field = object.getClass().getDeclaredField(name);
            field.setAccessible(true);
            field.set(object, value);
        }
        catch (NoSuchFieldException e)
        {
            final String error = object.getClass().getSimpleName() +
                " field " + name + "not found";
            throw new RuntimeException(error, e);
        }
        catch (IllegalAccessException e)
        {
            final String error = "unable to update " + name + " in " +
                object.getClass().getSimpleName();
            throw new RuntimeException(error, e);
        }
    }