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