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 java.security.Principal;
022import java.util.HashMap;
023import java.util.HashSet;
024import java.util.Map;
025import java.util.Properties;
026import java.util.Set;
027import java.util.StringTokenizer;
028
029import javax.servlet.http.HttpServletRequest;
030
031import org.apache.commons.lang.ArrayUtils;
032import org.apache.log4j.Logger;
033import org.apache.wiki.WikiContext;
034import org.apache.wiki.WikiEngine;
035import org.apache.wiki.WikiSession;
036import org.apache.wiki.api.exceptions.NoRequiredPropertyException;
037import org.apache.wiki.api.exceptions.WikiException;
038import org.apache.wiki.auth.AuthenticationManager;
039import org.apache.wiki.auth.Authorizer;
040import org.apache.wiki.auth.GroupPrincipal;
041import org.apache.wiki.auth.NoSuchPrincipalException;
042import org.apache.wiki.auth.WikiPrincipal;
043import org.apache.wiki.auth.WikiSecurityException;
044import org.apache.wiki.auth.user.UserProfile;
045import org.apache.wiki.event.WikiEvent;
046import org.apache.wiki.event.WikiEventListener;
047import org.apache.wiki.event.WikiEventManager;
048import org.apache.wiki.event.WikiSecurityEvent;
049import org.apache.wiki.ui.InputValidator;
050import 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 */
068public 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}