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