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