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; 020 021import java.io.IOException; 022import java.security.Permission; 023import java.security.Principal; 024import java.text.MessageFormat; 025import java.util.List; 026import java.util.Map; 027import java.util.Properties; 028import java.util.ResourceBundle; 029import java.util.WeakHashMap; 030 031import javax.servlet.ServletException; 032import javax.servlet.http.HttpServletRequest; 033import javax.servlet.http.HttpServletResponse; 034 035import org.apache.commons.lang.StringUtils; 036import org.apache.log4j.Logger; 037import org.apache.wiki.WikiContext; 038import org.apache.wiki.WikiEngine; 039import org.apache.wiki.WikiSession; 040import org.apache.wiki.ajax.AjaxUtil; 041import org.apache.wiki.ajax.WikiAjaxDispatcherServlet; 042import org.apache.wiki.ajax.WikiAjaxServlet; 043import org.apache.wiki.api.engine.FilterManager; 044import org.apache.wiki.api.exceptions.NoRequiredPropertyException; 045import org.apache.wiki.api.exceptions.WikiException; 046import org.apache.wiki.api.filters.PageFilter; 047import org.apache.wiki.auth.permissions.AllPermission; 048import org.apache.wiki.auth.permissions.WikiPermission; 049import org.apache.wiki.auth.user.AbstractUserDatabase; 050import org.apache.wiki.auth.user.DuplicateUserException; 051import org.apache.wiki.auth.user.UserDatabase; 052import org.apache.wiki.auth.user.UserProfile; 053import org.apache.wiki.event.WikiEventListener; 054import org.apache.wiki.event.WikiEventManager; 055import org.apache.wiki.event.WikiSecurityEvent; 056import org.apache.wiki.filters.SpamFilter; 057import org.apache.wiki.i18n.InternationalizationManager; 058import org.apache.wiki.preferences.Preferences; 059import org.apache.wiki.ui.InputValidator; 060import org.apache.wiki.util.ClassUtil; 061import org.apache.wiki.workflow.Decision; 062import org.apache.wiki.workflow.DecisionRequiredException; 063import org.apache.wiki.workflow.Fact; 064import org.apache.wiki.workflow.Step; 065import org.apache.wiki.workflow.Workflow; 066import org.apache.wiki.workflow.WorkflowBuilder; 067import org.apache.wiki.workflow.WorkflowManager; 068 069 070/** 071 * Provides a facade for obtaining user information. 072 * @since 2.3 073 */ 074public class UserManager { 075 076 private static final String USERDATABASE_PACKAGE = "org.apache.wiki.auth.user"; 077 private static final String SESSION_MESSAGES = "profile"; 078 private static final String PARAM_EMAIL = "email"; 079 private static final String PARAM_FULLNAME = "fullname"; 080 private static final String PARAM_PASSWORD = "password"; 081 private static final String PARAM_LOGINNAME = "loginname"; 082 private static final String UNKNOWN_CLASS = "<unknown>"; 083 084 private WikiEngine m_engine; 085 086 private static final Logger log = Logger.getLogger(UserManager.class); 087 088 /** Message key for the "save profile" message. */ 089 private static final String PROP_DATABASE = "jspwiki.userdatabase"; 090 091 public static final String JSON_USERS = "users"; 092 093 // private static final String PROP_ACLMANAGER = "jspwiki.aclManager"; 094 095 /** Associates wiki sessions with profiles */ 096 private final Map<WikiSession,UserProfile> m_profiles = new WeakHashMap<>(); 097 098 /** The user database loads, manages and persists user identities */ 099 private UserDatabase m_database; 100 101 /** 102 * Initializes the engine for its nefarious purposes. 103 * @param engine the current wiki engine 104 * @param props the wiki engine initialization properties 105 */ 106 public void initialize( WikiEngine engine, Properties props ) 107 { 108 m_engine = engine; 109 110 // Attach the PageManager as a listener 111 // TODO: it would be better if we did this in PageManager directly 112 addWikiEventListener( engine.getPageManager() ); 113 114 //TODO: Replace with custom annotations. See JSPWIKI-566 115 WikiAjaxDispatcherServlet.registerServlet( JSON_USERS, new JSONUserModule(this), new AllPermission(null)); 116 } 117 118 /** 119 * Returns the UserDatabase employed by this WikiEngine. The UserDatabase is 120 * lazily initialized by this method, if it does not exist yet. If the 121 * initialization fails, this method will use the inner class 122 * DummyUserDatabase as a default (which is enough to get JSPWiki running). 123 * @return the dummy user database 124 * @since 2.3 125 */ 126 public UserDatabase getUserDatabase() 127 { 128 // FIXME: Must not throw RuntimeException, but something else. 129 if( m_database != null ) 130 { 131 return m_database; 132 } 133 134 String dbClassName = UNKNOWN_CLASS; 135 136 try 137 { 138 dbClassName = m_engine.getRequiredProperty( m_engine.getWikiProperties(), PROP_DATABASE ); 139 140 log.info("Attempting to load user database class "+dbClassName); 141 final Class<?> dbClass = ClassUtil.findClass( USERDATABASE_PACKAGE, dbClassName ); 142 m_database = (UserDatabase) dbClass.newInstance(); 143 m_database.initialize( m_engine, m_engine.getWikiProperties() ); 144 log.info("UserDatabase initialized."); 145 } 146 catch( final NoRequiredPropertyException e ) 147 { 148 log.error( "You have not set the '"+PROP_DATABASE+"'. You need to do this if you want to enable user management by JSPWiki.", e ); 149 } 150 catch( final ClassNotFoundException e ) 151 { 152 log.error( "UserDatabase class " + dbClassName + " cannot be found", e ); 153 } 154 catch( final InstantiationException e ) 155 { 156 log.error( "UserDatabase class " + dbClassName + " cannot be created", e ); 157 } 158 catch( final IllegalAccessException e ) 159 { 160 log.error( "You are not allowed to access this user database class", e ); 161 } 162 catch( final WikiSecurityException e ) 163 { 164 log.error( "Exception initializing user database: " + e.getMessage(), e ); 165 } 166 finally 167 { 168 if( m_database == null ) 169 { 170 log.info("I could not create a database object you specified (or didn't specify), so I am falling back to a default."); 171 m_database = new DummyUserDatabase(); 172 } 173 } 174 175 return m_database; 176 } 177 178 /** 179 * <p>Retrieves the {@link org.apache.wiki.auth.user.UserProfile}for the 180 * user in a wiki session. If the user is authenticated, the UserProfile 181 * returned will be the one stored in the user database; if one does not 182 * exist, a new one will be initialized and returned. If the user is 183 * anonymous or asserted, the UserProfile will <i>always</i> be newly 184 * initialized to prevent spoofing of identities. If a UserProfile needs to 185 * be initialized, its 186 * {@link org.apache.wiki.auth.user.UserProfile#isNew()} method will 187 * return <code>true</code>, and its login name will will be set 188 * automatically if the user is authenticated. Note that this method does 189 * not modify the retrieved (or newly created) profile otherwise; other 190 * fields in the user profile may be <code>null</code>.</p> 191 * <p>If a new UserProfile was created, but its 192 * {@link org.apache.wiki.auth.user.UserProfile#isNew()} method returns 193 * <code>false</code>, this method throws an {@link IllegalStateException}. 194 * This is meant as a quality check for UserDatabase providers; 195 * it should only be thrown if the implementation is faulty.</p> 196 * @param session the wiki session, which may not be <code>null</code> 197 * @return the user's profile, which will be newly initialized if the user 198 * is anonymous or asserted, or if the user cannot be found in the user 199 * database 200 */ 201 public UserProfile getUserProfile( WikiSession session ) 202 { 203 // Look up cached user profile 204 UserProfile profile = m_profiles.get( session ); 205 boolean newProfile = profile == null; 206 Principal user = null; 207 208 // If user is authenticated, figure out if this is an existing profile 209 if ( session.isAuthenticated() ) 210 { 211 user = session.getUserPrincipal(); 212 try 213 { 214 profile = getUserDatabase().find( user.getName() ); 215 newProfile = false; 216 } 217 catch( final NoSuchPrincipalException e ) 218 { 219 } 220 } 221 222 if ( newProfile ) 223 { 224 profile = getUserDatabase().newProfile(); 225 if ( user != null ) 226 { 227 profile.setLoginName( user.getName() ); 228 } 229 if ( !profile.isNew() ) 230 { 231 throw new IllegalStateException( 232 "New profile should be marked 'new'. Check your UserProfile implementation." ); 233 } 234 } 235 236 // Stash the profile for next time 237 m_profiles.put( session, profile ); 238 return profile; 239 } 240 241 /** 242 * <p> 243 * Saves the {@link org.apache.wiki.auth.user.UserProfile}for the user in 244 * a wiki session. This method verifies that a user profile to be saved 245 * doesn't collide with existing profiles; that is, the login name 246 * or full name is already used by another profile. If the profile 247 * collides, a <code>DuplicateUserException</code> is thrown. After saving 248 * the profile, the user database changes are committed, and the user's 249 * credential set is refreshed; if custom authentication is used, this means 250 * the user will be automatically be logged in. 251 * </p> 252 * <p> 253 * When the user's profile is saved successfully, this method fires a 254 * {@link WikiSecurityEvent#PROFILE_SAVE} event with the WikiSession as the 255 * source and the UserProfile as target. For existing profiles, if the 256 * user's full name changes, this method also fires a "name changed" 257 * event ({@link WikiSecurityEvent#PROFILE_NAME_CHANGED}) with the 258 * WikiSession as the source and an array containing the old and new 259 * UserProfiles, respectively. The <code>NAME_CHANGED</code> event allows 260 * the GroupManager and PageManager can change group memberships and 261 * ACLs if needed. 262 * </p> 263 * <p> 264 * Note that WikiSessions normally attach event listeners to the 265 * UserManager, so changes to the profile will automatically cause the 266 * correct Principals to be reloaded into the current WikiSession's Subject. 267 * </p> 268 * @param session the wiki session, which may not be <code>null</code> 269 * @param profile the user profile, which may not be <code>null</code> 270 * @throws DuplicateUserException if the proposed profile's login name or full name collides with another 271 * @throws WikiException if the save fails for some reason. If the current user does not have 272 * permission to save the profile, this will be a {@link org.apache.wiki.auth.WikiSecurityException}; 273 * if if the user profile must be approved before it can be saved, it will be a 274 * {@link org.apache.wiki.workflow.DecisionRequiredException}. All other WikiException 275 * indicate a condition that is not normal is probably due to mis-configuration 276 */ 277 public void setUserProfile( WikiSession session, UserProfile profile ) throws DuplicateUserException, WikiException 278 { 279 // Verify user is allowed to save profile! 280 final Permission p = new WikiPermission( m_engine.getApplicationName(), WikiPermission.EDIT_PROFILE_ACTION ); 281 if ( !m_engine.getAuthorizationManager().checkPermission( session, p ) ) 282 { 283 throw new WikiSecurityException( "You are not allowed to save wiki profiles." ); 284 } 285 286 // Check if profile is new, and see if container allows creation 287 final boolean newProfile = profile.isNew(); 288 289 // Check if another user profile already has the fullname or loginname 290 final UserProfile oldProfile = getUserProfile( session ); 291 final boolean nameChanged = ( oldProfile == null || oldProfile.getFullname() == null ) 292 ? false 293 : !( oldProfile.getFullname().equals( profile.getFullname() ) && 294 oldProfile.getLoginName().equals( profile.getLoginName() ) ); 295 UserProfile otherProfile; 296 try 297 { 298 otherProfile = getUserDatabase().findByLoginName( profile.getLoginName() ); 299 if ( otherProfile != null && !otherProfile.equals( oldProfile ) ) 300 { 301 throw new DuplicateUserException( "security.error.login.taken", profile.getLoginName() ); 302 } 303 } 304 catch( final NoSuchPrincipalException e ) 305 { 306 } 307 try 308 { 309 otherProfile = getUserDatabase().findByFullName( profile.getFullname() ); 310 if ( otherProfile != null && !otherProfile.equals( oldProfile ) ) 311 { 312 throw new DuplicateUserException( "security.error.fullname.taken", profile.getFullname() ); 313 } 314 } 315 catch( final NoSuchPrincipalException e ) 316 { 317 } 318 319 // For new accounts, create approval workflow for user profile save. 320 if ( newProfile && oldProfile != null && oldProfile.isNew() ) 321 { 322 startUserProfileCreationWorkflow(session, profile); 323 324 // If the profile doesn't need approval, then just log the user in 325 326 try 327 { 328 final AuthenticationManager mgr = m_engine.getAuthenticationManager(); 329 if ( newProfile && !mgr.isContainerAuthenticated() ) 330 { 331 mgr.login( session, null, profile.getLoginName(), profile.getPassword() ); 332 } 333 } 334 catch ( final WikiException e ) 335 { 336 throw new WikiSecurityException( e.getMessage(), e ); 337 } 338 339 // Alert all listeners that the profile changed... 340 // ...this will cause credentials to be reloaded in the wiki session 341 fireEvent( WikiSecurityEvent.PROFILE_SAVE, session, profile ); 342 } 343 344 // For existing accounts, just save the profile 345 else 346 { 347 // If login name changed, rename it first 348 if ( nameChanged && oldProfile != null && !oldProfile.getLoginName().equals( profile.getLoginName() ) ) 349 { 350 getUserDatabase().rename( oldProfile.getLoginName(), profile.getLoginName() ); 351 } 352 353 // Now, save the profile (userdatabase will take care of timestamps for us) 354 getUserDatabase().save( profile ); 355 356 if ( nameChanged ) 357 { 358 // Fire an event if the login name or full name changed 359 final UserProfile[] profiles = new UserProfile[] { oldProfile, profile }; 360 fireEvent( WikiSecurityEvent.PROFILE_NAME_CHANGED, session, profiles ); 361 } 362 else 363 { 364 // Fire an event that says we have new a new profile (new principals) 365 fireEvent( WikiSecurityEvent.PROFILE_SAVE, session, profile ); 366 } 367 } 368 } 369 370 public void startUserProfileCreationWorkflow( final WikiSession session, final UserProfile profile ) throws WikiException { 371 final WorkflowBuilder builder = WorkflowBuilder.getBuilder( m_engine ); 372 final Principal submitter = session.getUserPrincipal(); 373 final Step completionTask = m_engine.getTasksManager().buildSaveUserProfileTask( m_engine, session.getLocale() ); 374 375 // Add user profile attribute as Facts for the approver (if required) 376 final boolean hasEmail = profile.getEmail() != null; 377 final Fact[] facts = new Fact[ hasEmail ? 4 : 3]; 378 facts[0] = new Fact( WorkflowManager.WF_UP_CREATE_SAVE_FACT_PREFS_FULL_NAME, profile.getFullname() ); 379 facts[1] = new Fact( WorkflowManager.WF_UP_CREATE_SAVE_FACT_PREFS_LOGIN_NAME, profile.getLoginName() ); 380 facts[2] = new Fact( WorkflowManager.WF_UP_CREATE_SAVE_FACT_SUBMITTER, submitter.getName() ); 381 if ( hasEmail ) 382 { 383 facts[3] = new Fact( WorkflowManager.WF_UP_CREATE_SAVE_FACT_PREFS_EMAIL, profile.getEmail() ); 384 } 385 final Workflow workflow = builder.buildApprovalWorkflow( submitter, 386 WorkflowManager.WF_UP_CREATE_SAVE_APPROVER, 387 null, 388 WorkflowManager.WF_UP_CREATE_SAVE_DECISION_MESSAGE_KEY, 389 facts, 390 completionTask, 391 null ); 392 393 workflow.setAttribute( WorkflowManager.WF_UP_CREATE_SAVE_ATTR_SAVED_PROFILE, profile ); 394 m_engine.getWorkflowManager().start(workflow); 395 396 final boolean approvalRequired = workflow.getCurrentStep() instanceof Decision; 397 398 // If the profile requires approval, redirect user to message page 399 if ( approvalRequired ) 400 { 401 throw new DecisionRequiredException( "This profile must be approved before it becomes active" ); 402 } 403 } 404 405 /** 406 * <p> Extracts user profile parameters from the HTTP request and populates 407 * a UserProfile with them. The UserProfile will either be a copy of the 408 * user's existing profile (if one can be found), or a new profile (if not). 409 * The rules for populating the profile as as follows: </p> <ul> <li>If the 410 * <code>email</code> or <code>password</code> parameter values differ 411 * from those in the existing profile, the passed parameters override the 412 * old values.</li> <li>For new profiles, the user-supplied 413 * <code>fullname</code> parameter is always 414 * used; for existing profiles the existing value is used, and whatever 415 * value the user supplied is discarded. The wiki name is automatically 416 * computed by taking the full name and extracting all whitespace.</li> 417 * <li>In all cases, the 418 * created/last modified timestamps of the user's existing or new profile 419 * always override whatever values the user supplied.</li> <li>If 420 * container authentication is used, the login name property of the profile 421 * is set to the name of 422 * {@link org.apache.wiki.WikiSession#getLoginPrincipal()}. Otherwise, 423 * the value of the <code>loginname</code> parameter is used.</li> </ul> 424 * @param context the current wiki context 425 * @return a new, populated user profile 426 */ 427 public UserProfile parseProfile( WikiContext context ) 428 { 429 // Retrieve the user's profile (may have been previously cached) 430 final UserProfile profile = getUserProfile( context.getWikiSession() ); 431 final HttpServletRequest request = context.getHttpRequest(); 432 433 // Extract values from request stream (cleanse whitespace as needed) 434 String loginName = request.getParameter( PARAM_LOGINNAME ); 435 String password = request.getParameter( PARAM_PASSWORD ); 436 String fullname = request.getParameter( PARAM_FULLNAME ); 437 String email = request.getParameter( PARAM_EMAIL ); 438 loginName = InputValidator.isBlank( loginName ) ? null : loginName; 439 password = InputValidator.isBlank( password ) ? null : password; 440 fullname = InputValidator.isBlank( fullname ) ? null : fullname; 441 email = InputValidator.isBlank( email ) ? null : email; 442 443 // A special case if we have container authentication 444 // If authenticated, login name is always taken from container 445 if ( m_engine.getAuthenticationManager().isContainerAuthenticated() && 446 context.getWikiSession().isAuthenticated() ) 447 { 448 loginName = context.getWikiSession().getLoginPrincipal().getName(); 449 } 450 451 // Set the profile fields! 452 profile.setLoginName( loginName ); 453 profile.setEmail( email ); 454 profile.setFullname( fullname ); 455 profile.setPassword( password ); 456 return profile; 457 } 458 459 /** 460 * Validates a user profile, and appends any errors to the session errors 461 * list. If the profile is new, the password will be checked to make sure it 462 * isn't null. Otherwise, the password is checked for length and that it 463 * matches the value of the 'password2' HTTP parameter. Note that we have a 464 * special case when container-managed authentication is used and the user 465 * is not authenticated; this will always cause validation to fail. Any 466 * validation errors are added to the wiki session's messages collection 467 * (see {@link WikiSession#getMessages()}. 468 * @param context the current wiki context 469 * @param profile the supplied UserProfile 470 */ 471 public void validateProfile( WikiContext context, UserProfile profile ) 472 { 473 final boolean isNew = profile.isNew(); 474 final WikiSession session = context.getWikiSession(); 475 final InputValidator validator = new InputValidator( SESSION_MESSAGES, context ); 476 final ResourceBundle rb = Preferences.getBundle( context, InternationalizationManager.CORE_BUNDLE ); 477 478 // 479 // Query the SpamFilter first 480 // 481 final FilterManager fm = m_engine.getFilterManager(); 482 final List<PageFilter> ls = fm.getFilterList(); 483 for( final PageFilter pf : ls ) 484 { 485 if( pf instanceof SpamFilter ) 486 { 487 if( ((SpamFilter)pf).isValidUserProfile( context, profile ) == false ) 488 { 489 session.addMessage( SESSION_MESSAGES, "Invalid userprofile" ); 490 return; 491 } 492 break; 493 } 494 } 495 496 // If container-managed auth and user not logged in, throw an error 497 if ( m_engine.getAuthenticationManager().isContainerAuthenticated() 498 && !context.getWikiSession().isAuthenticated() ) 499 { 500 session.addMessage( SESSION_MESSAGES, rb.getString("security.error.createprofilebeforelogin") ); 501 } 502 503 validator.validateNotNull( profile.getLoginName(), rb.getString("security.user.loginname") ); 504 validator.validateNotNull( profile.getFullname(), rb.getString("security.user.fullname") ); 505 validator.validate( profile.getEmail(), rb.getString("security.user.email"), InputValidator.EMAIL ); 506 507 // If new profile, passwords must match and can't be null 508 if ( !m_engine.getAuthenticationManager().isContainerAuthenticated() ) 509 { 510 final String password = profile.getPassword(); 511 if ( password == null ) 512 { 513 if ( isNew ) 514 { 515 session.addMessage( SESSION_MESSAGES, rb.getString("security.error.blankpassword") ); 516 } 517 } 518 else 519 { 520 final HttpServletRequest request = context.getHttpRequest(); 521 final String password2 = ( request == null ) ? null : request.getParameter( "password2" ); 522 if ( !password.equals( password2 ) ) 523 { 524 session.addMessage( SESSION_MESSAGES, rb.getString("security.error.passwordnomatch") ); 525 } 526 } 527 } 528 529 UserProfile otherProfile; 530 final String fullName = profile.getFullname(); 531 final String loginName = profile.getLoginName(); 532 final String email = profile.getEmail(); 533 534 // It's illegal to use as a full name someone else's login name 535 try 536 { 537 otherProfile = getUserDatabase().find( fullName ); 538 if ( otherProfile != null && !profile.equals( otherProfile ) && !fullName.equals( otherProfile.getFullname() ) ) 539 { 540 final Object[] args = { fullName }; 541 session.addMessage( SESSION_MESSAGES, MessageFormat.format( rb.getString("security.error.illegalfullname"), args ) ); 542 } 543 } 544 catch ( final NoSuchPrincipalException e) 545 { /* It's clean */ } 546 547 // It's illegal to use as a login name someone else's full name 548 try 549 { 550 otherProfile = getUserDatabase().find( loginName ); 551 if ( otherProfile != null && !profile.equals( otherProfile ) && !loginName.equals( otherProfile.getLoginName() ) ) 552 { 553 final Object[] args = { loginName }; 554 session.addMessage( SESSION_MESSAGES, MessageFormat.format( rb.getString("security.error.illegalloginname"), args ) ); 555 } 556 } 557 catch ( final NoSuchPrincipalException e) 558 { /* It's clean */ } 559 560 // It's illegal to use multiple accounts with the same email 561 try 562 { 563 otherProfile = getUserDatabase().findByEmail( email ); 564 if ( otherProfile != null 565 && !profile.getUid().equals(otherProfile.getUid()) // Issue JSPWIKI-1042 566 && !profile.equals( otherProfile ) && StringUtils.lowerCase( email ).equals( StringUtils.lowerCase(otherProfile.getEmail() ) ) ) 567 { 568 final Object[] args = { email }; 569 session.addMessage( SESSION_MESSAGES, MessageFormat.format( rb.getString("security.error.email.taken"), args ) ); 570 } 571 } 572 catch ( final NoSuchPrincipalException e) 573 { /* It's clean */ } 574 } 575 576 /** 577 * A helper method for returning all of the known WikiNames in this system. 578 * 579 * @return An Array of Principals 580 * @throws WikiSecurityException If for reason the names cannot be fetched 581 */ 582 public Principal[] listWikiNames() 583 throws WikiSecurityException 584 { 585 return getUserDatabase().getWikiNames(); 586 } 587 588 /** 589 * This is a database that gets used if nothing else is available. It does 590 * nothing of note - it just mostly throws NoSuchPrincipalExceptions if 591 * someone tries to log in. 592 */ 593 public static class DummyUserDatabase extends AbstractUserDatabase 594 { 595 596 /** 597 * No-op. 598 * @param loginName the login name to delete 599 * @throws WikiSecurityException never... 600 */ 601 @Override 602 public void deleteByLoginName( String loginName ) throws WikiSecurityException 603 { 604 // No operation 605 } 606 607 /** 608 * No-op; always throws <code>NoSuchPrincipalException</code>. 609 * @param index the name to search for 610 * @return the user profile 611 * @throws NoSuchPrincipalException never... 612 */ 613 @Override 614 public UserProfile findByEmail(String index) throws NoSuchPrincipalException 615 { 616 throw new NoSuchPrincipalException("No user profiles available"); 617 } 618 619 /** 620 * No-op; always throws <code>NoSuchPrincipalException</code>. 621 * @param index the name to search for 622 * @return the user profile 623 * @throws NoSuchPrincipalException never... 624 */ 625 @Override 626 public UserProfile findByFullName(String index) throws NoSuchPrincipalException 627 { 628 throw new NoSuchPrincipalException("No user profiles available"); 629 } 630 631 /** 632 * No-op; always throws <code>NoSuchPrincipalException</code>. 633 * @param index the name to search for 634 * @return the user profile 635 * @throws NoSuchPrincipalException never... 636 */ 637 @Override 638 public UserProfile findByLoginName(String index) throws NoSuchPrincipalException 639 { 640 throw new NoSuchPrincipalException("No user profiles available"); 641 } 642 643 /** 644 * No-op; always throws <code>NoSuchPrincipalException</code>. 645 * @param uid the unique identifier to search for 646 * @return the user profile 647 * @throws NoSuchPrincipalException never... 648 */ 649 @Override 650 public UserProfile findByUid( String uid ) throws NoSuchPrincipalException 651 { 652 throw new NoSuchPrincipalException("No user profiles available"); 653 } 654 /** 655 * No-op; always throws <code>NoSuchPrincipalException</code>. 656 * @param index the name to search for 657 * @return the user profile 658 * @throws NoSuchPrincipalException never... 659 */ 660 @Override 661 public UserProfile findByWikiName(String index) throws NoSuchPrincipalException 662 { 663 throw new NoSuchPrincipalException("No user profiles available"); 664 } 665 666 /** 667 * No-op. 668 * @return a zero-length array 669 * @throws WikiSecurityException never... 670 */ 671 @Override 672 public Principal[] getWikiNames() throws WikiSecurityException 673 { 674 return new Principal[0]; 675 } 676 677 /** 678 * No-op. 679 * 680 * @param engine the wiki engine 681 * @param props the properties used to initialize the wiki engine 682 * @throws NoRequiredPropertyException never... 683 */ 684 @Override 685 public void initialize(WikiEngine engine, Properties props) throws NoRequiredPropertyException 686 { 687 } 688 689 /** 690 * No-op; always throws <code>NoSuchPrincipalException</code>. 691 * @param loginName the login name 692 * @param newName the proposed new login name 693 * @throws DuplicateUserException never... 694 * @throws WikiSecurityException never... 695 */ 696 @Override 697 public void rename( String loginName, String newName ) throws DuplicateUserException, WikiSecurityException 698 { 699 throw new NoSuchPrincipalException("No user profiles available"); 700 } 701 702 /** 703 * No-op. 704 * @param profile the user profile 705 * @throws WikiSecurityException never... 706 */ 707 @Override 708 public void save( UserProfile profile ) throws WikiSecurityException 709 { 710 } 711 712 } 713 714 // events processing ....................................................... 715 716 /** 717 * Registers a WikiEventListener with this instance. 718 * This is a convenience method. 719 * @param listener the event listener 720 */ 721 public synchronized void addWikiEventListener( WikiEventListener listener ) 722 { 723 WikiEventManager.addWikiEventListener( this, listener ); 724 } 725 726 /** 727 * Un-registers a WikiEventListener with this instance. 728 * This is a convenience method. 729 * @param listener the event listener 730 */ 731 public synchronized void removeWikiEventListener( WikiEventListener listener ) 732 { 733 WikiEventManager.removeWikiEventListener( this, listener ); 734 } 735 736 /** 737 * Fires a WikiSecurityEvent of the provided type, Principal and target Object 738 * to all registered listeners. 739 * 740 * @see org.apache.wiki.event.WikiSecurityEvent 741 * @param type the event type to be fired 742 * @param session the wiki session supporting the event 743 * @param profile the user profile (or array of user profiles), which may be <code>null</code> 744 */ 745 protected void fireEvent( int type, WikiSession session, Object profile ) 746 { 747 if ( WikiEventManager.isListening(this) ) 748 { 749 WikiEventManager.fireEvent(this,new WikiSecurityEvent(session,type,profile)); 750 } 751 } 752 753 /** 754 * Implements the JSON API for usermanager. 755 * <p> 756 * Even though this gets serialized whenever container shuts down/restarts, 757 * this gets reinstalled to the session when JSPWiki starts. This means 758 * that it's not actually necessary to save anything. 759 */ 760 public static final class JSONUserModule implements WikiAjaxServlet 761 { 762 private volatile UserManager m_manager; 763 764 /** 765 * Create a new JSONUserModule. 766 * @param mgr Manager 767 */ 768 public JSONUserModule( UserManager mgr ) 769 { 770 m_manager = mgr; 771 } 772 773 @Override 774 public String getServletMapping() { 775 return JSON_USERS; 776 } 777 778 @Override 779 public void service(HttpServletRequest req, HttpServletResponse resp, String actionName, List<String> params) throws ServletException, IOException { 780 try { 781 String uid = null; 782 if (params.size()<1) { 783 return; 784 } 785 uid = params.get(0); 786 log.debug("uid="+uid); 787 if (StringUtils.isNotBlank(uid)) { 788 final UserProfile prof = getUserInfo(uid); 789 resp.getWriter().write(AjaxUtil.toJson(prof)); 790 } 791 } catch (final NoSuchPrincipalException e) { 792 throw new ServletException(e); 793 } 794 } 795 796 /** 797 * Directly returns the UserProfile object attached to an uid. 798 * 799 * @param uid The user id (e.g. WikiName) 800 * @return A UserProfile object 801 * @throws NoSuchPrincipalException If such a name does not exist. 802 */ 803 public UserProfile getUserInfo( String uid ) 804 throws NoSuchPrincipalException 805 { 806 if( m_manager != null ) 807 { 808 final UserProfile prof = m_manager.getUserDatabase().find( uid ); 809 810 return prof; 811 } 812 813 throw new IllegalStateException("The manager is offline."); 814 } 815 } 816}