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}