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.HashMap;
023 import java.util.HashSet;
024 import java.util.Map;
025 import java.util.Properties;
026 import java.util.Set;
027 import java.util.StringTokenizer;
028
029 import javax.servlet.http.HttpServletRequest;
030
031 import org.apache.commons.lang.ArrayUtils;
032 import org.apache.log4j.Logger;
033 import org.apache.wiki.WikiContext;
034 import org.apache.wiki.WikiEngine;
035 import org.apache.wiki.WikiSession;
036 import org.apache.wiki.api.exceptions.NoRequiredPropertyException;
037 import org.apache.wiki.api.exceptions.WikiException;
038 import org.apache.wiki.auth.AuthenticationManager;
039 import org.apache.wiki.auth.Authorizer;
040 import org.apache.wiki.auth.GroupPrincipal;
041 import org.apache.wiki.auth.NoSuchPrincipalException;
042 import org.apache.wiki.auth.WikiPrincipal;
043 import org.apache.wiki.auth.WikiSecurityException;
044 import org.apache.wiki.auth.user.UserProfile;
045 import org.apache.wiki.event.WikiEvent;
046 import org.apache.wiki.event.WikiEventListener;
047 import org.apache.wiki.event.WikiEventManager;
048 import org.apache.wiki.event.WikiSecurityEvent;
049 import org.apache.wiki.ui.InputValidator;
050 import org.apache.wiki.util.ClassUtil;
051
052
053 /**
054 * <p>
055 * Facade class for storing, retrieving and managing wiki groups on behalf of
056 * AuthorizationManager, JSPs and other presentation-layer classes. GroupManager
057 * works in collaboration with a back-end {@link GroupDatabase}, which persists
058 * groups to permanent storage.
059 * </p>
060 * <p>
061 * <em>Note: prior to JSPWiki 2.4.19, GroupManager was an interface; it
062 * is now a concrete, final class. The aspects of GroupManager which previously
063 * extracted group information from storage (e.g., wiki pages) have been
064 * refactored into the GroupDatabase interface.</em>
065 * </p>
066 * @since 2.4.19
067 */
068 public class GroupManager implements Authorizer, WikiEventListener {
069
070 /** Key used for adding UI messages to a user's WikiSession. */
071 public static final String MESSAGES_KEY = "group";
072
073 private static final String PROP_GROUPDATABASE = "jspwiki.groupdatabase";
074
075 static final Logger log = Logger.getLogger( GroupManager.class );
076
077 protected WikiEngine m_engine;
078
079 protected WikiEventListener m_groupListener;
080
081 private GroupDatabase m_groupDatabase = null;
082
083 /** Map with GroupPrincipals as keys, and Groups as values */
084 private final Map<Principal, Group> m_groups = new HashMap<Principal, Group>();
085
086 /**
087 * <p>
088 * Returns a GroupPrincipal matching a given name. If a group cannot be
089 * found, return <code>null</code>.
090 * </p>
091 * @param name Name of the group. This is case-sensitive.
092 * @return A DefaultGroup instance.
093 */
094 public Principal findRole( String name )
095 {
096 try
097 {
098 Group group = getGroup( name );
099 return group.getPrincipal();
100 }
101 catch( NoSuchPrincipalException e )
102 {
103 return null;
104 }
105 }
106
107 /**
108 * Returns the Group matching a given name. If the group cannot be found,
109 * this method throws a <code>NoSuchPrincipalException</code>.
110 * @param name the name of the group to find
111 * @return the group
112 * @throws NoSuchPrincipalException if the group cannot be found
113 */
114 public Group getGroup( String name ) throws NoSuchPrincipalException
115 {
116 Group group = m_groups.get( new GroupPrincipal( name ) );
117 if ( group != null )
118 {
119 return group;
120 }
121 throw new NoSuchPrincipalException( "Group " + name + " not found." );
122 }
123
124 /**
125 * Returns the current external {@link GroupDatabase} in use. This method
126 * is guaranteed to return a properly-initialized GroupDatabase, unless
127 * it could not be initialized. In that case, this method throws
128 * a {@link org.apache.wiki.api.exceptions.WikiException}. The GroupDatabase
129 * is lazily initialized.
130 * @throws org.apache.wiki.auth.WikiSecurityException if the GroupDatabase could
131 * not be initialized
132 * @return the current GroupDatabase
133 * @since 2.3
134 */
135 public GroupDatabase getGroupDatabase() throws WikiSecurityException
136 {
137 if ( m_groupDatabase != null )
138 {
139 return m_groupDatabase;
140 }
141
142 String dbClassName = "<unknown>";
143 String dbInstantiationError = null;
144 Throwable cause = null;
145 try
146 {
147 Properties props = m_engine.getWikiProperties();
148 dbClassName = props.getProperty( PROP_GROUPDATABASE );
149 if ( dbClassName == null )
150 {
151 dbClassName = XMLGroupDatabase.class.getName();
152 }
153 log.info( "Attempting to load group database class " + dbClassName );
154 Class<?> dbClass = ClassUtil.findClass( "org.apache.wiki.auth.authorize", dbClassName );
155 m_groupDatabase = (GroupDatabase) dbClass.newInstance();
156 m_groupDatabase.initialize( m_engine, m_engine.getWikiProperties() );
157 log.info( "Group database initialized." );
158 }
159 catch( ClassNotFoundException e )
160 {
161 log.error( "GroupDatabase class " + dbClassName + " cannot be found.", e );
162 dbInstantiationError = "Failed to locate GroupDatabase class " + dbClassName;
163 cause = e;
164 }
165 catch( InstantiationException e )
166 {
167 log.error( "GroupDatabase class " + dbClassName + " cannot be created.", e );
168 dbInstantiationError = "Failed to create GroupDatabase class " + dbClassName;
169 cause = e;
170 }
171 catch( IllegalAccessException e )
172 {
173 log.error( "You are not allowed to access group database class " + dbClassName + ".", e );
174 dbInstantiationError = "Access GroupDatabase class " + dbClassName + " denied";
175 cause = e;
176 }
177 catch( NoRequiredPropertyException e )
178 {
179 log.error( "Missing property: " + e.getMessage() + "." );
180 dbInstantiationError = "Missing property: " + e.getMessage();
181 cause = e;
182 }
183
184 if( dbInstantiationError != null )
185 {
186 throw new WikiSecurityException( dbInstantiationError + " Cause: " + (cause != null ? cause.getMessage() : ""), cause );
187 }
188
189 return m_groupDatabase;
190 }
191
192 /**
193 * Returns an array of GroupPrincipals this GroupManager knows about. This
194 * method will return an array of GroupPrincipal objects corresponding to
195 * the wiki groups managed by this class. This method actually returns a
196 * defensive copy of an internally stored hashmap.
197 * @return an array of Principals representing the roles
198 */
199 public Principal[] getRoles()
200 {
201 return m_groups.keySet().toArray( new Principal[m_groups.size()] );
202 }
203
204 /**
205 * Initializes the group cache by initializing the group database and
206 * obtaining a list of all of the groups it stores.
207 * @param engine the wiki engine
208 * @param props the properties used to initialize the wiki engine
209 * @see GroupDatabase#initialize(org.apache.wiki.WikiEngine,
210 * java.util.Properties)
211 * @see GroupDatabase#groups()
212 * @throws WikiSecurityException if GroupManager cannot be initialized
213 */
214 public void initialize( WikiEngine engine, Properties props ) throws WikiSecurityException
215 {
216 m_engine = engine;
217
218 try
219 {
220 m_groupDatabase = getGroupDatabase();
221 }
222 catch ( WikiException e )
223 {
224 throw new WikiSecurityException( e.getMessage(), e );
225 }
226
227 // Load all groups from the database into the cache
228 Group[] groups = m_groupDatabase.groups();
229 synchronized( m_groups )
230 {
231 for( Group group : groups )
232 {
233 // Add new group to cache; fire GROUP_ADD event
234 m_groups.put( group.getPrincipal(), group );
235 fireEvent( WikiSecurityEvent.GROUP_ADD, group );
236 }
237 }
238
239 // Make the GroupManager listen for WikiEvents (WikiSecurityEvents for changed user profiles)
240 engine.getUserManager().addWikiEventListener( this );
241
242 // Success!
243 log.info( "Authorizer GroupManager initialized successfully; loaded " + groups.length + " group(s)." );
244
245 }
246
247 /**
248 * <p>
249 * Determines whether the Subject associated with a WikiSession is in a
250 * particular role. This method takes two parameters: the WikiSession
251 * containing the subject and the desired role ( which may be a Role or a
252 * Group). If either parameter is <code>null</code>, or if the user is
253 * not authenticated, this method returns <code>false</code>.
254 * </p>
255 * <p>
256 * With respect to this implementation, the supplied Principal must be a
257 * GroupPrincipal. The Subject posesses the "role" if it the session is
258 * authenticated <em>and</em> a Subject's principal is a member of the
259 * corresponding Group. This method simply finds the Group in question, then
260 * delegates to {@link Group#isMember(Principal)} for each of the principals
261 * in the Subject's principal set.
262 * </p>
263 * @param session the current WikiSession
264 * @param role the role to check
265 * @return <code>true</code> if the user is considered to be in the role,
266 * <code>false</code> otherwise
267 */
268 public boolean isUserInRole( WikiSession session, Principal role )
269 {
270 // Always return false if session/role is null, or if
271 // role isn't a GroupPrincipal
272 if ( session == null || role == null || !( role instanceof GroupPrincipal ) || !session.isAuthenticated() )
273 {
274 return false;
275 }
276
277 // Get the group we're examining
278 Group group = m_groups.get( role );
279 if ( group == null )
280 {
281 return false;
282 }
283
284 // Check each user principal to see if it belongs to the group
285 for ( Principal principal : session.getPrincipals() )
286 {
287 if ( AuthenticationManager.isUserPrincipal( principal ) && group.isMember( principal ) )
288 {
289 return true;
290 }
291 }
292 return false;
293 }
294
295 /**
296 * <p>
297 * Extracts group name and members from passed parameters and populates an
298 * existing Group with them. The Group will either be a copy of an existing
299 * Group (if one can be found), or a new, unregistered Group (if not).
300 * Optionally, this method can throw a WikiSecurityException if the Group
301 * does not yet exist in the GroupManager cache.
302 * </p>
303 * <p>
304 * The <code>group</code> parameter in the HTTP request contains the Group
305 * name to look up and populate. The <code>members</code> parameter
306 * contains the member list. If these differ from those in the existing
307 * group, the passed values override the old values.
308 * </p>
309 * <p>
310 * This method does not commit the new Group to the GroupManager cache. To
311 * do that, use {@link #setGroup(WikiSession, Group)}.
312 * </p>
313 * @param name the name of the group to construct
314 * @param memberLine the line of text containing the group membership list
315 * @param create whether this method should create a new, empty Group if one
316 * with the requested name is not found. If <code>false</code>,
317 * groups that do not exist will cause a
318 * <code>NoSuchPrincipalException</code> to be thrown
319 * @return a new, populated group
320 * @see org.apache.wiki.auth.authorize.Group#RESTRICTED_GROUPNAMES
321 * @throws WikiSecurityException if the group name isn't allowed, or if
322 * <code>create</code> is <code>false</code>
323 * and the Group named <code>name</code> does not exist
324 */
325 public Group parseGroup( String name, String memberLine, boolean create ) throws WikiSecurityException
326 {
327 // If null name parameter, it's because someone's creating a new group
328 if ( name == null )
329 {
330 if ( create )
331 {
332 name = "MyGroup";
333 }
334 else
335 {
336 throw new WikiSecurityException( "Group name cannot be blank." );
337 }
338 }
339 else if ( ArrayUtils.contains( Group.RESTRICTED_GROUPNAMES, name ) )
340 {
341 // Certain names are forbidden
342 throw new WikiSecurityException( "Illegal group name: " + name );
343 }
344 name = name.trim();
345
346 // Normalize the member line
347 if ( InputValidator.isBlank( memberLine ) )
348 {
349 memberLine = "";
350 }
351 memberLine = memberLine.trim();
352
353 // Create or retrieve the group (may have been previously cached)
354 Group group = new Group( name, m_engine.getApplicationName() );
355 try
356 {
357 Group existingGroup = getGroup( name );
358
359 // If existing, clone it
360 group.setCreator( existingGroup.getCreator() );
361 group.setCreated( existingGroup.getCreated() );
362 group.setModifier( existingGroup.getModifier() );
363 group.setLastModified( existingGroup.getLastModified() );
364 for( Principal existingMember : existingGroup.members() )
365 {
366 group.add( existingMember );
367 }
368 }
369 catch( NoSuchPrincipalException e )
370 {
371 // It's a new group.... throw error if we don't create new ones
372 if ( !create )
373 {
374 throw new NoSuchPrincipalException( "Group '" + name + "' does not exist." );
375 }
376 }
377
378 // If passed members not empty, overwrite
379 String[] members = extractMembers( memberLine );
380 if ( members.length > 0 )
381 {
382 group.clear();
383 for( String member : members )
384 {
385 group.add( new WikiPrincipal( member ) );
386 }
387 }
388
389 return group;
390 }
391
392 /**
393 * <p>
394 * Extracts group name and members from the HTTP request and populates an
395 * existing Group with them. The Group will either be a copy of an existing
396 * Group (if one can be found), or a new, unregistered Group (if not).
397 * Optionally, this method can throw a WikiSecurityException if the Group
398 * does not yet exist in the GroupManager cache.
399 * </p>
400 * <p>
401 * The <code>group</code> parameter in the HTTP request contains the Group
402 * name to look up and populate. The <code>members</code> parameter
403 * contains the member list. If these differ from those in the existing
404 * group, the passed values override the old values.
405 * </p>
406 * <p>
407 * This method does not commit the new Group to the GroupManager cache. To
408 * do that, use {@link #setGroup(WikiSession, Group)}.
409 * </p>
410 * @param context the current wiki context
411 * @param create whether this method should create a new, empty Group if one
412 * with the requested name is not found. If <code>false</code>,
413 * groups that do not exist will cause a
414 * <code>NoSuchPrincipalException</code> to be thrown
415 * @return a new, populated group
416 * @throws WikiSecurityException if the group name isn't allowed, or if
417 * <code>create</code> is <code>false</code>
418 * and the Group does not exist
419 */
420 public Group parseGroup( WikiContext context, boolean create ) throws WikiSecurityException
421 {
422 // Extract parameters
423 HttpServletRequest request = context.getHttpRequest();
424 String name = request.getParameter( "group" );
425 String memberLine = request.getParameter( "members" );
426
427 // Create the named group; we pass on any NoSuchPrincipalExceptions
428 // that may be thrown if create == false, or WikiSecurityExceptions
429 Group group = parseGroup( name, memberLine, create );
430
431 // If no members, add the current user by default
432 if ( group.members().length == 0 )
433 {
434 group.add( context.getWikiSession().getUserPrincipal() );
435 }
436
437 return group;
438 }
439
440 /**
441 * Removes a named Group from the group database. If not found, throws a
442 * <code>NoSuchPrincipalException</code>. After removal, this method will
443 * commit the delete to the back-end group database. It will also fire a
444 * {@link org.apache.wiki.event.WikiSecurityEvent#GROUP_REMOVE} event with
445 * the GroupManager instance as the source and the Group as target.
446 * If <code>index</code> is <code>null</code>, this method throws
447 * an {@link IllegalArgumentException}.
448 * @param index the group to remove
449 * @throws WikiSecurityException if the Group cannot be removed by
450 * the back-end
451 * @see org.apache.wiki.auth.authorize.GroupDatabase#delete(Group)
452 */
453 public void removeGroup( String index ) throws WikiSecurityException
454 {
455 if ( index == null )
456 {
457 throw new IllegalArgumentException( "Group cannot be null." );
458 }
459
460 Group group = m_groups.get( new GroupPrincipal( index ) );
461 if ( group == null )
462 {
463 throw new NoSuchPrincipalException( "Group " + index + " not found" );
464 }
465
466 // Delete the group
467 // TODO: need rollback procedure
468 synchronized( m_groups )
469 {
470 m_groups.remove( group.getPrincipal() );
471 }
472 m_groupDatabase.delete( group );
473 fireEvent( WikiSecurityEvent.GROUP_REMOVE, group );
474 }
475
476 /**
477 * <p>
478 * Saves the {@link Group} created by a user in a wiki session. This method
479 * registers the Group with the GroupManager and saves it to the back-end
480 * database. If an existing Group with the same name already exists, the new
481 * group will overwrite it. After saving the Group, the group database
482 * changes are committed.
483 * </p>
484 * <p>
485 * This method fires the following events:
486 * </p>
487 * <ul>
488 * <li><strong>When creating a new Group</strong>, this method fires a
489 * {@link org.apache.wiki.event.WikiSecurityEvent#GROUP_ADD} with the
490 * GroupManager instance as its source and the new Group as the target.</li>
491 * <li><strong>When overwriting an existing Group</strong>, this method
492 * fires a new {@link org.apache.wiki.event.WikiSecurityEvent#GROUP_REMOVE}
493 * with this GroupManager instance as the source, and the new Group as the
494 * target. It then fires a
495 * {@link org.apache.wiki.event.WikiSecurityEvent#GROUP_ADD} event with the
496 * same source and target.</li>
497 * </ul>
498 * <p>
499 * In addition, if the save or commit actions fail, this method will attempt
500 * to restore the older version of the wiki group if it exists. This will
501 * result in a <code>GROUP_REMOVE</code> event (for the new version of the
502 * Group) followed by a <code>GROUP_ADD</code> event (to indicate
503 * restoration of the old version).
504 * </p>
505 * <p>
506 * This method will register the new Group with the GroupManager. For example,
507 * {@link org.apache.wiki.auth.AuthenticationManager} attaches each
508 * WikiSession as a GroupManager listener. Thus, the act of registering a
509 * Group with <code>setGroup</code> means that all WikiSessions will
510 * automatically receive group add/change/delete events immediately.
511 * </p>
512 * @param session the wiki session, which may not be <code>null</code>
513 * @param group the Group, which may not be <code>null</code>
514 * @throws WikiSecurityException if the Group cannot be saved by the back-end
515 */
516 public void setGroup( WikiSession session, Group group ) throws WikiSecurityException
517 {
518 // TODO: check for appropriate permissions
519
520 // If group already exists, delete it; fire GROUP_REMOVE event
521 Group oldGroup = m_groups.get( group.getPrincipal() );
522 if ( oldGroup != null )
523 {
524 fireEvent( WikiSecurityEvent.GROUP_REMOVE, oldGroup );
525 synchronized( m_groups )
526 {
527 m_groups.remove( oldGroup.getPrincipal() );
528 }
529 }
530
531 // Copy existing modifier info & timestamps
532 if ( oldGroup != null )
533 {
534 group.setCreator( oldGroup.getCreator() );
535 group.setCreated( oldGroup.getCreated() );
536 group.setModifier( oldGroup.getModifier() );
537 group.setLastModified( oldGroup.getLastModified() );
538 }
539
540 // Add new group to cache; announce GROUP_ADD event
541 synchronized( m_groups )
542 {
543 m_groups.put( group.getPrincipal(), group );
544 }
545 fireEvent( WikiSecurityEvent.GROUP_ADD, group );
546
547 // Save the group to back-end database; if it fails,
548 // roll back to previous state. Note that the back-end
549 // MUST timestammp the create/modify fields in the Group.
550 try
551 {
552 m_groupDatabase.save( group, session.getUserPrincipal() );
553 }
554
555 // We got an exception! Roll back...
556 catch( WikiSecurityException e )
557 {
558 if ( oldGroup != null )
559 {
560 // Restore previous version, re-throw...
561 fireEvent( WikiSecurityEvent.GROUP_REMOVE, group );
562 fireEvent( WikiSecurityEvent.GROUP_ADD, oldGroup );
563 synchronized( m_groups )
564 {
565 m_groups.put( oldGroup.getPrincipal(), oldGroup );
566 }
567 throw new WikiSecurityException( e.getMessage() + " (rolled back to previous version).", e );
568 }
569 // Re-throw security exception
570 throw new WikiSecurityException( e.getMessage(), e );
571 }
572 }
573
574 /**
575 * Validates a Group, and appends any errors to the session errors list. Any
576 * validation errors are added to the wiki session's messages collection
577 * (see {@link WikiSession#getMessages()}.
578 * @param context the current wiki context
579 * @param group the supplied Group
580 */
581 public void validateGroup( WikiContext context, Group group )
582 {
583 InputValidator validator = new InputValidator( MESSAGES_KEY, context );
584
585 // Name cannot be null or one of the restricted names
586 try
587 {
588 checkGroupName( context, group.getName() );
589 }
590 catch( WikiSecurityException e )
591 {
592
593 }
594
595 // Member names must be "safe" strings
596 Principal[] members = group.members();
597 for( int i = 0; i < members.length; i++ )
598 {
599 validator.validateNotNull( members[i].getName(), "Full name", InputValidator.ID );
600 }
601 }
602
603 /**
604 * Extracts carriage-return separated members into a Set of String objects.
605 * @param memberLine the list of members
606 * @return the list of members
607 */
608 protected String[] extractMembers( String memberLine )
609 {
610 Set<String> members = new HashSet<String>();
611 if ( memberLine != null )
612 {
613 StringTokenizer tok = new StringTokenizer( memberLine, "\n" );
614 while( tok.hasMoreTokens() )
615 {
616 String uid = tok.nextToken().trim();
617 if ( uid != null && uid.length() > 0 )
618 {
619 members.add( uid );
620 }
621 }
622 }
623 return members.toArray( new String[members.size()] );
624 }
625
626 /**
627 * Checks if a String is blank or a restricted Group name, and if it is,
628 * appends an error to the WikiSession's message list.
629 * @param context the wiki context
630 * @param name the Group name to test
631 * @throws WikiSecurityException if <code>session</code> is
632 * <code>null</code> or the Group name is illegal
633 * @see Group#RESTRICTED_GROUPNAMES
634 */
635 protected void checkGroupName( WikiContext context, String name ) throws WikiSecurityException
636 {
637 //TODO: groups cannot have the same name as a user
638
639 // Name cannot be null
640 InputValidator validator = new InputValidator( MESSAGES_KEY, context );
641 validator.validateNotNull( name, "Group name" );
642
643 // Name cannot be one of the restricted names either
644 if( ArrayUtils.contains( Group.RESTRICTED_GROUPNAMES, name ) )
645 {
646 throw new WikiSecurityException( "The group name '" + name + "' is illegal. Choose another." );
647 }
648 }
649
650
651 // events processing .......................................................
652
653 /**
654 * Registers a WikiEventListener with this instance.
655 * This is a convenience method.
656 * @param listener the event listener
657 */
658 public synchronized void addWikiEventListener( WikiEventListener listener )
659 {
660 WikiEventManager.addWikiEventListener( this, listener );
661 }
662
663 /**
664 * Un-registers a WikiEventListener with this instance.
665 * This is a convenience method.
666 * @param listener the event listener
667 */
668 public synchronized void removeWikiEventListener( WikiEventListener listener )
669 {
670 WikiEventManager.removeWikiEventListener( this, listener );
671 }
672
673 /**
674 * Fires a WikiSecurityEvent of the provided type, Principal and target Object
675 * to all registered listeners.
676 *
677 * @see org.apache.wiki.event.WikiSecurityEvent
678 * @param type the event type to be fired
679 * @param target the changed Object, which may be <code>null</code>
680 */
681 protected void fireEvent( int type, Object target )
682 {
683 if ( WikiEventManager.isListening(this) )
684 {
685 WikiEventManager.fireEvent(this,new WikiSecurityEvent(this,type,target));
686 }
687 }
688
689 /**
690 * Listens for {@link org.apache.wiki.event.WikiSecurityEvent#PROFILE_NAME_CHANGED}
691 * events. If a user profile's name changes, each group is inspected. If an entry contains
692 * a name that has changed, it is replaced with the new one. No group events are emitted
693 * as a consequence of this method, because the group memberships are still the same; it is
694 * only the representations of the names within that are changing.
695 * @param event the incoming event
696 */
697 public void actionPerformed(WikiEvent event)
698 {
699 if (! ( event instanceof WikiSecurityEvent ) )
700 {
701 return;
702 }
703
704 WikiSecurityEvent se = (WikiSecurityEvent)event;
705 if ( se.getType() == WikiSecurityEvent.PROFILE_NAME_CHANGED )
706 {
707 WikiSession session = se.getSrc();
708 UserProfile[] profiles = (UserProfile[])se.getTarget();
709 Principal[] oldPrincipals = new Principal[] {
710 new WikiPrincipal( profiles[0].getLoginName() ),
711 new WikiPrincipal( profiles[0].getFullname() ),
712 new WikiPrincipal( profiles[0].getWikiName() ) };
713 Principal newPrincipal = new WikiPrincipal( profiles[1].getFullname() );
714
715 // Examine each group
716 int groupsChanged = 0;
717 try
718 {
719 for ( Group group : m_groupDatabase.groups() )
720 {
721 boolean groupChanged = false;
722 for ( Principal oldPrincipal : oldPrincipals )
723 {
724 if ( group.isMember( oldPrincipal ) )
725 {
726 group.remove( oldPrincipal );
727 group.add( newPrincipal );
728 groupChanged = true;
729 }
730 }
731 if ( groupChanged )
732 {
733 setGroup( session, group );
734 groupsChanged++;
735 }
736 }
737 }
738 catch ( WikiException e )
739 {
740 // Oooo! This is really bad...
741 log.error( "Could not change user name in Group lists because of GroupDatabase error:" + e.getMessage() );
742 }
743 log.info( "Profile name change for '" + newPrincipal.toString() +
744 "' caused " + groupsChanged + " groups to change also." );
745 }
746 }
747
748 }