001: /*
002: * JSPWiki - a JSP-based WikiWiki clone. Copyright (C) 2001-2003 Janne Jalkanen
003: * (Janne.Jalkanen@iki.fi) This program is free software; you can redistribute
004: * it and/or modify it under the terms of the GNU Lesser General Public License
005: * as published by the Free Software Foundation; either version 2.1 of the
006: * License, or (at your option) any later version. This program is distributed
007: * in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
008: * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
009: * See the GNU Lesser General Public License for more details. You should have
010: * received a copy of the GNU Lesser General Public License along with this
011: * program; if not, write to the Free Software Foundation, Inc., 59 Temple
012: * Place, Suite 330, Boston, MA 02111-1307 USA
013: */
014: package com.ecyrd.jspwiki.auth.authorize;
015:
016: import java.security.Principal;
017: import java.util.Date;
018: import java.util.Iterator;
019: import java.util.Vector;
020:
021: import com.ecyrd.jspwiki.auth.GroupPrincipal;
022:
023: /**
024: * <p>
025: * Groups are a specialized type of ad-hoc role used by the wiki system. Unlike
026: * externally-provided roles (such as those provided by an LDAP server or web
027: * container), JSPWiki groups can be created dynamically by wiki users, without
028: * requiring special container privileges or administrator intervention. They
029: * are designed to provide a lightweight role-based access control system that
030: * complements existing role systems.
031: * </p>
032: * <p>
033: * Group names are case-insensitive, and have a few naming restrictions, which
034: * are enforced by the {@link GroupManager}:
035: * </p>
036: * <ul>
037: * <li>Groups cannot have the same name as a built-in Role (e.g., "Admin",
038: * "Authenticated" etc.)</li>
039: * <li>Groups cannot have the same name as an existing user</li>
040: * </ul>
041: * <p>
042: * <em>Note: prior to JSPWiki 2.4.19, Group was an interface; it
043: * is now a concrete, final class.</em>
044: * </p>
045: * <p>
046: * Groups are related to {@link GroupPrincipal}s. A GroupPrincipal, when
047: * injected into the Principal set of a WikiSession's Subject, means that the
048: * user is a member of a Group of the same name -- it is, in essence, an
049: * "authorization token." GroupPrincipals, unlike Groups, are thread-safe,
050: * lightweight and immutable. That's why we use them in Subjects rather than the
051: * Groups themselves.
052: * </p>
053: * @author Janne Jalkanen
054: * @author Andrew Jaquith
055: * @since 2.3
056: */
057: public class Group {
058:
059: static final String[] RESTRICTED_GROUPNAMES = new String[] {
060: "Anonymous", "All", "Asserted", "Authenticated" };
061:
062: private final Vector m_members = new Vector();
063:
064: private String m_creator = null;
065:
066: private Date m_created = null;
067:
068: private String m_modifier = null;
069:
070: private Date m_modified = null;
071:
072: private final String m_name;
073:
074: private final Principal m_principal;
075:
076: private final String m_wiki;
077:
078: /**
079: * Protected constructor to prevent direct instantiation except by other
080: * package members. Callers should use
081: * {@link GroupManager#parseGroup(String, String, boolean)} or
082: * {@link GroupManager#parseGroup(com.ecyrd.jspwiki.WikiContext, boolean)}.
083: * instead.
084: * @param name the name of the group
085: * @param wiki the wiki the group belongs to
086: */
087: protected Group(String name, String wiki) {
088: m_name = name;
089: m_wiki = wiki;
090: m_principal = new GroupPrincipal(name);
091: }
092:
093: /**
094: * Adds a Principal to the group.
095: *
096: * @param user the principal to add
097: * @return <code>true</code> if the operation was successful
098: */
099: public synchronized boolean add(Principal user) {
100: if (isMember(user)) {
101: return false;
102: }
103:
104: m_members.add(user);
105: return true;
106: }
107:
108: /**
109: * Clears all Principals from the group list.
110: */
111: public synchronized void clear() {
112: m_members.clear();
113: }
114:
115: /**
116: * Two DefaultGroups are equal if they contain identical member Principals
117: * and have the same name.
118: * @param o the object to compare
119: * @return the comparison
120: */
121: public boolean equals(Object o) {
122: if (o == null || !(o instanceof Group))
123: return false;
124:
125: Group g = (Group) o; // Just a shortcut.
126:
127: if (g.m_members.size() != m_members.size())
128: return false;
129:
130: if (getName() != null && !getName().equals(g.getName())) {
131: return false;
132: } else if (getName() == null && g.getName() != null) {
133: return false;
134: }
135:
136: for (Iterator i = m_members.iterator(); i.hasNext();) {
137: if (!(g.isMember((Principal) i.next()))) {
138: return false;
139: }
140: }
141:
142: return true;
143: }
144:
145: /**
146: * The hashcode is calculated as a XOR sum over all members of
147: * the Group.
148: * @return the hash code
149: */
150: public int hashCode() {
151: int hc = 0;
152: for (Iterator i = m_members.iterator(); i.hasNext();) {
153: hc ^= i.next().hashCode();
154: }
155: return hc;
156: }
157:
158: /**
159: * Returns the creation date.
160: * @return the creation date
161: */
162: public synchronized Date getCreated() {
163: return m_created;
164: }
165:
166: /**
167: * Returns the creator of this Group.
168: * @return the creator
169: */
170: public final synchronized String getCreator() {
171: return m_creator;
172: }
173:
174: /**
175: * Returns the last-modified date.
176: * @return the date and time of last modification
177: */
178: public synchronized Date getLastModified() {
179: return m_modified;
180: }
181:
182: /**
183: * Returns the name of the user who last modified this group.
184: * @return the modifier
185: */
186: public final synchronized String getModifier() {
187: return m_modifier;
188: }
189:
190: /**
191: * The name of the group. This is set in the class constructor.
192: * @return the name of the Group
193: */
194: public String getName() {
195: return m_name;
196: }
197:
198: /**
199: * Returns the GroupPrincipal that represents this Group.
200: * @return the group principal
201: */
202: public Principal getPrincipal() {
203: return m_principal;
204: }
205:
206: /**
207: * Returns the wiki name.
208: * @return the wiki name
209: */
210: public String getWiki() {
211: return m_wiki;
212: }
213:
214: /**
215: * Returns <code>true</code> if a Principal is a member of the group.
216: * Specifically, the Principal's <code>getName()</code> method must return
217: * the same value as one of the Principals in the group member list. The
218: * Principal's type does <em>not</em> need to match.
219: * @param principal the principal about whom membeship status is sought
220: * @return the result of the operation
221: */
222: public boolean isMember(Principal principal) {
223: return findMember(principal.getName()) != null;
224: }
225:
226: /**
227: * Returns the members of the group as an array of Principal objects.
228: * @return the members
229: */
230: public Principal[] members() {
231: return (Principal[]) m_members.toArray(new Principal[m_members
232: .size()]);
233: }
234:
235: /**
236: * Removes a Principal from the group.
237: *
238: * @param user the principal to remove
239: * @return <code>true</code> if the operation was successful
240: */
241: public synchronized boolean remove(Principal user) {
242: user = findMember(user.getName());
243:
244: if (user == null)
245: return false;
246:
247: m_members.remove(user);
248:
249: return true;
250: }
251:
252: /**
253: * Sets the created date.
254: * @param date the creation date
255: */
256: public synchronized void setCreated(Date date) {
257: m_created = date;
258: }
259:
260: /**
261: * Sets the creator of this Group.
262: * @param creator the creator
263: */
264: public final synchronized void setCreator(String creator) {
265: this .m_creator = creator;
266: }
267:
268: /**
269: * Sets the last-modified date
270: * @param date the last-modified date
271: */
272: public synchronized void setLastModified(Date date) {
273: m_modified = date;
274: }
275:
276: /**
277: * Sets the name of the user who last modified this group.
278: * @param modifier the modifier
279: */
280: public final synchronized void setModifier(String modifier) {
281: this .m_modifier = modifier;
282: }
283:
284: /**
285: * Returns a string representation of the Group.
286: * @return the string
287: * @see java.lang.Object#toString()
288: */
289: public String toString() {
290: StringBuffer sb = new StringBuffer();
291: sb.append("(Group " + getName() + ")");
292: return sb.toString();
293: }
294:
295: private Principal findMember(String name) {
296: for (Iterator i = m_members.iterator(); i.hasNext();) {
297: Principal member = (Principal) i.next();
298:
299: if (member.getName().equals(name)) {
300: return member;
301: }
302: }
303:
304: return null;
305: }
306:
307: }
|