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}