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<>(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    @Override
053    public boolean addMember(Principal user) {
054        if (groupMembers.contains(user)) {
055            return false;
056        }
057
058        // do not allow groups to be added to itself.
059        if (group.equals(user.toString())) {
060            throw new IllegalArgumentException();
061        }
062
063        groupMembers.addElement(user);
064        return true;
065    }
066
067    /**
068     * Removes the specified member from the group.
069     *
070     * @param user The principal to remove from the group.
071     * @return true if the principal was removed false if the principal was not a member
072     */
073    @Override
074    public boolean removeMember(Principal user) {
075        return groupMembers.removeElement(user);
076    }
077
078    /**
079     * returns the enumeration of the members in the group.
080     */
081    @Override
082    public Enumeration<? extends Principal> members() {
083        return groupMembers.elements();
084    }
085
086    /**
087     * This function returns true if the group passed matches the group represented in this interface.
088     *
089     * @param obj the group to compare this group to.
090     */
091    @Override
092    public boolean equals(Object obj) {
093        if (this == obj) {
094            return true;
095        }
096        if (!(obj instanceof Group)) {
097            return false;
098        }
099        Group other = (Group) obj;
100        return group.equals(other.toString());
101    }
102
103    // equals(Group) for compatibility
104    public boolean equals(Group other) {
105        return equals((Object) other);
106    }
107
108    /**
109     * Prints a stringified version of the group.
110     */
111    @Override
112    public String toString() {
113        return group;
114    }
115
116    /**
117     * return a hashcode for the principal.
118     */
119    @Override
120    public int hashCode() {
121        return group.hashCode();
122    }
123
124    /**
125     * returns true if the passed principal is a member of the group.
126     *
127     * @param member The principal whose membership must be checked for.
128     * @return true if the principal is a member of this group, false otherwise
129     */
130    @Override
131    public boolean isMember(Principal member) {
132
133        //
134        // if the member is part of the group (common case), return true.
135        // if not, recursively search depth first in the group looking for the
136        // principal.
137        //
138        if (groupMembers.contains(member)) {
139            return true;
140        } else {
141            Vector<Group> alreadySeen = new Vector<>(10);
142            return isMemberRecurse(member, alreadySeen);
143        }
144    }
145
146    /**
147     * return the name of the principal.
148     */
149    @Override
150    public String getName() {
151        return group;
152    }
153
154    //
155    // This function is the recursive search of groups for this
156    // implementation of the Group. The search proceeds building up
157    // a vector of already seen groups. Only new groups are considered,
158    // thereby avoiding loops.
159    //
160    boolean isMemberRecurse(Principal member, Vector<Group> alreadySeen) {
161        Enumeration<? extends Principal> e = members();
162        while (e.hasMoreElements()) {
163            boolean mem = false;
164            Principal p = e.nextElement();
165
166            // if the member is in this collection, return true
167            if (p.equals(member)) {
168                return true;
169            } else if (p instanceof GroupImpl) {
170                //
171                // if not recurse if the group has not been checked already.
172                // Can call method in this package only if the object is an
173                // instance of this class. Otherwise call the method defined
174                // in the interface. (This can lead to a loop if a mixture of
175                // implementations form a loop, but we live with this improbable
176                // case rather than clutter the interface by forcing the
177                // implementation of this method.)
178                //
179                GroupImpl g = (GroupImpl) p;
180                alreadySeen.addElement(this);
181                if (!alreadySeen.contains(g)) {
182                    mem = g.isMemberRecurse(member, alreadySeen);
183                }
184            } else if (p instanceof Group) {
185                Group g = (Group) p;
186                if (!alreadySeen.contains(g)) {
187                    mem = g.isMember(member);
188                }
189            }
190
191            if (mem) {
192                return mem;
193            }
194        }
195        return false;
196    }
197}