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.logging.log4j.LogManager; 023import org.apache.logging.log4j.Logger; 024import org.apache.wiki.ajax.AjaxUtil; 025import org.apache.wiki.ajax.WikiAjaxDispatcherServlet; 026import org.apache.wiki.ajax.WikiAjaxServlet; 027import org.apache.wiki.api.core.Context; 028import org.apache.wiki.api.core.Engine; 029import org.apache.wiki.api.core.Session; 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.DummyUserDatabase; 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.FilterManager; 043import org.apache.wiki.filters.SpamFilter; 044import org.apache.wiki.i18n.InternationalizationManager; 045import org.apache.wiki.pages.PageManager; 046import org.apache.wiki.preferences.Preferences; 047import org.apache.wiki.tasks.TasksManager; 048import org.apache.wiki.ui.InputValidator; 049import org.apache.wiki.util.ClassUtil; 050import org.apache.wiki.util.TextUtil; 051import org.apache.wiki.workflow.Decision; 052import org.apache.wiki.workflow.DecisionRequiredException; 053import org.apache.wiki.workflow.Fact; 054import org.apache.wiki.workflow.Step; 055import org.apache.wiki.workflow.Workflow; 056import org.apache.wiki.workflow.WorkflowBuilder; 057import org.apache.wiki.workflow.WorkflowManager; 058 059import jakarta.servlet.ServletException; 060import jakarta.servlet.http.HttpServletRequest; 061import jakarta.servlet.http.HttpServletResponse; 062import java.io.IOException; 063import java.security.Permission; 064import java.security.Principal; 065import java.text.MessageFormat; 066import java.util.List; 067import java.util.Map; 068import java.util.NoSuchElementException; 069import java.util.Properties; 070import java.util.ResourceBundle; 071import java.util.WeakHashMap; 072import org.apache.wiki.auth.authorize.Group; 073import org.apache.wiki.auth.authorize.GroupManager; 074import tools.jackson.databind.ObjectMapper; 075import tools.jackson.databind.node.ObjectNode; 076 077 078 079/** 080 * Default implementation for {@link UserManager}. 081 * 082 * @since 2.3 083 */ 084public class DefaultUserManager implements UserManager { 085 086 private static final String USERDATABASE_PACKAGE = "org.apache.wiki.auth.user"; 087 public static final String SESSION_MESSAGES = "profile"; 088 private static final String PARAM_EMAIL = "email"; 089 private static final String PARAM_FULLNAME = "fullname"; 090 private static final String PARAM_PASSWORD = "password"; 091 private static final String PARAM_LOGINNAME = "loginname"; 092 private static final String UNKNOWN_CLASS = "<unknown>"; 093 094 private Engine m_engine; 095 096 private static final Logger LOG = LogManager.getLogger( DefaultUserManager.class); 097 098 /** Associates wiki sessions with profiles */ 099 private final Map< Session, UserProfile > m_profiles = new WeakHashMap<>(); 100 101 /** The user database loads, manages and persists user identities */ 102 private UserDatabase m_database; 103 104 /** {@inheritDoc} */ 105 @Override 106 public void initialize( final Engine engine, final Properties props ) { 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.getManager( PageManager.class ) ); 112 113 //TODO: Replace with custom annotations. See JSPWIKI-566 114 WikiAjaxDispatcherServlet.registerServlet( JSON_USERS, new JSONUserModule(this), new AllPermission(null)); 115 } 116 117 /** {@inheritDoc} */ 118 @Override 119 public UserDatabase getUserDatabase() { 120 if( m_database != null ) { 121 return m_database; 122 } 123 124 String dbClassName = UNKNOWN_CLASS; 125 126 try { 127 dbClassName = TextUtil.getRequiredProperty( m_engine.getWikiProperties(), PROP_DATABASE ); 128 129 LOG.info( "Attempting to load user database class {}", dbClassName ); 130 m_database = ClassUtil.buildInstance( USERDATABASE_PACKAGE, dbClassName ); 131 m_database.initialize( m_engine, m_engine.getWikiProperties() ); 132 LOG.info( "UserDatabase initialized." ); 133 } catch( final NoSuchElementException | NoRequiredPropertyException e ) { 134 LOG.error( "You have not set the '{}'. You need to do this if you want to enable user management by JSPWiki.", PROP_DATABASE, e ); 135 } catch( final ReflectiveOperationException e ) { 136 LOG.error( "UserDatabase {} cannot be instantiated", dbClassName, e ); 137 } catch( final WikiSecurityException e ) { 138 LOG.error( "Exception initializing user database: {}", e.getMessage(), e ); 139 } finally { 140 if( m_database == null ) { 141 LOG.info( "I could not create a database object you specified (or didn't specify), so I am falling back to a default." ); 142 m_database = new DummyUserDatabase(); 143 } 144 } 145 146 return m_database; 147 } 148 149 /** {@inheritDoc} */ 150 @Override 151 public UserProfile getUserProfile( final Session session ) { 152 // Look up cached user profile 153 UserProfile profile = m_profiles.get( session ); 154 boolean newProfile = profile == null; 155 Principal user = null; 156 157 // If user is authenticated, figure out if this is an existing profile 158 if ( session.isAuthenticated() ) { 159 user = session.getUserPrincipal(); 160 try { 161 profile = getUserDatabase().findByWikiName( user.getName()); 162 newProfile = false; 163 } catch( final NoSuchPrincipalException e ) { 164 LOG.debug(e.getMessage(), e); 165 } 166 } 167 168 if ( newProfile ) { 169 profile = getUserDatabase().newProfile(); 170 if ( user != null ) { 171 profile.setLoginName( user.getName() ); 172 } else { 173 LOG.warn("new profile however the user principal is null. this shouldn't happen"); 174 } 175 if ( !profile.isNew() ) { 176 throw new IllegalStateException( "New profile should be marked 'new'. Check your UserProfile implementation." ); 177 } 178 } 179 180 // Stash the profile for next time 181 m_profiles.put( session, profile ); 182 return profile; 183 } 184 185 /** {@inheritDoc} */ 186 @Override 187 public void setUserProfile( final Context context, final UserProfile profile ) throws DuplicateUserException, WikiException { 188 final Session session = context.getWikiSession(); 189 // Verify user is allowed to save profile! 190 final Permission p = new WikiPermission( m_engine.getApplicationName(), WikiPermission.EDIT_PROFILE_ACTION ); 191 if ( !m_engine.getManager( AuthorizationManager.class ).checkPermission( session, p ) ) { 192 throw new WikiSecurityException( "You are not allowed to save wiki profiles." ); 193 } 194 195 // Check if profile is new, and see if container allows creation 196 final boolean newProfile = profile.isNew(); 197 198 // Check if another user profile already has the fullname or loginname 199 final UserProfile oldProfile = getUserProfile( session ); 200 final boolean nameChanged = ( oldProfile != null && oldProfile.getFullname() != null ) && 201 !( oldProfile.getFullname().equals( profile.getFullname() ) && 202 oldProfile.getLoginName().equals( profile.getLoginName() ) ); 203 UserProfile otherProfile; 204 try { 205 otherProfile = getUserDatabase().findByLoginName( profile.getLoginName() ); 206 if( otherProfile != null && !otherProfile.equals( oldProfile ) ) { 207 throw new DuplicateUserException( "security.error.login.taken", profile.getLoginName() ); 208 } 209 } catch (final NoSuchPrincipalException e) { 210 LOG.debug(e.getMessage(), e); 211 } 212 try { 213 otherProfile = getUserDatabase().findByFullName( profile.getFullname() ); 214 if( otherProfile != null && !otherProfile.equals( oldProfile ) ) { 215 throw new DuplicateUserException( "security.error.fullname.taken", profile.getFullname() ); 216 } 217 } catch( final NoSuchPrincipalException e ) { 218 LOG.debug(e.getMessage(), e); 219 } 220 221 // For new accounts, create approval workflow for user profile save. 222 if( newProfile && oldProfile != null && oldProfile.isNew() ) { 223 startUserProfileCreationWorkflow( context, profile ); 224 225 // If the profile doesn't need approval, then just log the user in 226 227 try { 228 final AuthenticationManager mgr = m_engine.getManager( AuthenticationManager.class ); 229 if( !mgr.isContainerAuthenticated() ) { 230 mgr.login( session, null, profile.getLoginName(), profile.getPassword() ); 231 } 232 } catch( final WikiException e ) { 233 throw new WikiSecurityException( e.getMessage(), e ); 234 } 235 236 // Alert all listeners that the profile changed... 237 // ...this will cause credentials to be reloaded in the wiki session 238 fireEvent( WikiSecurityEvent.PROFILE_SAVE, session, profile ); 239 } else { // For existing accounts, just save the profile 240 // If login name changed, rename it first 241 if( nameChanged && !oldProfile.getLoginName().equals( profile.getLoginName() ) ) { 242 getUserDatabase().rename( oldProfile.getLoginName(), profile.getLoginName() ); 243 } 244 245 // Now, save the profile (userdatabase will take care of timestamps for us) 246 getUserDatabase().save( profile ); 247 248 if( nameChanged ) { 249 // Fire an event if the login name or full name changed 250 final UserProfile[] profiles = new UserProfile[] { oldProfile, profile }; 251 fireEvent( WikiSecurityEvent.PROFILE_NAME_CHANGED, session, profiles ); 252 } else { 253 // Fire an event that says we have new a new profile (new principals) 254 fireEvent( WikiSecurityEvent.PROFILE_SAVE, session, profile ); 255 } 256 } 257 m_profiles.put( session, profile ); 258 } 259 260 /** {@inheritDoc} */ 261 @Override 262 public void startUserProfileCreationWorkflow( final Context context, final UserProfile profile ) throws WikiException { 263 final WorkflowBuilder builder = WorkflowBuilder.getBuilder( m_engine ); 264 final Principal submitter = context.getWikiSession().getUserPrincipal(); 265 final Step completionTask = m_engine.getManager( TasksManager.class ).buildSaveUserProfileTask( context.getWikiSession().getLocale() ); 266 267 // Add user profile attribute as Facts for the approver (if required) 268 final boolean hasEmail = profile.getEmail() != null; 269 final Fact[] facts = new Fact[ hasEmail ? 4 : 3 ]; 270 facts[ 0 ] = new Fact( WorkflowManager.WF_UP_CREATE_SAVE_FACT_PREFS_FULL_NAME, profile.getFullname() ); 271 facts[ 1 ] = new Fact( WorkflowManager.WF_UP_CREATE_SAVE_FACT_PREFS_LOGIN_NAME, profile.getLoginName() ); 272 facts[ 2 ] = new Fact( WorkflowManager.WF_UP_CREATE_SAVE_FACT_SUBMITTER, submitter.getName() ); 273 if ( hasEmail ) { 274 facts[ 3 ] = new Fact( WorkflowManager.WF_UP_CREATE_SAVE_FACT_PREFS_EMAIL, profile.getEmail() ); 275 } 276 final Workflow workflow = builder.buildApprovalWorkflow( submitter, 277 WorkflowManager.WF_UP_CREATE_SAVE_APPROVER, 278 null, 279 WorkflowManager.WF_UP_CREATE_SAVE_DECISION_MESSAGE_KEY, 280 facts, 281 completionTask, 282 null ); 283 284 workflow.setAttribute( WorkflowManager.WF_UP_CREATE_SAVE_ATTR_SAVED_PROFILE, profile ); 285 workflow.start( context ); 286 287 final boolean approvalRequired = workflow.getCurrentStep() instanceof Decision; 288 289 // If the profile requires approval, redirect user to message page 290 if ( approvalRequired ) { 291 throw new DecisionRequiredException( "This profile must be approved before it becomes active" ); 292 } 293 } 294 295 /** {@inheritDoc} */ 296 @Override 297 public UserProfile parseProfile( final Context context ) { 298 // Retrieve the user's profile (may have been previously cached) 299 final UserProfile profile = getUserProfile( context.getWikiSession() ); 300 final HttpServletRequest request = context.getHttpRequest(); 301 302 // Extract values from request stream (cleanse whitespace as needed) 303 String loginName = request.getParameter( PARAM_LOGINNAME ); 304 String password = request.getParameter( PARAM_PASSWORD ); 305 String fullname = request.getParameter( PARAM_FULLNAME ); 306 String email = request.getParameter( PARAM_EMAIL ); 307 loginName = StringUtils.trim( loginName ); 308 password = InputValidator.isBlank( password ) ? null : password; 309 fullname = StringUtils.trim( fullname ); 310 email = StringUtils.trim( email ); 311 312 // A special case if we have container authentication: if authenticated, login name is always taken from container 313 if ( m_engine.getManager( AuthenticationManager.class ).isContainerAuthenticated() && context.getWikiSession().isAuthenticated() ) { 314 loginName = context.getWikiSession().getLoginPrincipal().getName(); 315 } 316 317 // Set the profile fields! 318 profile.setLoginName( loginName ); 319 profile.setEmail( email ); 320 profile.setFullname( fullname ); 321 profile.setPassword( password ); 322 return profile; 323 } 324 325 /** {@inheritDoc} */ 326 @Override 327 public void validateProfile( final Context context, final UserProfile profile ) { 328 final Session session = context.getWikiSession(); 329 final InputValidator validator = new InputValidator( SESSION_MESSAGES, context ); 330 final ResourceBundle rb = Preferences.getBundle( context, InternationalizationManager.CORE_BUNDLE ); 331 332 // Query the SpamFilter first 333 final FilterManager fm = m_engine.getManager( FilterManager.class ); 334 final List< PageFilter > ls = fm.getFilterList(); 335 for( final PageFilter pf : ls ) { 336 if( pf instanceof SpamFilter ) { 337 if( !( ( SpamFilter )pf ).isValidUserProfile( context, profile ) ) { 338 session.addMessage( SESSION_MESSAGES, "Invalid userprofile" ); 339 return; 340 } 341 break; 342 } 343 } 344 345 // If container-managed auth and user not logged in, throw an error 346 if ( m_engine.getManager( AuthenticationManager.class ).isContainerAuthenticated() 347 && !context.getWikiSession().isAuthenticated() ) { 348 session.addMessage( SESSION_MESSAGES, rb.getString("security.error.createprofilebeforelogin") ); 349 } 350 351 validator.validateNotNull( profile.getLoginName(), rb.getString("security.user.loginname") ); 352 validator.validateNotNull( profile.getFullname(), rb.getString("security.user.fullname") ); 353 validator.validate( profile.getEmail(), rb.getString("security.user.email"), InputValidator.EMAIL ); 354 355 if( !m_engine.getManager( AuthenticationManager.class ).isContainerAuthenticated() ) { 356 // passwords must match and can't be null 357 358 //this is the new password 359 final String newpassword = profile.getPassword(); 360 if( newpassword == null ) { 361 session.addMessage( SESSION_MESSAGES, rb.getString( "security.error.blankpassword" ) ); 362 } else { 363 final HttpServletRequest request = context.getHttpRequest(); 364 //the existing password 365 final String existingPassword = ( request == null ) ? null : request.getParameter( "password0" ); 366 //the new password confirmation 367 final String passwordConfirmation = ( request == null ) ? null : request.getParameter( "password2" ); 368 if (!newpassword.equals(passwordConfirmation)) { 369 //password confirmation does not match 370 session.addMessage( SESSION_MESSAGES, rb.getString( "security.error.passwordnomatch" ) ); 371 } 372 if( !profile.isNew() && (existingPassword==null || existingPassword.equals( newpassword ) ) ) { 373 //existing account and the existing password matches the new password 374 session.addMessage( SESSION_MESSAGES, "existing password matches the proposed new one" ); 375 } 376 if( !profile.isNew() && !getUserDatabase().validatePassword( profile.getLoginName(), existingPassword ) ) { 377 //existing account and the provided password does not match what we currently have 378 session.addMessage( SESSION_MESSAGES, rb.getString( "security.error.passwordnomatch" ) ); 379 } 380 List<String> msg = PasswordComplexityVerifier.validate(passwordConfirmation, existingPassword, context); 381 for (String s : msg) { 382 session.addMessage( SESSION_MESSAGES, s ); 383 } 384 int reuseCount = Integer.parseInt(m_engine.getWikiProperties().getProperty("jspwiki.credentials.reuseCount", "-1")); 385 if (reuseCount > 0) { 386 //if it's set to 0 or less, we don't store it so we can skip this check 387 if (!m_database.validatePasswordReuse(profile.getLoginName(), passwordConfirmation)) { 388 //password reuse detected 389 session.addMessage(SESSION_MESSAGES, 390 MessageFormat.format(rb.getString("security.error.passwordReuseError"), reuseCount)); 391 } 392 } 393 } 394 } 395 396 UserProfile otherProfile; 397 final String fullName = profile.getFullname(); 398 final String loginName = profile.getLoginName(); 399 final String wikiName = profile.getWikiName(); 400 final String email = profile.getEmail(); 401 402 403 if ("true".equalsIgnoreCase(m_engine.getWikiProperties().getProperty(Engine.PROP_USE_2_X_ACL_LOGIC, "false"))) { 404 // It's illegal to use as a full name someone else's login name 405 try { 406 otherProfile = getUserDatabase().findByFullName(fullName ); 407 if( otherProfile != null && !profile.equals( otherProfile ) && !fullName.equals( otherProfile.getFullname() ) ) { 408 final Object[] args = { fullName }; 409 session.addMessage( SESSION_MESSAGES, MessageFormat.format( rb.getString( "security.error.illegalfullname" ), args ) ); 410 } 411 } catch( final NoSuchPrincipalException e ) { 412 LOG.debug(e.getMessage(), e); 413 /* It's clean */ 414 } 415 416 // It's illegal to use as a login name someone else's full name 417 try { 418 otherProfile = getUserDatabase().findByLoginName(loginName ); 419 if( otherProfile != null && !profile.equals( otherProfile ) && !loginName.equals( otherProfile.getLoginName() ) ) { 420 final Object[] args = { loginName }; 421 session.addMessage( SESSION_MESSAGES, MessageFormat.format( rb.getString( "security.error.illegalloginname" ), args ) ); 422 } 423 } catch( final NoSuchPrincipalException e ) { 424 LOG.debug(e.getMessage(), e); 425 /* It's clean */ 426 } 427 } else { 428 //JSPWIKI-130, v3+ behavior 429 // It is legal to use as a full name someone else's login name 430 431 // It's illegal to use as a login name someone else's full name 432 try { 433 otherProfile = getUserDatabase().findByLoginName(loginName ); 434 if( otherProfile != null && !profile.equals( otherProfile ) && !loginName.equals( otherProfile.getLoginName() ) ) { 435 final Object[] args = { loginName }; 436 session.addMessage( SESSION_MESSAGES, MessageFormat.format( 437 rb.getString( "security.error.illegalloginname" ), args ) ); 438 } 439 } catch( final NoSuchPrincipalException e ) { 440 LOG.debug(e.getMessage(), e); 441 /* It's clean */ 442 } 443 //it's illegal to use a username, email or wiki name as a group name 444 try { 445 Group[] groups = m_engine.getManager(GroupManager.class).getGroupDatabase().groups(); 446 for (Group grp : groups) { 447 if (grp.getName().equals(loginName)) { 448 final Object[] args = {loginName}; 449 session.addMessage(SESSION_MESSAGES, 450 MessageFormat.format(rb.getString("security.error.illegalloginname"), args)); 451 } 452 if (grp.getName().equals(wikiName)) { 453 final Object[] args = {wikiName}; 454 session.addMessage(SESSION_MESSAGES, 455 MessageFormat.format(rb.getString("security.error.illegalloginname"), args)); 456 } 457 if (grp.getName().equals(email)) { 458 final Object[] args = {email}; 459 session.addMessage(SESSION_MESSAGES, 460 MessageFormat.format(rb.getString("security.error.illegalloginname"), args)); 461 } 462 } 463 } catch (WikiSecurityException ex) { 464 session.addMessage(SESSION_MESSAGES, 465 "Processing failed. see log for details."); 466 LOG.error("failed to query for groups", ex); 467 } 468 //wiki names must be unique as well. 469 try { 470 otherProfile = getUserDatabase().findByWikiName(wikiName ); 471 if( otherProfile != null && !profile.equals( otherProfile ) && !loginName.equals( otherProfile.getLoginName() ) ) { 472 final Object[] args = { loginName }; 473 session.addMessage( SESSION_MESSAGES, MessageFormat.format( rb.getString( "security.error.illegalloginname" ), args ) ); 474 } 475 } catch( final NoSuchPrincipalException e ) { 476 LOG.debug(e.getMessage(), e); 477 /* It's clean */ 478 } 479 } 480 481 482 // It's illegal to use multiple accounts with the same email 483 if (email != null && email.trim().length() > 0) { 484 try { 485 otherProfile = getUserDatabase().findByEmail( email ); 486 if( otherProfile != null && !profile.getUid().equals( otherProfile.getUid() ) // Issue JSPWIKI-1042 487 && !profile.equals( otherProfile ) && StringUtils.lowerCase( email ) 488 .equals( StringUtils.lowerCase( otherProfile.getEmail() ) ) ) { 489 final Object[] args = { email }; 490 session.addMessage( SESSION_MESSAGES, MessageFormat.format( rb.getString( "security.error.email.taken" ), args ) ); 491 } 492 } catch( final NoSuchPrincipalException e ) { 493 LOG.debug(e.getMessage(), e); 494 /* It's clean */ 495 } 496 } 497 } 498 499 /** {@inheritDoc} */ 500 @Override 501 public Principal[] listWikiNames() throws WikiSecurityException { 502 return getUserDatabase().getWikiNames(); 503 } 504 505 // events processing ....................................................... 506 507 /** 508 * Registers a WikiEventListener with this instance. 509 * This is a convenience method. 510 * @param listener the event listener 511 */ 512 @Override public synchronized void addWikiEventListener( final WikiEventListener listener ) { 513 WikiEventManager.addWikiEventListener( this, listener ); 514 } 515 516 /** 517 * Un-registers a WikiEventListener with this instance. 518 * This is a convenience method. 519 * @param listener the event listener 520 */ 521 @Override public synchronized void removeWikiEventListener( final WikiEventListener listener ) { 522 WikiEventManager.removeWikiEventListener( this, listener ); 523 } 524 525 /** 526 * Implements the JSON API for usermanager. 527 * <p> 528 * Even though this gets serialized whenever container shuts down/restarts, this gets reinstalled to the session when JSPWiki starts. 529 * This means that it's not actually necessary to save anything. 530 */ 531 public static final class JSONUserModule implements WikiAjaxServlet { 532 533 private final DefaultUserManager m_manager; 534 535 /** 536 * Create a new JSONUserModule. 537 * @param mgr Manager 538 */ 539 public JSONUserModule( final DefaultUserManager mgr ) 540 { 541 m_manager = mgr; 542 } 543 544 @Override 545 public String getServletMapping() { 546 return JSON_USERS; 547 } 548 549 @Override 550 public void service( final HttpServletRequest req, final HttpServletResponse resp, final String actionName, final List<String> params) throws ServletException, IOException { 551 try { 552 if( params.isEmpty() ) { 553 return; 554 } 555 resp.setContentType("application/json"); 556 final String uid = params.get(0); 557 LOG.debug("uid="+uid); 558 if (StringUtils.isNotBlank(uid)) { 559 final UserProfile prof = getUserInfo(uid); 560 //clone the object 561 ObjectMapper om = new ObjectMapper(); 562 ObjectNode node = om.convertValue(prof, ObjectNode.class); 563 node.remove("password"); 564 node.remove("previousHashedCredentials"); 565 resp.getWriter().write(node.toString()); 566 } 567 } catch (final NoSuchPrincipalException e) { 568 throw new ServletException(e); 569 } 570 } 571 572 /** 573 * Directly returns the UserProfile object attached to an uid. 574 * 575 * @param uid The user id (e.g. WikiName) 576 * @return A UserProfile object 577 * @throws NoSuchPrincipalException If such a name does not exist. 578 */ 579 public UserProfile getUserInfo( final String uid ) throws NoSuchPrincipalException { 580 if( m_manager != null ) { 581 return m_manager.getUserDatabase().findByUid( uid ); 582 } 583 584 throw new IllegalStateException( "The manager is offline." ); 585 } 586 } 587 588}