001/*
002 * (C) Copyright 2010 Nuxeo SAS (http://nuxeo.com/) and contributors.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the GNU Lesser General Public License
006 * (LGPL) version 2.1 which accompanies this distribution, and is available at
007 * http://www.gnu.org/licenses/lgpl.html
008 *
009 * This library is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * Contributors:
015 *     Nuxeo - initial API and implementation
016 */
017
018package org.nuxeo.ecm.platform.login;
019
020import java.security.Principal;
021import java.security.acl.Group;
022import java.util.Enumeration;
023import java.util.Vector;
024
025/**
026 * This class implements a group of principals.
027 *
028 * @author Satish Dharmaraj
029 */
030public class GroupImpl implements Group {
031    private Vector<Principal> groupMembers = new Vector<Principal>(50, 100);
032
033    private String group;
034
035    /**
036     * Constructs a Group object with no members.
037     *
038     * @param groupName the name of the group
039     */
040    public GroupImpl(String groupName) {
041        this.group = groupName;
042    }
043
044    /**
045     * adds the specified member to the group.
046     *
047     * @param user The principal to add to the group.
048     * @return true if the member was added - false if the member could not be added.
049     */
050    public boolean addMember(Principal user) {
051        if (groupMembers.contains(user)) {
052            return false;
053        }
054
055        // do not allow groups to be added to itself.
056        if (group.equals(user.toString())) {
057            throw new IllegalArgumentException();
058        }
059
060        groupMembers.addElement(user);
061        return true;
062    }
063
064    /**
065     * Removes the specified member from the group.
066     *
067     * @param user The principal to remove from the group.
068     * @return true if the principal was removed false if the principal was not a member
069     */
070    public boolean removeMember(Principal user) {
071        return groupMembers.removeElement(user);
072    }
073
074    /**
075     * returns the enumeration of the members in the group.
076     */
077    public Enumeration<? extends Principal> members() {
078        return groupMembers.elements();
079    }
080
081    /**
082     * This function returns true if the group passed matches the group represented in this interface.
083     *
084     * @param obj the group to compare this group to.
085     */
086    @Override
087    public boolean equals(Object obj) {
088        if (this == obj) {
089            return true;
090        }
091        if (!(obj instanceof Group)) {
092            return false;
093        }
094        Group other = (Group) obj;
095        return group.equals(other.toString());
096    }
097
098    // equals(Group) for compatibility
099    public boolean equals(Group other) {
100        return equals((Object) other);
101    }
102
103    /**
104     * Prints a stringified version of the group.
105     */
106    @Override
107    public String toString() {
108        return group;
109    }
110
111    /**
112     * return a hashcode for the principal.
113     */
114    @Override
115    public int hashCode() {
116        return group.hashCode();
117    }
118
119    /**
120     * returns true if the passed principal is a member of the group.
121     *
122     * @param member The principal whose membership must be checked for.
123     * @return true if the principal is a member of this group, false otherwise
124     */
125    public boolean isMember(Principal member) {
126
127        //
128        // if the member is part of the group (common case), return true.
129        // if not, recursively search depth first in the group looking for the
130        // principal.
131        //
132        if (groupMembers.contains(member)) {
133            return true;
134        } else {
135            Vector<Group> alreadySeen = new Vector<Group>(10);
136            return isMemberRecurse(member, alreadySeen);
137        }
138    }
139
140    /**
141     * return the name of the principal.
142     */
143    public String getName() {
144        return group;
145    }
146
147    //
148    // This function is the recursive search of groups for this
149    // implementation of the Group. The search proceeds building up
150    // a vector of already seen groups. Only new groups are considered,
151    // thereby avoiding loops.
152    //
153    boolean isMemberRecurse(Principal member, Vector<Group> alreadySeen) {
154        Enumeration<? extends Principal> e = members();
155        while (e.hasMoreElements()) {
156            boolean mem = false;
157            Principal p = e.nextElement();
158
159            // if the member is in this collection, return true
160            if (p.equals(member)) {
161                return true;
162            } else if (p instanceof GroupImpl) {
163                //
164                // if not recurse if the group has not been checked already.
165                // Can call method in this package only if the object is an
166                // instance of this class. Otherwise call the method defined
167                // in the interface. (This can lead to a loop if a mixture of
168                // implementations form a loop, but we live with this improbable
169                // case rather than clutter the interface by forcing the
170                // implementation of this method.)
171                //
172                GroupImpl g = (GroupImpl) p;
173                alreadySeen.addElement(this);
174                if (!alreadySeen.contains(g)) {
175                    mem = g.isMemberRecurse(member, alreadySeen);
176                }
177            } else if (p instanceof Group) {
178                Group g = (Group) p;
179                if (!alreadySeen.contains(g)) {
180                    mem = g.isMember(member);
181                }
182            }
183
184            if (mem) {
185                return mem;
186            }
187        }
188        return false;
189    }
190}