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