001/* 002 * Copyright 2012 Anyware Services 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.ametys.web.usermanagement; 017 018import java.io.IOException; 019import java.sql.Connection; 020import java.sql.SQLException; 021import java.sql.Timestamp; 022import java.time.LocalDate; 023import java.time.ZoneId; 024import java.time.ZonedDateTime; 025import java.util.ArrayList; 026import java.util.Arrays; 027import java.util.Date; 028import java.util.HashMap; 029import java.util.List; 030import java.util.Map; 031import java.util.Map.Entry; 032import java.util.Optional; 033import java.util.Set; 034import java.util.UUID; 035import java.util.stream.Collectors; 036 037import org.apache.avalon.framework.configuration.Configuration; 038import org.apache.avalon.framework.configuration.ConfigurationException; 039import org.apache.avalon.framework.service.ServiceException; 040import org.apache.avalon.framework.service.ServiceManager; 041import org.apache.commons.lang.StringUtils; 042import org.apache.commons.lang3.RandomStringUtils; 043import org.apache.ibatis.session.RowBounds; 044import org.apache.ibatis.session.SqlSession; 045 046import org.ametys.cms.repository.Content; 047import org.ametys.cms.transformation.xslt.ResolveURIComponent; 048import org.ametys.core.datasource.AbstractMyBatisDAO; 049import org.ametys.core.datasource.ConnectionHelper; 050import org.ametys.core.right.RightManager; 051import org.ametys.core.right.RightManager.RightResult; 052import org.ametys.core.trace.ForensicLogger; 053import org.ametys.core.ui.Callable; 054import org.ametys.core.ui.mail.StandardMailBodyHelper; 055import org.ametys.core.user.CurrentUserProvider; 056import org.ametys.core.user.InvalidModificationException; 057import org.ametys.core.user.User; 058import org.ametys.core.user.User.UserCreationOrigin; 059import org.ametys.core.user.UserIdentity; 060import org.ametys.core.user.UserManager; 061import org.ametys.core.user.directory.ModifiableUserDirectory; 062import org.ametys.core.user.directory.NotUniqueUserException; 063import org.ametys.core.user.directory.UserDirectory; 064import org.ametys.core.user.directory.UserDirectoryFactory; 065import org.ametys.core.user.directory.UserDirectoryModel; 066import org.ametys.core.user.population.PopulationContextHelper; 067import org.ametys.core.user.population.UserPopulation; 068import org.ametys.core.user.population.UserPopulationDAO; 069import org.ametys.core.util.I18nUtils; 070import org.ametys.core.util.URIUtils; 071import org.ametys.core.util.mail.SendMailHelper; 072import org.ametys.plugins.repository.AmetysObjectIterable; 073import org.ametys.plugins.repository.AmetysObjectResolver; 074import org.ametys.plugins.repository.AmetysRepositoryException; 075import org.ametys.plugins.repository.query.expression.Expression; 076import org.ametys.plugins.repository.query.expression.Expression.Operator; 077import org.ametys.runtime.i18n.I18nizableText; 078import org.ametys.runtime.i18n.I18nizableTextParameter; 079import org.ametys.web.renderingcontext.RenderingContext; 080import org.ametys.web.renderingcontext.RenderingContextHandler; 081import org.ametys.web.repository.page.Page; 082import org.ametys.web.repository.page.PageQueryHelper; 083import org.ametys.web.repository.page.Zone; 084import org.ametys.web.repository.page.ZoneItem; 085import org.ametys.web.repository.page.ZoneItem.ZoneType; 086import org.ametys.web.repository.site.Site; 087import org.ametys.web.repository.site.SiteManager; 088import org.ametys.web.site.SiteConfigurationExtensionPoint; 089import org.ametys.web.tags.TagExpression; 090import org.ametys.web.usermanagement.UserManagementException.StatusError; 091import org.ametys.web.usermanagement.UserSignupManager.TempUser.TempUserOrigin; 092 093import com.google.common.collect.Multimap; 094 095import jakarta.mail.MessagingException; 096 097/** 098 * Manages registration and password recovery of users. 099 */ 100public class UserSignupManager extends AbstractMyBatisDAO 101{ 102 103 /** The component role. */ 104 public static final String ROLE = UserSignupManager.class.getName(); 105 106 /** The site parameter name for signup type */ 107 public static final String SITE_PARAM_SIGNUP_TYPE = "signup-type"; 108 109 /** The Signup Service parameter : userDirectory */ 110 public static final String SIGNUP_SERVICE_PARAMETER_USERDIRECTORY = "userdirectory"; 111 112 /** Tag for signup pages */ 113 public static final String SIGNUP_PAGE_TAG_NAME = "USER_SIGNUP"; 114 115 /** Id of service for signup pages */ 116 public static final String SIGNUP_PAGE_SERVICE_ID = "org.ametys.web.service.UserSignup"; 117 118 /** Tag for password pages */ 119 public static final String CHANGE_PASSWORD_PAGE_TAG_NAME = "USER_PASSWORD_CHANGE"; 120 121 /** Id of service for signup pages */ 122 public static final String CHANGE_PASSWORD_PAGE_SERVICE_ID = "org.ametys.web.service.UserPassword"; 123 124 private static final String __EVENT_ACCOUNT_TEMP_SIGNUP = "account.temporary.signup"; 125 private static final String __EVENT_ACCOUNT_CREATED = "account.created"; 126 private static final String __EVENT_ACCOUNT_PASSWORD_RESET = "account.password.reset"; 127 private static final String __EVENT_ACCOUNT_PASSWORD_CHANGE = "account.password.change"; 128 private static final String __EVENT_ACCOUNT_PASSWORD_CHANGE_FAILED = "account.password.change.failed"; 129 130 /** The user manager. */ 131 protected UserManager _userManager; 132 133 /** The DAO for user populations */ 134 protected UserPopulationDAO _userPopulationDAO; 135 136 /** The site manager. */ 137 protected SiteManager _siteManager; 138 139 /** The site configuration extension point. */ 140 protected SiteConfigurationExtensionPoint _siteConf; 141 142 /** The ametys object resolver. */ 143 protected AmetysObjectResolver _resolver; 144 145 /** The i18n utils. */ 146 protected I18nUtils _i18nUtils; 147 148 /** The user sign up configuration */ 149 protected UserSignUpConfiguration _userSignUpConfiguration; 150 151 /** The temporary users table. */ 152 protected String _tempUsersTable; 153 154 /** The password change table. */ 155 protected String _pwdChangeTable; 156 157 /** The population context helper. */ 158 protected PopulationContextHelper _populationContextHelper; 159 160 /** The rendering context handler */ 161 protected RenderingContextHandler _renderingContextHandler; 162 163 /** factory for user directory models */ 164 protected UserDirectoryFactory _userDirectoryFactory; 165 166 /** The current user provider */ 167 protected CurrentUserProvider _currentUserProvider; 168 169 /** The right manager */ 170 protected RightManager _rightManager; 171 172 /** 173 * Enumeration for the different type of signup 174 * 175 */ 176 public enum SignupType 177 { 178 /** Signup is not authorized */ 179 UNAUTHORIZED, 180 181 /** Signup allowed on invitation only */ 182 INVITATION_ONLY, 183 184 /** Public signup allowed */ 185 PUBLIC 186 } 187 188 @Override 189 public void service(ServiceManager serviceManager) throws ServiceException 190 { 191 super.service(serviceManager); 192 193 _userManager = (UserManager) serviceManager.lookup(UserManager.ROLE); 194 _userPopulationDAO = (UserPopulationDAO) serviceManager.lookup(UserPopulationDAO.ROLE); 195 _siteManager = (SiteManager) serviceManager.lookup(SiteManager.ROLE); 196 _siteConf = (SiteConfigurationExtensionPoint) serviceManager.lookup(SiteConfigurationExtensionPoint.ROLE); 197 _resolver = (AmetysObjectResolver) serviceManager.lookup(AmetysObjectResolver.ROLE); 198 _i18nUtils = (I18nUtils) serviceManager.lookup(I18nUtils.ROLE); 199 _userSignUpConfiguration = (UserSignUpConfiguration) serviceManager.lookup(UserSignUpConfiguration.ROLE); 200 _populationContextHelper = (PopulationContextHelper) serviceManager.lookup(PopulationContextHelper.ROLE); 201 _renderingContextHandler = (RenderingContextHandler) serviceManager.lookup(RenderingContextHandler.ROLE); 202 _userDirectoryFactory = (UserDirectoryFactory) serviceManager.lookup(UserDirectoryFactory.ROLE); 203 _currentUserProvider = (CurrentUserProvider) serviceManager.lookup(CurrentUserProvider.ROLE); 204 _rightManager = (RightManager) serviceManager.lookup(RightManager.ROLE); 205 } 206 207 208 @Override 209 public void configure(Configuration configuration) throws ConfigurationException 210 { 211 super.configure(configuration); 212 213 // Configure pool and tables. 214 _tempUsersTable = configuration.getChild("temp-users-table").getValue(); 215 _pwdChangeTable = configuration.getChild("pwd-change-table").getValue(); 216 } 217 218 /** 219 * Get the type of signup authorized for this site 220 * @param siteName the site name 221 * @return the of signup 222 */ 223 public SignupType getSignupType(String siteName) 224 { 225 Site site = _siteManager.getSite(siteName); 226 String value = site.getValue(SITE_PARAM_SIGNUP_TYPE, false, SignupType.UNAUTHORIZED.name()); 227 return SignupType.valueOf(value.toUpperCase()); 228 } 229 230 /** 231 * Test if public signup is allowed in a site. 232 * @param siteName the site to test. 233 * @return true if public signup is allowed, false otherwise. 234 */ 235 public boolean isPublicSignupAllowed(String siteName) 236 { 237 return getSignupType(siteName) == SignupType.PUBLIC; 238 } 239 240 /** 241 * Test if signup is allowed in a site, public or on invitation 242 * @param siteName the site to test. 243 * @return true if signup is allowed, false otherwise. 244 */ 245 public boolean isSignupAllowed(String siteName) 246 { 247 SignupType signupType = getSignupType(siteName); 248 return signupType == SignupType.PUBLIC || signupType == SignupType.INVITATION_ONLY; 249 } 250 251 /** 252 * Get the sign-up page in a given site and sitemap.<br> 253 * If more than one page are tagged "sign-up", return the first. 254 * @param siteName the site name. 255 * @param language the sitemap name. 256 * @return the sign-up page or null if not found. 257 */ 258 public Page getSignupPage(String siteName, String language) 259 { 260 return getSignupPage(siteName, language, null, null); 261 } 262 263 /** 264 * Get the sign-up page in a given site and sitemap, configured for given population and user directory.<br> 265 * If more than one page are tagged "sign-up", return the first. 266 * @param siteName the site name. 267 * @param language the sitemap name. 268 * @param populationId The population id. Can be null to not check population. 269 * @param userDirectoryId The user directory id. Can be null to not check user directory. 270 * @return the sign-up page or null if not found. 271 */ 272 public Page getSignupPage(String siteName, String language, String populationId, String userDirectoryId) 273 { 274 List<Page> signupPages = getSignupPages(siteName, language, populationId, userDirectoryId); 275 return signupPages.isEmpty() ? null : signupPages.get(0); 276 } 277 278 /** 279 * Check if page is a signup page 280 * @param page the page 281 * @return true if the page contains the signup service 282 */ 283 public boolean isSignupPage(Page page) 284 { 285 return _checkPageService(page, SIGNUP_PAGE_SERVICE_ID); 286 } 287 288 /** 289 * Get all the pages tagged "sign-up" with signup service 290 * @param siteName the site name. 291 * @param language the sitemap name. 292 * @return an iterable on all the pages tagged "sign-up". 293 */ 294 public List<Page> getSignupPages(String siteName, String language) 295 { 296 return getSignupPages(siteName, language, null, null); 297 } 298 299 /** 300 * Get all the pages tagged "sign-up" with signup service configured for given population and user directory 301 * @param siteName the site name. 302 * @param language the sitemap name. 303 * @param populationId The population id. Can be null to not check population. 304 * @param userDirectoryId The user directory id. Can be null to not check user directory. 305 * @return an iterable on all the pages tagged "sign-up". 306 */ 307 public List<Page> getSignupPages(String siteName, String language, String populationId, String userDirectoryId) 308 { 309 Expression expression = new TagExpression(Operator.EQ, SIGNUP_PAGE_TAG_NAME); 310 String query = PageQueryHelper.getPageXPathQuery(siteName, language, null, expression, null); 311 312 AmetysObjectIterable<Page> pages = _resolver.query(query); 313 314 return pages.stream() 315 .filter(p -> _checkSignupPage(p, populationId, userDirectoryId)) 316 .collect(Collectors.toList()); 317 } 318 319 private boolean _checkPageService(Page page, String serviceId) 320 { 321 for (Zone zone : page.getZones()) 322 { 323 try (AmetysObjectIterable<? extends ZoneItem> zoneItems = zone.getZoneItems()) 324 { 325 for (ZoneItem zoneItem : zoneItems) 326 { 327 if (zoneItem.getType() == ZoneType.SERVICE && serviceId.equals(zoneItem.getServiceId())) 328 { 329 return true; 330 } 331 } 332 } 333 } 334 335 return false; 336 } 337 338 private boolean _checkSignupPage(Page page, String populationId, String userDirectoryId) 339 { 340 for (Zone zone : page.getZones()) 341 { 342 try (AmetysObjectIterable<? extends ZoneItem> zoneItems = zone.getZoneItems()) 343 { 344 for (ZoneItem zoneItem : zoneItems) 345 { 346 if (zoneItem.getType() == ZoneType.SERVICE && SIGNUP_PAGE_SERVICE_ID.equals(zoneItem.getServiceId())) 347 { 348 if (StringUtils.isEmpty(populationId) && StringUtils.isEmpty(userDirectoryId)) 349 { 350 // No filter on user directory 351 return true; 352 } 353 else 354 { 355 UserDirectory userDirectory = getUserDirectory(zoneItem); 356 if (userDirectory != null && userDirectory.getId().equals(userDirectoryId) && userDirectory.getPopulationId().equals(populationId)) 357 { 358 return true; 359 } 360 } 361 } 362 } 363 } 364 } 365 366 return false; 367 } 368 369 /** 370 * Get the password change page in a given site and sitemap. 371 * If more than one page are tagged "password change", return the first. 372 * @param siteName the site name. 373 * @param language the sitemap name. 374 * @return the password change page or null if not found. 375 */ 376 public Page getPwdChangePage(String siteName, String language) 377 { 378 List<Page> pwdChangePages = getPwdChangePages(siteName, language); 379 return pwdChangePages.isEmpty() ? null : pwdChangePages.get(0); 380 } 381 382 /** 383 * Get the GTU page 384 * Returns null if not found or empty 385 * @param zoneItem the zone item 386 * @return the GTU page or null. 387 */ 388 public Page getGTUPage(ZoneItem zoneItem) 389 { 390 Page page = null; 391 392 try 393 { 394 String tosMode = zoneItem.getServiceParameters().getValue("terms-of-service-mode"); 395 if ("PAGE".equals(tosMode)) 396 { 397 String tosPageId = zoneItem.getServiceParameters().getValue("terms-of-service-page"); 398 if (StringUtils.isNotEmpty(tosPageId)) 399 { 400 page = _resolver.resolveById(tosPageId); 401 } 402 } 403 } 404 catch (AmetysRepositoryException e) 405 { 406 // Nothing 407 } 408 409 return page; 410 } 411 412 /** 413 * Get the user directory 414 * @param zoneItem the zone item with signup service 415 * @return The user directory or null if not found 416 */ 417 public UserDirectory getUserDirectory(ZoneItem zoneItem) 418 { 419 try 420 { 421 String userDirectoryParameterValue = zoneItem.getServiceParameters().getValue(SIGNUP_SERVICE_PARAMETER_USERDIRECTORY); 422 String[] populationAndDirectory = userDirectoryParameterValue.split("#", 2); 423 String populationId = populationAndDirectory[0]; 424 String userDirectoryId = populationAndDirectory[1]; 425 426 UserPopulation userPopulation = _userPopulationDAO.getUserPopulation(populationId); 427 if (userPopulation == null) 428 { 429 getLogger().error("The user population with id '{}' configured into service parameters of '{}' does not exist.", populationId, zoneItem.getId()); 430 return null; 431 } 432 return userPopulation.getUserDirectory(userDirectoryId); 433 } 434 catch (AmetysRepositoryException e) 435 { 436 getLogger().error("Failed to get user directory from service parameters of '{}'", zoneItem.getId(), e); 437 return null; 438 } 439 } 440 441 442 /** 443 * Get the GTU content 444 * Returns null if not found or empty 445 * @param zoneItem the zone item 446 * @return the GTU content or null. 447 */ 448 public Content getGTUContent(ZoneItem zoneItem) 449 { 450 Content content = null; 451 452 try 453 { 454 String tosMode = zoneItem.getServiceParameters().getValue("terms-of-service-mode"); 455 if ("CONTENT".equals(tosMode)) 456 { 457 String tosContentId = zoneItem.getServiceParameters().getValue("terms-of-service-content"); 458 if (StringUtils.isNotEmpty(tosContentId)) 459 { 460 content = _resolver.resolveById(tosContentId); 461 } 462 } 463 464 } 465 catch (AmetysRepositoryException e) 466 { 467 // Nothing 468 } 469 470 return content; 471 } 472 473 /** 474 * Get the GTU page 475 * Returns null if not found or empty 476 * @param zoneItem the zone item 477 * @return the success page or null. 478 */ 479 public Page getSuccessPage(ZoneItem zoneItem) 480 { 481 Page page = null; 482 483 try 484 { 485 String successMode = zoneItem.getServiceParameters().getValue("success-mode"); 486 if ("PAGE".equals(successMode)) 487 { 488 String successPageId = zoneItem.getServiceParameters().getValue("success-page"); 489 if (StringUtils.isNotEmpty(successPageId)) 490 { 491 page = _resolver.resolveById(successPageId); 492 } 493 } 494 } 495 catch (AmetysRepositoryException e) 496 { 497 // Nothing 498 } 499 500 return page; 501 } 502 503 /** 504 * Get the success content 505 * Returns null if not found or empty 506 * @param zoneItem the zone item 507 * @return the success content or null. 508 */ 509 public Content getSuccessContent(ZoneItem zoneItem) 510 { 511 Content content = null; 512 513 try 514 { 515 String successMode = zoneItem.getServiceParameters().getValue("success-mode"); 516 if ("CONTENT".equals(successMode)) 517 { 518 String successContentId = zoneItem.getServiceParameters().getValue("success-content"); 519 if (StringUtils.isNotEmpty(successContentId)) 520 { 521 content = _resolver.resolveById(successContentId); 522 } 523 } 524 } 525 catch (AmetysRepositoryException e) 526 { 527 // Nothing 528 } 529 530 return content; 531 } 532 533 /** 534 * Get all the pages tagged "password change". 535 * @param siteName the site name. 536 * @param language the sitemap name. 537 * @return an iterable on all the pages tagged "password change". 538 */ 539 public List<Page> getPwdChangePages(String siteName, String language) 540 { 541 Expression expression = new TagExpression(Operator.EQ, CHANGE_PASSWORD_PAGE_TAG_NAME); 542 String query = PageQueryHelper.getPageXPathQuery(siteName, language, null, expression, null); 543 544 AmetysObjectIterable<Page> pages = _resolver.query(query); 545 546 return pages.stream() 547 .filter(p -> _checkPageService(p, CHANGE_PASSWORD_PAGE_SERVICE_ID)) 548 .collect(Collectors.toList()); 549 } 550 551 /** 552 * Check if page is a change password page 553 * @param page the page 554 * @return true if the page contains the change password service 555 */ 556 public boolean isPwdChangePage(Page page) 557 { 558 return _checkPageService(page, CHANGE_PASSWORD_PAGE_SERVICE_ID); 559 } 560 561 /** 562 * Get the user from email if he/she exists in the populations 563 * @param email the e-mail to test. 564 * @param siteName The site name 565 * @return the user if the user exists, empty otherwise. 566 * @throws UserManagementException if an error occurs. 567 * @throws NotUniqueUserException if several user respond to the email 568 */ 569 public Optional<User> getUserIfHeExists(String email, String siteName) throws UserManagementException, NotUniqueUserException 570 { 571 Set<String> populationIds = _populationContextHelper.getUserPopulationsOnContexts(Arrays.asList("/sites/" + siteName, "/sites-fo/" + siteName), false, false); 572 for (String population : populationIds) 573 { 574 User user = _userManager.getUser(population, email); 575 if (user != null) 576 { 577 return Optional.of(user); 578 } 579 else 580 { 581 User userByEmail = _userManager.getUserByEmail(population, email); 582 if (userByEmail != null) 583 { 584 return Optional.of(userByEmail); 585 } 586 } 587 } 588 589 return Optional.empty(); 590 } 591 592 /** 593 * Tests if the user already exists in the populations 594 * @param email the e-mail to test. 595 * @param siteName The site name 596 * @return true if the user exists, false otherwise. 597 * @throws UserManagementException if an error occurs. 598 */ 599 public boolean userExists(String email, String siteName) throws UserManagementException 600 { 601 try 602 { 603 return getUserIfHeExists(email, siteName).isPresent(); 604 } 605 catch (NotUniqueUserException e) 606 { 607 return true; 608 } 609 } 610 611 /** 612 * Validate the user subscription data. 613 * @param siteName the site name. 614 * @param email the user e-mail. 615 * @param additionalValues the additional user values. 616 * @return a Map of the Errors by field. 617 * @throws UserManagementException if an error occurs. 618 */ 619 public Map<String, List<I18nizableText>> validate(String siteName, String email, Map<String, String> additionalValues) throws UserManagementException 620 { 621 Map<String, String> userInfos = new HashMap<>(); 622 623 userInfos.putAll(additionalValues); 624 625 // Standard info for user (provide a dummy password, as we do not know it yet). 626 userInfos.put("login", email); 627 userInfos.put("email", email); 628 userInfos.put("password", "password"); 629 630 Map<String, List<I18nizableText>> usersManagerErrors = new HashMap<>(); //foUsersManager.validate(userInfos); 631 Map<String, List<I18nizableText>> errors = new HashMap<>(usersManagerErrors); 632 633 // If there are errors on the login, do not return it except if 634 if (errors.containsKey("login")) 635 { 636 if (!errors.containsKey("email")) 637 { 638 errors.put("email", errors.get("login")); 639 } 640 errors.remove("login"); 641 } 642 643 return errors; 644 } 645 646 /** 647 * Validate the user password. 648 * @param siteName the site name. 649 * @param password the password to validate. 650 * @param login the login of the user 651 * @param population The id of the population 652 * @return a Map of the Errors by field. 653 * @throws UserManagementException if an error occurs. 654 */ 655 public List<I18nizableText> validatePassword(String siteName, String password, String login, String population) throws UserManagementException 656 { 657 Map<String, String> userInfos = new HashMap<>(); 658 659 userInfos.put("password", password); 660 661 UserDirectory userDirectory = _userManager.getUserDirectory(population, login); 662 if (!(userDirectory instanceof ModifiableUserDirectory modifiableUserDirectory)) 663 { 664 throw new UserManagementException("The user subscription feature can't be used, as the UserDirectory is not modifiable.", StatusError.UNMODIFIABLE_USER_DIRECTORY); 665 } 666 Map<String, List<I18nizableText>> usersManagerErrors = modifiableUserDirectory.validate(userInfos); 667 List<I18nizableText> errors = new ArrayList<>(); 668 669 // Keep only errors related to the password field. 670 if (usersManagerErrors.containsKey("password")) 671 { 672 errors.addAll(usersManagerErrors.get("password")); 673 } 674 675 return errors; 676 } 677 678 /** 679 * Temporary sign the user in and returns the token 680 * @param siteName The site's name 681 * @param language The page's language 682 * @param email The email of the user 683 * @param population The population ID 684 * @param userDirectoryId The userDirectory ID 685 * @return The sign-up token 686 * @throws UserManagementException If an error occurs 687 */ 688 public String getOrCreateToken(String siteName, String language, String email, String population, String userDirectoryId) throws UserManagementException 689 { 690 String token = getToken(siteName, email, population, userDirectoryId); 691 692 if (token == null) 693 { 694 temporarySignup(siteName, language, email, population, userDirectoryId, false); 695 token = getToken(siteName, email, population, userDirectoryId); 696 } 697 698 return token; 699 } 700 701 /** 702 * Validate and store a sign-up request and send a confirmation e-mail. 703 * @param siteName the site name. 704 * @param language the sitemap name. 705 * @param email the user e-mail address. 706 * @param population the population 707 * @param userDirectoryId the id of the user directory of the population 708 * @throws UserManagementException if an error occurs. 709 */ 710 public void temporarySignup(String siteName, String language, String email, String population, String userDirectoryId) throws UserManagementException 711 { 712 temporarySignup(siteName, language, email, population, userDirectoryId, true); 713 } 714 715 /** 716 * Validate and store a sign-up request and send a confirmation e-mail. 717 * @param siteName the site name. 718 * @param language the sitemap name. 719 * @param email the user e-mail address. 720 * @param population the population 721 * @param userDirectoryId the id of the user directory of the population 722 * @param sendMail Set to false to not send mail at end of process 723 * @throws UserManagementException if an error occurs. 724 */ 725 public void temporarySignup(String siteName, String language, String email, String population, String userDirectoryId, boolean sendMail) throws UserManagementException 726 { 727 // Verify that public sign-up is allowed and throw an exception otherwise. 728 checkPublicSignup(siteName); 729 730 if (getLogger().isDebugEnabled()) 731 { 732 getLogger().debug("A user is requesting a sign-up: " + email); 733 } 734 735 _temporarySignup(siteName, language, email, population, userDirectoryId, null, null, TempUserOrigin.USER_REQUEST, sendMail, false); 736 } 737 738 /** 739 * Validate and store a invitation to sign-up request and send a confirmation e-mail. 740 * @param siteName the site name. 741 * @param language the sitemap name. 742 * @param email the user e-mail address. 743 * @param population the population 744 * @param userDirectoryId the id of the user directory of the population 745 * @param lastname The guest last name. Can be null. 746 * @param firstname The guest first name. Can be null. 747 * @throws UserManagementException if an error occurs. 748 */ 749 public void inviteToSignup(String siteName, String language, String email, String population, String userDirectoryId, String lastname, String firstname) throws UserManagementException 750 { 751 inviteToSignup(siteName, language, email, population, userDirectoryId, lastname, firstname, true, true, true); 752 } 753 754 /** 755 * Validate and store a invitation to sign-up request and send a confirmation e-mail. 756 * @param siteName the site name. 757 * @param language the sitemap name. 758 * @param email the user e-mail address. 759 * @param population the population 760 * @param userDirectoryId the id of the user directory of the population 761 * @param lastname The guest last name. Can be null. 762 * @param firstname The guest first name. Can be null. 763 * @param sendMail Set to false to not send mail at end of process 764 * @param resendInvitationIfExists Set to false to not resend invitation if the email already exists as a user waiting signup 765 * @param checkRights set to false to ignore rights 766 * @throws UserManagementException if an error occurs. 767 */ 768 public void inviteToSignup(String siteName, String language, String email, String population, String userDirectoryId, String lastname, String firstname, boolean sendMail, boolean resendInvitationIfExists, boolean checkRights) throws UserManagementException 769 { 770 // Verify that sign-up is allowed (public or on invitation only) and throw an exception otherwise. 771 checkSignupAllowed(siteName); 772 773 if (checkRights) 774 { 775 // Verify that user is allowed to send invitation for this population and throw an exception otherwise. 776 checkUserAllowedForInvitation(siteName, population); 777 } 778 779 if (getLogger().isDebugEnabled()) 780 { 781 getLogger().debug("A user is invited to sign-up: " + email); 782 } 783 784 _temporarySignup(siteName, language, email, population, userDirectoryId, lastname, firstname, TempUserOrigin.INVITATION, sendMail, resendInvitationIfExists); 785 } 786 787 private void _temporarySignup(String siteName, String language, String email, String population, String userDirectoryId, String lastname, String firstname, TempUserOrigin origin, boolean sendMail, boolean resendInvitationIfExists) throws UserManagementException 788 { 789 if (userExists(email, siteName)) 790 { 791 throw new UserManagementException("User with email " + email + " already exists", StatusError.USER_ALREADY_EXISTS); 792 } 793 794 // Generate unique token. 795 String token = UUID.randomUUID().toString().replace("-", ""); 796 797 try 798 { 799 addTemporaryUser(siteName, email, token, population, userDirectoryId, lastname, firstname, origin); 800 801 // Send validation e-mail with token. 802 if (sendMail) 803 { 804 TempUser tempUser = getTempUser(siteName, email, population, userDirectoryId); 805 sendSignupConfirmMail(tempUser, language); 806 } 807 808 ForensicLogger.info(__EVENT_ACCOUNT_TEMP_SIGNUP, Map.of("site", siteName, "email", email, "population", population, "userDirectory", userDirectoryId), null); 809 } 810 catch (UserManagementException e) 811 { 812 if (e.getStatusError() == StatusError.TEMP_USER_ALREADY_EXISTS && resendInvitationIfExists) 813 { 814 // Ignore error and resend the invitation if the temporary user was already registered 815 resendInvitation(siteName, email, population, userDirectoryId); 816 return; 817 } 818 else 819 { 820 // Remove the temporary user that was potentially created (as the temporary signup could not complete successfully) 821 removeTempUser(siteName, email, token, population, userDirectoryId); 822 throw e; 823 } 824 } 825 826 } 827 828 /** 829 * Get a token for temp user 830 * @param siteName the site name. 831 * @param email the user e-mail address. 832 * @param population The id of the population 833 * @param userDirectoryId The id of the user directory of the population 834 * @return the user token or null if not found 835 * @throws UserManagementException if an error occurs. 836 */ 837 public String getToken(String siteName, String email, String population, String userDirectoryId) throws UserManagementException 838 { 839 TempUser tempUser = getTempUser(siteName, email, population, userDirectoryId); 840 if (tempUser != null) 841 { 842 return tempUser.getToken(); 843 } 844 845 return null; 846 } 847 848 /** 849 * Update susbcription date and resend an invitation 850 * @param siteName the site name 851 * @param email the email 852 * @param population the population id 853 * @param userDirectoryId the user directory id 854 * @throws UserManagementException if an error occurred 855 */ 856 public void resendInvitation(String siteName, String email, String population, String userDirectoryId) throws UserManagementException 857 { 858 // Verify that sign-up is allowed and throw an exception otherwise. 859 checkSignupAllowed(siteName); 860 861 // Verify that user is allowed and throw an exception otherwise. 862 checkUserAllowedForInvitation(siteName, population); 863 864 TempUser tempUser = getTempUser(siteName, email, population, userDirectoryId); 865 if (tempUser.getOrigin() != TempUserOrigin.INVITATION) 866 { 867 throw new UserManagementException("Can not resend invitation. The subscription request is a user request", StatusError.UNMODIFIABLE_SIGNUP_REQUEST); 868 } 869 870 // Reset signup without generating new token 871 resetTempSignup(siteName, null, email, null, population, userDirectoryId); 872 } 873 874 /** 875 * Reset a sign-up request: generate a new token and re-send the confirmation e-mail. 876 * @param siteName the site name. 877 * @param language the sitemap name. 878 * @param email the user e-mail address. 879 * @param population The id of the population 880 * @param userDirectoryId The id of the user directory of the population 881 * @throws UserManagementException if an error occurs. 882 */ 883 public void resetTempSignup(String siteName, String language, String email, String population, String userDirectoryId) throws UserManagementException 884 { 885 // Verify that public sign-up is allowed and throw an exception otherwise. 886 checkPublicSignup(siteName); 887 888 // Generate a new token. 889 String newToken = UUID.randomUUID().toString().replace("-", ""); 890 891 if (getLogger().isDebugEnabled()) 892 { 893 getLogger().debug("Resetting temporary signup for email: " + email + " in site " + siteName); 894 } 895 896 resetTempSignup(siteName, language, email, newToken, population, userDirectoryId); 897 } 898 899 /** 900 * Reset a sign-up request: generate a new token and re-send the confirmation e-mail. 901 * @param siteName the site name. 902 * @param language the sitemap name. 903 * @param email the user e-mail address. 904 * @param token the token. Can be null to not re-generate token. 905 * @param population The id of the population 906 * @param userDirectoryId The id of the user directory of the population 907 * @throws UserManagementException if an error occurs. 908 */ 909 protected void resetTempSignup(String siteName, String language, String email, String token, String population, String userDirectoryId) throws UserManagementException 910 { 911 // Test if the subscription request really exists. 912 TempUser tempUser = getTempUser(siteName, email, population, userDirectoryId); 913 914 if (tempUser == null) 915 { 916 throw new UserManagementException("Found no subscription request with email '" + email + "' for site '" + siteName + "'", StatusError.UNKNOWN_EMAIL); 917 } 918 919 String newToken = token; 920 if (newToken == null) 921 { 922 // No generate new token 923 newToken = tempUser.getToken(); 924 } 925 926 updateTempToken(siteName, email, newToken, population, userDirectoryId); 927 928 tempUser = getTempUser(siteName, email, population, userDirectoryId); 929 930 sendSignupConfirmMail(tempUser, language); 931 } 932 933 934 /** 935 * Create the user in the FO UsersManager from his temporary request. 936 * @param siteName the site name. 937 * @param language the current language 938 * @param firstname the user firstname 939 * @param lastname the user lastname 940 * @param email the user e-mail address. 941 * @param token the request token. 942 * @param password the user password. 943 * @param population The id of the population 944 * @param userDirectoryId The id of the user directory of the population 945 * @param errors the errors 946 * @throws UserManagementException if an error occurs. 947 */ 948 public void signup(String siteName, String language, String firstname, String lastname, String email, String token, String password, String population, String userDirectoryId, Multimap<String, I18nizableText> errors) throws UserManagementException 949 { 950 // Verify that public sign-up is allowed and throw an exception otherwise. 951 checkSignupAllowed(siteName); 952 953 Map<String, String> userInfos = new HashMap<>(); 954 955 TempUser tempUser = getTempUser(siteName, email, token, population, userDirectoryId); 956 957 if (tempUser == null) 958 { 959 throw new UserManagementException("Provided token is unknown", StatusError.TOKEN_UNKNOWN); 960 } 961 962 // Generate login 963 String login = RandomStringUtils.randomNumeric(10); 964 965 // Standard info for user. 966 userInfos.put("login", login); 967 userInfos.put("email", email); 968 userInfos.put("firstname", firstname); 969 userInfos.put("lastname", lastname); 970 userInfos.put("password", password); 971 972 try 973 { 974 validationBeforeSignup(errors); 975 976 if (errors.isEmpty()) 977 { 978 // Add the user. 979 ModifiableUserDirectory userDirectory = (ModifiableUserDirectory) _userPopulationDAO.getUserPopulation(population).getUserDirectory(userDirectoryId); 980 userDirectory.add(userInfos, UserCreationOrigin.USER_SIGNUP); 981 982 // Remove the temporary user. 983 removeTempUser(siteName, email, token, population, userDirectoryId); 984 985 User createdUser = _userManager.getUserByDirectory(population, userDirectoryId, login); 986 987 // Do additional operations after signup 988 additionalSignupOperations(createdUser, errors); 989 990 if (errors.isEmpty()) 991 { 992 sendSignupValidatedMail(siteName, language, createdUser); 993 994 ForensicLogger.info(__EVENT_ACCOUNT_CREATED, Map.of("site", siteName, "user", createdUser, "population", population, "userDirectory", userDirectoryId), createdUser.getIdentity()); 995 } 996 } 997 else 998 { 999 throw new UserManagementException("Unable to signup user"); 1000 } 1001 1002 } 1003 catch (InvalidModificationException e) 1004 { 1005 throw new UserManagementException("An error occurred signing up the user.", StatusError.INVALID_MODIFICATION, e); 1006 } 1007 } 1008 1009 /** 1010 * Do some validation before signup 1011 * @param errors the map of errors to fill in cause of errors during validation 1012 */ 1013 public void validationBeforeSignup(Multimap<String, I18nizableText> errors) 1014 { 1015 // Nothing 1016 } 1017 1018 /** 1019 * Process additional operations after creation of user 1020 * @param createdUser the created user 1021 * @param errors the map of errors to fill in case of errors during additional operations 1022 * @throws UserManagementException if an error occurs. 1023 */ 1024 public void additionalSignupOperations(User createdUser, Multimap<String, I18nizableText> errors) throws UserManagementException 1025 { 1026 // Nothing 1027 } 1028 1029 1030 /** 1031 * Create a reset password request and send a confirmation e-mail. 1032 * @param siteName the site name. 1033 * @param language the sitemap name. 1034 * @param loginOrEmail the user login or email. 1035 * @param populationId the population 1036 * @throws UserManagementException if an error occurs. 1037 */ 1038 public void resetPassword(String siteName, String language, String loginOrEmail, String populationId) throws UserManagementException 1039 { 1040 // Check if the population exists 1041 UserPopulation population = _userPopulationDAO.getUserPopulation(populationId); 1042 if (population == null) 1043 { 1044 throw new UserManagementException("Unknown population with id '" + populationId + "'", StatusError.POPULATION_UNKNOWN); 1045 } 1046 1047 // Check if the user exists and get it 1048 User user = _userManager.getUser(populationId, loginOrEmail); 1049 if (user == null) 1050 { 1051 try 1052 { 1053 user = _userManager.getUserByEmail(populationId, loginOrEmail); 1054 if (user == null) 1055 { 1056 // No user with this e-mail or login. 1057 throw new UserManagementException("Unknown user with login or email '" + loginOrEmail + "' for population '" + populationId + "'", StatusError.USER_UNKNOWN); 1058 } 1059 } 1060 catch (NotUniqueUserException e) 1061 { 1062 throw new UserManagementException("Many users match for email '" + loginOrEmail + "' and population '" + populationId + "'", StatusError.NOT_UNIQUE_USER); 1063 } 1064 } 1065 1066 // Check if the user directory is modifiable 1067 if (!(user.getUserDirectory() instanceof ModifiableUserDirectory)) 1068 { 1069 throw new UserManagementException("User directory is not modifiable", StatusError.UNMODIFIABLE_USER_DIRECTORY); 1070 } 1071 1072 if (StringUtils.isEmpty(user.getEmail())) 1073 { 1074 throw new UserManagementException("User has no email", StatusError.EMPTY_EMAIL); 1075 } 1076 1077 // Generate a new token. 1078 String token = UUID.randomUUID().toString().replace("-", ""); 1079 1080 // Insert the token in the database. 1081 addPasswordToken(siteName, user.getIdentity().getLogin(), token, populationId); 1082 1083 // Send the e-mail. 1084 sendResetPasswordMail(siteName, language, user, token); 1085 1086 // Trace event 1087 ForensicLogger.info(__EVENT_ACCOUNT_PASSWORD_RESET, Map.of("site", siteName, "user", user), user.getIdentity()); 1088 } 1089 1090 /** 1091 * Change the user password. 1092 * @param siteName the site name. 1093 * @param login the user login. 1094 * @param token the password change request token. 1095 * @param newPassword the new password. 1096 * @param population the population 1097 * @throws UserManagementException if an error occurs. 1098 */ 1099 public void changeUserPassword(String siteName, String login, String token, String newPassword, String population) throws UserManagementException 1100 { 1101 checkPasswordToken(siteName, login, token, population); 1102 1103 Map<String, String> userInfos = new HashMap<>(); 1104 1105 userInfos.put("login", login); 1106 userInfos.put("password", newPassword); 1107 1108 try 1109 { 1110 UserDirectory userDirectory = _userManager.getUserDirectory(population, login); 1111 if (!(userDirectory instanceof ModifiableUserDirectory)) 1112 { 1113 _logPasswordChangeFailed(siteName, login, population, "unmodifiable-modification"); 1114 throw new UserManagementException("The user's password can't be changed, as the UserDirectory is not modifiable.", StatusError.UNMODIFIABLE_USER_DIRECTORY); 1115 } 1116 ((ModifiableUserDirectory) userDirectory).update(userInfos); 1117 1118 removePasswordToken(siteName, login, token, population); 1119 1120 ForensicLogger.info(__EVENT_ACCOUNT_PASSWORD_CHANGE, Map.of("site", siteName, "login", login, "population", population), new UserIdentity(login, population)); 1121 } 1122 catch (InvalidModificationException e) 1123 { 1124 _logPasswordChangeFailed(siteName, login, population, "invalid-modification"); 1125 throw new UserManagementException("Invalid user inputs to change password", StatusError.INVALID_MODIFICATION, e); 1126 } 1127 } 1128 1129 private void _logPasswordChangeFailed(String siteName, String login, String population, String errorCause) 1130 { 1131 Map<String, Object> eventArgs = Map.of("site", siteName, "login", login, "population", population, "error", errorCause); 1132 ForensicLogger.warn(__EVENT_ACCOUNT_PASSWORD_CHANGE_FAILED, eventArgs, new UserIdentity(login, population)); 1133 } 1134 1135 /** 1136 * Check the sign-up request token. 1137 * @param siteName the site name. 1138 * @param email the user e-mail. 1139 * @param token the sign-up request token. 1140 * @param population The id of the population 1141 * @param userDirectoryId The id of the user directory of the population 1142 * @throws UserManagementException if an error occurs. 1143 */ 1144 public void checkToken(String siteName, String email, String token, String population, String userDirectoryId) throws UserManagementException 1145 { 1146 removeExpiredTokens(); 1147 1148 try (SqlSession sqlSession = getSession()) 1149 { 1150 String stmtId = "UserSignupManager.getSubscriptionDate"; 1151 1152 Map<String, Object> params = new HashMap<>(); 1153 params.put("tempUsersTable", _tempUsersTable); 1154 1155 params.put("site", siteName); 1156 params.put("email", email); 1157 params.put("token", token); 1158 params.put("population", population); 1159 params.put("userDirectory", userDirectoryId); 1160 1161 Date rawSubscriptionDate = sqlSession.selectOne(stmtId, params); 1162 1163 // Date verification. 1164 if (rawSubscriptionDate == null) 1165 { 1166 throw new UserManagementException("Provided token is unknown", StatusError.TOKEN_UNKNOWN); 1167 } 1168 1169 // The validity limit 1170 ZonedDateTime limit = LocalDate.now().atStartOfDay(ZoneId.systemDefault()).minusDays(_userSignUpConfiguration.getTokenValidity()); 1171 ZonedDateTime subscriptionDate = rawSubscriptionDate.toInstant().atZone(ZoneId.systemDefault()); 1172 1173 if (subscriptionDate.isBefore(limit)) 1174 { 1175 throw new UserManagementException("Provided token is expired", StatusError.TOKEN_EXPIRED); 1176 } 1177 } 1178 catch (Exception e) 1179 { 1180 if (e instanceof UserManagementException) 1181 { 1182 throw e; 1183 } 1184 else 1185 { 1186 throw new UserManagementException("Database error while testing the token for user [" + email + "]", StatusError.DATABASE_ERROR, e); 1187 } 1188 } 1189 } 1190 1191 /** 1192 * Check the password change request token. 1193 * @param siteName the site name. 1194 * @param login the user login. 1195 * @param token the password change request token. 1196 * @param population the population 1197 * @throws UserManagementException if an error occurs. 1198 */ 1199 public void checkPasswordToken(String siteName, String login, String token, String population) throws UserManagementException 1200 { 1201 removeExpiredPasswordTokens(); 1202 1203 try (SqlSession sqlSession = getSession()) 1204 { 1205 String stmtId = "UserSignupManager.getRequestDate"; 1206 1207 Map<String, Object> params = new HashMap<>(); 1208 params.put("pwdChangeTable", _pwdChangeTable); 1209 1210 params.put("site", siteName); 1211 params.put("login", login); 1212 params.put("token", token); 1213 params.put("population", population); 1214 1215 Date rawRequestDate = sqlSession.selectOne(stmtId, params); 1216 1217 // Date verification. 1218 if (rawRequestDate == null) 1219 { 1220 throw new UserManagementException("Provided token for login '" + login + "' and site '" + siteName + "' is unknown", StatusError.TOKEN_UNKNOWN); 1221 } 1222 1223 // Check the validity. 1224 ZonedDateTime limit = LocalDate.now().atStartOfDay(ZoneId.systemDefault()).minusDays(_userSignUpConfiguration.getTokenValidity()); 1225 ZonedDateTime requestDate = rawRequestDate.toInstant().atZone(ZoneId.systemDefault()); 1226 1227 if (requestDate.isBefore(limit)) 1228 { 1229 throw new UserManagementException("Provided token for login '" + login + "' and site '" + siteName + "' is expired", StatusError.TOKEN_EXPIRED); 1230 } 1231 } 1232 catch (Exception e) 1233 { 1234 if (e instanceof UserManagementException) 1235 { 1236 throw e; 1237 } 1238 else 1239 { 1240 throw new UserManagementException("Database error while testing the password token for user " + login, StatusError.DATABASE_ERROR, e); 1241 } 1242 } 1243 } 1244 1245 /** 1246 * Remove the expired sign-up request tokens (user request only) 1247 * @throws UserManagementException if an error occurs. 1248 */ 1249 public void removeExpiredTokens() throws UserManagementException 1250 { 1251 removeExpiredTokens(null); 1252 } 1253 1254 /** 1255 * Remove the expired sign-up request tokens (user request only) 1256 * @param siteName the site name. Can be null. 1257 * @return the number of deleted tokens 1258 * @throws UserManagementException if an error occurs. 1259 */ 1260 public int removeExpiredTokens(String siteName) throws UserManagementException 1261 { 1262 try (SqlSession sqlSession = getSession()) 1263 { 1264 String stmtId = "UserSignupManager.deleteExpiredTokens"; 1265 1266 Map<String, Object> params = new HashMap<>(); 1267 params.put("tempUsersTable", _tempUsersTable); 1268 1269 ZonedDateTime limit = LocalDate.now().atStartOfDay(ZoneId.systemDefault()).minusDays(_userSignUpConfiguration.getTokenValidity()); 1270 Timestamp limitTimestamp = Timestamp.from(limit.toInstant()); 1271 params.put("threshold", limitTimestamp); 1272 if (StringUtils.isNotBlank(siteName)) 1273 { 1274 params.put("site", siteName); 1275 } 1276 1277 int deletedTokens = sqlSession.delete(stmtId, params); 1278 sqlSession.commit(); 1279 return deletedTokens; 1280 } 1281 catch (Exception e) 1282 { 1283 throw new UserManagementException("Database error while removing the expired tokens.", StatusError.DATABASE_ERROR, e); 1284 } 1285 } 1286 1287 /** 1288 * Remove the expired change password request tokens. 1289 * @throws UserManagementException if an error occurs. 1290 */ 1291 public void removeExpiredPasswordTokens() throws UserManagementException 1292 { 1293 try (SqlSession sqlSession = getSession()) 1294 { 1295 String stmtId = "UserSignupManager.deleteExpiredPasswordTokens"; 1296 1297 Map<String, Object> params = new HashMap<>(); 1298 params.put("pwdChangeTable", _pwdChangeTable); 1299 1300 ZonedDateTime limit = LocalDate.now().atStartOfDay(ZoneId.systemDefault()).minusDays(_userSignUpConfiguration.getTokenValidity()); 1301 Timestamp limitTimestamp = Timestamp.from(limit.toInstant()); 1302 params.put("threshold", limitTimestamp); 1303 1304 sqlSession.delete(stmtId, params); 1305 sqlSession.commit(); 1306 } 1307 catch (Exception e) 1308 { 1309 throw new UserManagementException("Database error while removing the expired tokens.", StatusError.DATABASE_ERROR, e); 1310 } 1311 } 1312 1313 /** 1314 * Verify that public sign-up is allowed. If not, throw an exception. 1315 * @param siteName the site name. 1316 * @throws UserManagementException if public sign-up is not enabled. 1317 */ 1318 protected void checkPublicSignup(String siteName) throws UserManagementException 1319 { 1320 if (!isPublicSignupAllowed(siteName)) 1321 { 1322 throw new UserManagementException("Public signup is disabled for this site.", StatusError.PUBLIC_SIGNUP_NOT_ALLOWED); 1323 } 1324 } 1325 1326 /** 1327 * Verify that public sign-up is allowed. If not, throw an exception. 1328 * @param siteName the site name. 1329 * @throws UserManagementException if public sign-up is not enabled. 1330 */ 1331 protected void checkSignupAllowed(String siteName) throws UserManagementException 1332 { 1333 if (!isSignupAllowed(siteName)) 1334 { 1335 throw new UserManagementException("Signup is disabled for this site.", StatusError.SIGNUP_NOT_ALLOWED); 1336 } 1337 } 1338 1339 /** 1340 * Create a user sign-up request ("temporary" user) in the database. 1341 * @param siteName the site name. 1342 * @param email the user e-mail. 1343 * @param token the generated token. 1344 * @param population the population 1345 * @param userDirectoryId the id of the user directory of the population 1346 * @param lastname The guest last name. Can be null. 1347 * @param firstname The guest first name. Can be null. 1348 * @param origin true if this signup request origin 1349 * @throws UserManagementException if an error occurs. 1350 */ 1351 protected void addTemporaryUser(String siteName, String email, String token, String population, String userDirectoryId, String lastname, String firstname, TempUserOrigin origin) throws UserManagementException 1352 { 1353 try (SqlSession sqlSession = getSession()) 1354 { 1355 // Does this email already exists 1356 String stmtId = "UserSignupManager.tempEmailExists"; 1357 1358 Map<String, Object> params = new HashMap<>(); 1359 params.put("tempUsersTable", _tempUsersTable); 1360 1361 params.put("site", siteName); 1362 params.put("email", email); 1363 1364 List<String> emails = sqlSession.selectList(stmtId, params); 1365 1366 if (!emails.isEmpty()) 1367 { 1368 throw new UserManagementException("Temporary user with email '" + email + "' and site name '" + siteName + "' already exists", StatusError.TEMP_USER_ALREADY_EXISTS); 1369 } 1370 1371 // Add temporary user 1372 stmtId = "UserSignupManager.addTempUser"; 1373 params = new HashMap<>(); 1374 params.put("tempUsersTable", _tempUsersTable); 1375 1376 Timestamp now = new Timestamp(System.currentTimeMillis()); 1377 1378 params.put("site", siteName); 1379 params.put("email", email); 1380 params.put("population", population); 1381 params.put("userDirectory", userDirectoryId); 1382 params.put("subscription_date", now); 1383 params.put("token", token); 1384 params.put("lastname", lastname); 1385 params.put("firstname", firstname); 1386 params.put("origin", origin.name()); 1387 1388 sqlSession.insert(stmtId, params); 1389 sqlSession.commit(); 1390 } 1391 catch (Exception e) 1392 { 1393 if (e instanceof UserManagementException) 1394 { 1395 throw e; 1396 } 1397 else 1398 { 1399 throw new UserManagementException("Database error while signing up a new user [" + email + "]", StatusError.DATABASE_ERROR, e); 1400 } 1401 } 1402 } 1403 1404 /** 1405 * Send a sign-up confirmation link by e-mail. 1406 * @param tempUser the temp user 1407 * @param language the e-mail language. 1408 * @throws UserManagementException if an error occurs. 1409 */ 1410 protected void sendSignupConfirmMail(TempUser tempUser, String language) throws UserManagementException 1411 { 1412 String email = tempUser.getEmail(); 1413 String siteName = tempUser.getSite(); 1414 String populationId = tempUser.getPopulation(); 1415 String userDirectoryId = tempUser.getUserDirectoryId(); 1416 String token = tempUser.getToken(); 1417 String lastname = tempUser.getLastname(); 1418 String firstname = tempUser.getFirstname(); 1419 String fullname = (StringUtils.isEmpty(firstname) ? "" : " " + firstname) + (StringUtils.isEmpty(lastname) ? "" : " " + lastname); 1420 boolean fromInvitation = tempUser.getOrigin() == TempUserOrigin.INVITATION; 1421 1422 if (getLogger().isDebugEnabled()) 1423 { 1424 getLogger().debug("Sending signup confirmation e-mail to " + email); 1425 } 1426 1427 Site site = _siteManager.getSite(siteName); 1428 String from = site.getValue("site-mail-from"); 1429 1430 if (!SendMailHelper.EMAIL_VALIDATION.matcher(StringUtils.trimToEmpty(email)).matches()) 1431 { 1432 throw new UserManagementException("Cannot send signup email. The email is invalid [" + email + "]", StatusError.INVALID_EMAIL); 1433 } 1434 1435 // Prepare mail. 1436 Map<String, I18nizableTextParameter> i18nParams = new HashMap<>(); 1437 i18nParams.put("siteName", new I18nizableText(siteName)); 1438 i18nParams.put("email", new I18nizableText(email)); 1439 i18nParams.put("fullName", new I18nizableText(fullname)); 1440 i18nParams.put("token", new I18nizableText(tempUser.getToken())); 1441 i18nParams.put("tokenValidity", new I18nizableText(String.valueOf(_userSignUpConfiguration.getTokenValidity()))); 1442 1443 Page signupPage = getSignupPage(siteName, language, populationId, userDirectoryId); 1444 if (signupPage != null) 1445 { 1446 RenderingContext currentContext = _renderingContextHandler.getRenderingContext(); 1447 1448 try 1449 { 1450 // Force front context to resolver signup page uri 1451 _renderingContextHandler.setRenderingContext(RenderingContext.FRONT); 1452 1453 String encodedEmail = URIUtils.encodeParameter(email); 1454 1455 String confirmUri = ResolveURIComponent.resolve("page", signupPage.getId(), false, true); 1456 confirmUri = confirmUri + "?email=" + encodedEmail + "&token=" + token; 1457 1458 if (StringUtils.isNotEmpty(lastname)) 1459 { 1460 confirmUri += "&lastname=" + URIUtils.encodeParameter(lastname); 1461 } 1462 if (StringUtils.isNotEmpty(firstname)) 1463 { 1464 confirmUri += "&firstname=" + URIUtils.encodeParameter(firstname); 1465 } 1466 1467 i18nParams.put("confirmUri", new I18nizableText(confirmUri)); 1468 } 1469 finally 1470 { 1471 _renderingContextHandler.setRenderingContext(currentContext); 1472 } 1473 } 1474 else 1475 { 1476 throw new UserManagementException("No signup page found for site " + siteName + " and language " + language, StatusError.NO_SIGNUP_PAGE); 1477 } 1478 1479 // Add site information in the parameters. 1480 i18nParams.put("siteTitle", new I18nizableText(site.getTitle())); 1481 i18nParams.put("siteUrl", new I18nizableText(site.getUrl())); 1482 1483 String subject = fromInvitation ? _userSignUpConfiguration.getSubjectForInvitationToSignUpEmail(i18nParams, language) : _userSignUpConfiguration.getSubjectForSignUpEmail(i18nParams, language); 1484 String textBody = fromInvitation ? _userSignUpConfiguration.getTextBodyForInvitationToSignUpEmail(i18nParams, language) : _userSignUpConfiguration.getTextBodyForSignUpEmail(i18nParams, language); 1485 String htmlBody = fromInvitation ? _userSignUpConfiguration.getHtmlBodyForInvitationToSignUpEmail(i18nParams, language) : _userSignUpConfiguration.getHtmlBodyForSignUpEmail(i18nParams, language); 1486 1487 try 1488 { 1489 List<String> errorReport = new ArrayList<>(); 1490 1491 String wrappedHTMLBody = null; 1492 try 1493 { 1494 wrappedHTMLBody = htmlBody != null ? StandardMailBodyHelper.newHTMLBody() 1495 .withTitle(subject) 1496 .withMessage(htmlBody) 1497 .withLink(site.getUrl(), new I18nizableText("plugin.web", "PLUGINS_WEB_USER_SIGNUP_SITE_LINK_TITLE")) 1498 .build() : null; 1499 } 1500 catch (IOException e) 1501 { 1502 wrappedHTMLBody = htmlBody; 1503 getLogger().warn("Failed to build wrapped HTML body for signup up email", e); 1504 } 1505 1506 // Send the e-mail. 1507 SendMailHelper.newMail() 1508 .withSubject(subject) 1509 .withHTMLBody(wrappedHTMLBody) 1510 .withTextBody(textBody) 1511 .withSender(from) 1512 .withRecipient(email) 1513 .withErrorReport(errorReport) 1514 .sendMail(); 1515 1516 if (errorReport.contains(email)) 1517 { 1518 throw new UserManagementException("Error sending the sign-up confirmation mail.", StatusError.MAIL_ERROR); 1519 } 1520 } 1521 catch (MessagingException | IOException e) 1522 { 1523 throw new UserManagementException("Error sending the sign-up confirmation mail.", StatusError.MAIL_ERROR, e); 1524 } 1525 } 1526 1527 /** 1528 * Send a sign-up confirmation link by e-mail. 1529 * @param siteName the site name. 1530 * @param language the e-mail language. 1531 * @param user the created user 1532 * @throws UserManagementException if an error occurs. 1533 */ 1534 protected void sendSignupValidatedMail(String siteName, String language, User user) throws UserManagementException 1535 { 1536 Site site = _siteManager.getSite(siteName); 1537 String from = site.getValue("site-mail-from"); 1538 String email = user.getEmail(); 1539 1540 if (getLogger().isDebugEnabled()) 1541 { 1542 getLogger().debug("Sending signup validation e-mail to " + email); 1543 } 1544 1545 // Prepare mail. 1546 Map<String, I18nizableTextParameter> i18nParams = new HashMap<>(); 1547 i18nParams.put("siteName", new I18nizableText(siteName)); 1548 i18nParams.put("fullName", new I18nizableText(user.getFullName())); 1549 i18nParams.put("email", new I18nizableText(user.getEmail())); 1550 i18nParams.put("login", new I18nizableText(user.getIdentity().getLogin())); 1551 1552 // Add site information in the parameters. 1553 i18nParams.put("siteTitle", new I18nizableText(site.getTitle())); 1554 i18nParams.put("siteUrl", new I18nizableText(site.getUrl())); 1555 1556 String subject = _userSignUpConfiguration.getSubjectForSignUpValidatedEmail(i18nParams, language); 1557 String textBody = _userSignUpConfiguration.getTextBodyForSignUpValidatedEmail(i18nParams, language); 1558 String htmlBody = _userSignUpConfiguration.getHtmlBodyForSignUpValidatedEmail(i18nParams, language); 1559 1560 try 1561 { 1562 String wrappedHTMLBody = null; 1563 try 1564 { 1565 wrappedHTMLBody = htmlBody != null ? StandardMailBodyHelper.newHTMLBody() 1566 .withTitle(subject) 1567 .withMessage(htmlBody) 1568 .withLink(site.getUrl(), new I18nizableText("plugin.web", "PLUGINS_WEB_USER_SIGNUP_SITE_LINK_TITLE")) 1569 .build() : null; 1570 } 1571 catch (IOException e) 1572 { 1573 wrappedHTMLBody = htmlBody; 1574 getLogger().warn("Failed to build wrapped HTML body for signup up validation email", e); 1575 } 1576 1577 // Send the e-mail. 1578 SendMailHelper.newMail() 1579 .withSubject(subject) 1580 .withHTMLBody(wrappedHTMLBody) 1581 .withTextBody(textBody) 1582 .withSender(from) 1583 .withRecipient(email) 1584 .sendMail(); 1585 } 1586 catch (MessagingException | IOException e) 1587 { 1588 throw new UserManagementException("Error sending the sign-up validation mail.", StatusError.MAIL_ERROR, e); 1589 } 1590 } 1591 1592 /** 1593 * Update the sign-up request token: reset the date and set a new token. 1594 * @param siteName the site name. 1595 * @param email the user e-mail. 1596 * @param newToken the new token. 1597 * @param population The id of the population 1598 * @param userDirectoryId The id of the user directory of the population 1599 * @throws UserManagementException if an error occurs. 1600 */ 1601 protected void updateTempToken(String siteName, String email, String newToken, String population, String userDirectoryId) throws UserManagementException 1602 { 1603 try (SqlSession sqlSession = getSession()) 1604 { 1605 String stmtId = "UserSignupManager.updateTempToken"; 1606 1607 Map<String, Object> params = new HashMap<>(); 1608 params.put("tempUsersTable", _tempUsersTable); 1609 1610 Timestamp now = new Timestamp(System.currentTimeMillis()); 1611 params.put("subscription_date", now); 1612 params.put("token", newToken); 1613 params.put("site", siteName); 1614 params.put("email", email); 1615 params.put("population", population); 1616 params.put("userDirectory", userDirectoryId); 1617 1618 sqlSession.update(stmtId, params); 1619 sqlSession.commit(); 1620 } 1621 catch (Exception e) 1622 { 1623 throw new UserManagementException("Database error while resetting the subscription for user [" + email + "]", StatusError.DATABASE_ERROR, e); 1624 } 1625 } 1626 1627 /** 1628 * Create a user password change request in the database. 1629 * @param siteName the site name. 1630 * @param login the user login. 1631 * @param token the generated token. 1632 * @param population the population 1633 * @throws UserManagementException if an error occurs. 1634 */ 1635 public void addPasswordToken(String siteName, String login, String token, String population) throws UserManagementException 1636 { 1637 try (SqlSession sqlSession = getSession()) 1638 { 1639 String stmtId = "UserSignupManager.hasToken"; 1640 1641 Map<String, Object> params = new HashMap<>(); 1642 params.put("pwdChangeTable", _pwdChangeTable); 1643 1644 params.put("site", siteName); 1645 params.put("login", login); 1646 params.put("population", population); 1647 1648 List<Object> pwdEntries = sqlSession.selectList(stmtId, params); 1649 1650 if (pwdEntries.isEmpty()) 1651 { 1652 // Insert a new token. 1653 stmtId = "UserSignupManager.addPasswordToken"; 1654 params = new HashMap<>(); 1655 params.put("pwdChangeTable", _pwdChangeTable); 1656 1657 Timestamp now = new Timestamp(System.currentTimeMillis()); 1658 params.put("site", siteName); 1659 params.put("login", login); 1660 params.put("request_date", now); 1661 params.put("token", token); 1662 params.put("population", population); 1663 1664 sqlSession.insert(stmtId, params); 1665 } 1666 else 1667 { 1668 // Update the existing token. 1669 stmtId = "UserSignupManager.updatePasswordToken"; 1670 params = new HashMap<>(); 1671 params.put("pwdChangeTable", _pwdChangeTable); 1672 1673 Timestamp now = new Timestamp(System.currentTimeMillis()); 1674 params.put("request_date", now); 1675 params.put("token", token); 1676 params.put("site", siteName); 1677 params.put("login", login); 1678 params.put("population", population); 1679 1680 sqlSession.update(stmtId, params); 1681 } 1682 1683 // commit insert or update 1684 sqlSession.commit(); 1685 } 1686 catch (Exception e) 1687 { 1688 throw new UserManagementException("Database error while inserting a password change token for " + login, StatusError.DATABASE_ERROR, e); 1689 } 1690 } 1691 1692 /** 1693 * Get a temporary user from his site name and e-mail. 1694 * @param siteName the site name. 1695 * @param email The temporary user e-mail. Cannot be null. 1696 * @param population the population 1697 * @param userDirectoryId the id of the user directory of the population 1698 * @return the temporary user or null if not found. 1699 * @throws UserManagementException if an error occurs. 1700 */ 1701 protected TempUser getTempUser(String siteName, String email, String population, String userDirectoryId) throws UserManagementException 1702 { 1703 return getTempUser(siteName, email, null, population, userDirectoryId); 1704 } 1705 1706 /** 1707 * Get temporary users properties 1708 * @param emails the emails of temporary users 1709 * @param siteName the site name of temporary users 1710 * @return The temporary users properties 1711 * @throws UserManagementException if an error occurs. 1712 */ 1713 @Callable 1714 public Map<String, Object> getTempUsersProperties(List<String> emails, String siteName) throws UserManagementException 1715 { 1716 List<Map<String, Object>> tempUsers = new ArrayList<>(); 1717 List<String> unknownTempUsers = new ArrayList<>(); 1718 1719 for (String email : emails) 1720 { 1721 TempUser tempUser = getTempUser(siteName, email, null, null); 1722 if (tempUser != null) 1723 { 1724 tempUsers.add(_tempUser2json(tempUser, false, true)); 1725 } 1726 else 1727 { 1728 unknownTempUsers.add(email); 1729 } 1730 } 1731 1732 return Map.of("tempusers", tempUsers, "unknownTempusers", unknownTempUsers); 1733 } 1734 1735 1736 /** 1737 * Get the temporary users from a given site matching the search parameters 1738 * @param siteName the site name 1739 * @param searchParameters the search parameters 1740 * @param offset index of the start of search 1741 * @param limit the maximum number of results 1742 * @param sorts The sorters 1743 * @return the temporary users as JSON object 1744 * @throws UserManagementException if an error occurs. 1745 */ 1746 @Callable 1747 public Map<String, Object> searchTempUsers(String siteName, Map<String, Object> searchParameters, int offset, int limit, List<Map<String, String>> sorts) throws UserManagementException 1748 { 1749 // Remove expired token from user request 1750 removeExpiredTokens(); 1751 1752 Map<String, Object> results = new HashMap<>(); 1753 1754 List<Map<String, Object>> tempUsers = getTempUsers(siteName, searchParameters, offset, limit, sorts).stream() 1755 .map(u -> _tempUser2json(u, true, false)) 1756 .collect(Collectors.toList()); 1757 1758 int totalCount = getTotalCount(siteName, searchParameters); 1759 1760 results.put("users", tempUsers); 1761 results.put("total", totalCount); 1762 1763 return results; 1764 } 1765 1766 private Map<String, Object> _tempUser2json(TempUser tempUser, boolean full, boolean withRights) 1767 { 1768 Map<String, Object> userInfos = new HashMap<>(); 1769 1770 String populationId = tempUser.getPopulation(); 1771 userInfos.put("population", populationId); 1772 1773 String udId = tempUser.getUserDirectoryId(); 1774 userInfos.put("userDirectory", udId); 1775 1776 if (full) 1777 { 1778 UserPopulation userPopulation = _userPopulationDAO.getUserPopulation(populationId); 1779 if (userPopulation != null) 1780 { 1781 userInfos.put("populationLabel", userPopulation.getLabel()); 1782 1783 UserDirectory userDirectory = userPopulation.getUserDirectory(udId); 1784 if (userDirectory != null) 1785 { 1786 String udLabel = userDirectory.getLabel(); 1787 if (StringUtils.isEmpty(udLabel)) 1788 { 1789 String udModelId = userDirectory.getUserDirectoryModelId(); 1790 UserDirectoryModel udModel = _userDirectoryFactory.getExtension(udModelId); 1791 userInfos.put("userDirectoryLabel", udModel.getLabel()); 1792 } 1793 else 1794 { 1795 userInfos.put("userDirectoryLabel", udLabel); 1796 } 1797 } 1798 } 1799 } 1800 1801 userInfos.put("email", tempUser.getEmail()); 1802 userInfos.put("firstname", tempUser.getFirstname()); 1803 userInfos.put("lastname", tempUser.getLastname()); 1804 userInfos.put("origin", tempUser.getOrigin().name()); 1805 userInfos.put("siteName", tempUser.getSite()); 1806 1807 Date rawSubscriptionDate = tempUser.getSubscriptionDate(); 1808 ZonedDateTime subscriptionDate = rawSubscriptionDate.toInstant().atZone(ZoneId.systemDefault()); 1809 ZonedDateTime expirationDate = subscriptionDate.toLocalDate().atStartOfDay(ZoneId.systemDefault()).plusDays(_userSignUpConfiguration.getTokenValidity()); 1810 1811 userInfos.put("subscriptionDate", subscriptionDate); 1812 userInfos.put("expirationDate", expirationDate); 1813 1814 userInfos.put("expired", expirationDate.toLocalDate().isBefore(LocalDate.now())); 1815 1816 if (withRights) 1817 { 1818 boolean canHandle = tempUser.getOrigin() == TempUserOrigin.INVITATION && getAllowedUserPopulationForInvitation(tempUser.getSite()).contains(tempUser.getPopulation()); 1819 userInfos.put("canHandle", canHandle); 1820 } 1821 1822 return userInfos; 1823 } 1824 1825 /** 1826 * Verify that current user is allowed to handle invitation for a given population 1827 * @param siteName the site name. 1828 * @param populationId The population id 1829 * @throws UserManagementException if user is not allowed 1830 */ 1831 protected void checkUserAllowedForInvitation(String siteName, String populationId) throws UserManagementException 1832 { 1833 UserIdentity currentUser = _currentUserProvider.getUser(); 1834 1835 boolean hasRight = _rightManager.hasRight(currentUser, "Web_Rights_HandleInvitations", "/cms") == RightResult.RIGHT_ALLOW 1836 || _rightManager.hasRight(currentUser, "Web_Rights_HandleInvitations_OwnPopulation", "/cms") == RightResult.RIGHT_ALLOW && currentUser.getPopulationId().equals(populationId); 1837 1838 if (!hasRight) 1839 { 1840 throw new UserManagementException("User is not allowed to handle invitations for population '" + populationId + "' and site name '" + siteName + "'", StatusError.USER_NOT_ALLOWED); 1841 } 1842 } 1843 1844 /** 1845 * Get the ids of user populations the current user can handle for invitation 1846 * @param siteName the site name 1847 * @return the ids of user populations handled by current users 1848 */ 1849 public Set<String> getAllowedUserPopulationForInvitation(String siteName) 1850 { 1851 UserIdentity currentUser = _currentUserProvider.getUser(); 1852 boolean canHandleAll = _rightManager.hasRight(currentUser, "Web_Rights_HandleInvitations", "/cms") == RightResult.RIGHT_ALLOW; 1853 1854 Set<String> populationIds = _populationContextHelper.getUserPopulationsOnContexts(List.of("/sites/" + siteName, "/sites-fo/" + siteName), false); 1855 return populationIds.stream() 1856 .filter(pId -> canHandleAll || _rightManager.hasRight(currentUser, "Web_Rights_HandleInvitations_OwnPopulation", "/cms") == RightResult.RIGHT_ALLOW && currentUser.getPopulationId().equals(pId)) 1857 .collect(Collectors.toSet()); 1858 } 1859 1860 /** 1861 * Get the temporary users from a given site matching the search parameters 1862 * @param siteName the site name. Can not be null 1863 * @param searchParameters The search parameters 1864 * @param offset index of the start of search 1865 * @param limit the maximum number of results 1866 * @param sorts The sorters. Can be null or empty 1867 * @return the temporary users 1868 * @throws UserManagementException if an error occurs. 1869 */ 1870 public List<TempUser> getTempUsers(String siteName, Map<String, Object> searchParameters, int offset, int limit, List<Map<String, String>> sorts) throws UserManagementException 1871 { 1872 try (SqlSession sqlSession = getSession(); Connection connection = sqlSession.getConnection();) 1873 { 1874 String stmtId = "UserSignupManager.searchTempUsers"; 1875 1876 Map<String, Object> params = new HashMap<>(); 1877 params.put("tempUsersTable", _tempUsersTable); 1878 params.put("databaseType", ConnectionHelper.getDatabaseType(connection)); 1879 1880 params.put("site", siteName); 1881 if (sorts != null && !sorts.isEmpty()) 1882 { 1883 params.put("sorts", sorts); 1884 } 1885 else 1886 { 1887 // default sort by email 1888 params.put("sorts", List.of(Map.of("property", "email", "direction", "ASC"))); 1889 } 1890 1891 for (Entry<String, Object> searchParameter : searchParameters.entrySet()) 1892 { 1893 String paramName = searchParameter.getKey(); 1894 Object value = searchParameter.getValue(); 1895 1896 if (value != null && !(value instanceof String) || value instanceof String strValue && StringUtils.isNotEmpty(strValue)) 1897 { 1898 params.put(paramName, "pattern".equals(paramName) ? StringUtils.lowerCase((String) value) : value); 1899 } 1900 } 1901 1902 return sqlSession.selectList(stmtId, params, new RowBounds(offset, limit)); 1903 } 1904 catch (Exception e) 1905 { 1906 throw new UserManagementException("Database error while getting temporay users for site '" + siteName + "'", StatusError.DATABASE_ERROR, e); 1907 } 1908 } 1909 1910 /** 1911 * Get the number of temporary users matching the search parameters 1912 * @param siteName the site name. Can not be null 1913 * @param searchParameters The search parameters 1914 * @return the total number of results 1915 * @throws UserManagementException if an error occurs. 1916 */ 1917 public int getTotalCount(String siteName, Map<String, Object> searchParameters) throws UserManagementException 1918 { 1919 try (SqlSession session = getSession(); Connection connection = session.getConnection();) 1920 { 1921 1922 Map<String, Object> params = new HashMap<>(); 1923 params.put("tempUsersTable", _tempUsersTable); 1924 params.put("databaseType", ConnectionHelper.getDatabaseType(connection)); 1925 1926 params.put("site", siteName); 1927 1928 for (Entry<String, Object> searchParameter : searchParameters.entrySet()) 1929 { 1930 String paramName = searchParameter.getKey(); 1931 Object value = searchParameter.getValue(); 1932 1933 if (value != null && !(value instanceof String) || value instanceof String strValue && StringUtils.isNotEmpty(strValue)) 1934 { 1935 params.put(paramName, value); 1936 } 1937 } 1938 return session.selectOne("UserSignupManager.getTotalCount", params); 1939 } 1940 catch (SQLException e) 1941 { 1942 throw new UserManagementException("Database error while getting temporay users for site '" + siteName + "'", StatusError.DATABASE_ERROR, e); 1943 } 1944 } 1945 1946 /** 1947 * Get a temporary user from his site name, e-mail and/or token. 1948 * At least one of e-mail and token must be provided. 1949 * @param siteName the site name. 1950 * @param email The temporary user e-mail. Can be null. 1951 * @param token The temporary user token. Can be null. 1952 * @param population the population. Must be not null if email is not null 1953 * @param userDirectoryId the id of the user directory of the population. Must be not null if email is not null 1954 * @return the temporary user or null if not found. 1955 * @throws UserManagementException if an error occurs. 1956 */ 1957 protected TempUser getTempUser(String siteName, String email, String token, String population, String userDirectoryId) throws UserManagementException 1958 { 1959 if (StringUtils.isEmpty(email) && StringUtils.isEmpty(token)) 1960 { 1961 throw new UserManagementException("Either e-mail or token must be provided."); 1962 } 1963 1964 try (SqlSession sqlSession = getSession()) 1965 { 1966 // Does this email already exists 1967 String stmtId = "UserSignupManager.getTempUser"; 1968 1969 Map<String, Object> params = new HashMap<>(); 1970 params.put("tempUsersTable", _tempUsersTable); 1971 1972 params.put("site", siteName); 1973 if (StringUtils.isNotEmpty(email)) 1974 { 1975 params.put("email", email); 1976 if (StringUtils.isNotEmpty(population)) 1977 { 1978 params.put("population", population); 1979 } 1980 if (StringUtils.isNotEmpty(userDirectoryId)) 1981 { 1982 params.put("userDirectory", userDirectoryId); 1983 } 1984 } 1985 1986 if (StringUtils.isNotEmpty(token)) 1987 { 1988 params.put("token", token); 1989 } 1990 1991 return sqlSession.selectOne(stmtId, params); 1992 } 1993 catch (Exception e) 1994 { 1995 throw new UserManagementException("Database error while getting the request for user [" + email + "] and token [" + token + "]", StatusError.DATABASE_ERROR, e); 1996 } 1997 } 1998 1999 /** 2000 * Remove the temporary . 2001 * @param siteName the site name. 2002 * @param email the user e-mail address. 2003 * @param token the request token. 2004 * @param population the population 2005 * @param userDirectoryId the id of the user directory of the population 2006 * @throws UserManagementException if an error occurs. 2007 */ 2008 protected void removeTempUser(String siteName, String email, String token, String population, String userDirectoryId) throws UserManagementException 2009 { 2010 try (SqlSession sqlSession = getSession()) 2011 { 2012 String stmtId = "UserSignupManager.removeTempUser"; 2013 2014 Map<String, Object> params = new HashMap<>(); 2015 params.put("tempUsersTable", _tempUsersTable); 2016 2017 params.put("site", siteName); 2018 params.put("email", email); 2019 2020 if (StringUtils.isNotBlank(token)) 2021 { 2022 params.put("token", token); 2023 } 2024 if (StringUtils.isNotBlank(population)) 2025 { 2026 params.put("population", population); 2027 } 2028 if (StringUtils.isNotBlank(userDirectoryId)) 2029 { 2030 params.put("userDirectory", userDirectoryId); 2031 } 2032 sqlSession.delete(stmtId, params); 2033 sqlSession.commit(); 2034 } 2035 catch (Exception e) 2036 { 2037 throw new UserManagementException("Database error while removing the token of user " + email, StatusError.DATABASE_ERROR, e); 2038 } 2039 } 2040 2041 /** 2042 * Remove the password change request. 2043 * @param siteName the site name. 2044 * @param login the user login. 2045 * @param token the request token. 2046 * @param population the population 2047 * @throws UserManagementException if an error occurs. 2048 */ 2049 protected void removePasswordToken(String siteName, String login, String token, String population) throws UserManagementException 2050 { 2051 try (SqlSession sqlSession = getSession()) 2052 { 2053 String stmtId = "UserSignupManager.removePasswordToken"; 2054 2055 Map<String, Object> params = new HashMap<>(); 2056 params.put("pwdChangeTable", _pwdChangeTable); 2057 2058 params.put("site", siteName); 2059 params.put("login", login); 2060 params.put("token", token); 2061 params.put("population", population); 2062 2063 sqlSession.delete(stmtId, params); 2064 sqlSession.commit(); 2065 } 2066 catch (Exception e) 2067 { 2068 throw new UserManagementException("Database error while removing the token of user " + login, StatusError.DATABASE_ERROR, e); 2069 } 2070 } 2071 2072 /** 2073 * Send a sign-up confirmation link by e-mail. 2074 * @param siteName the site name. 2075 * @param language the e-mail language. 2076 * @param user the user object. 2077 * @param token the generated token. 2078 * @throws UserManagementException if an error occurs. 2079 */ 2080 protected void sendResetPasswordMail(String siteName, String language, User user, String token) throws UserManagementException 2081 { 2082 String login = user.getIdentity().getLogin(); 2083 String population = user.getIdentity().getPopulationId(); 2084 2085 if (getLogger().isDebugEnabled()) 2086 { 2087 getLogger().debug("Sending reset password e-mail to " + login); 2088 } 2089 2090 Site site = _siteManager.getSite(siteName); 2091 String from = site.getValue("site-mail-from"); 2092 2093 // Prepare mail 2094 Map<String, I18nizableTextParameter> i18nParams = new HashMap<>(); 2095 i18nParams.put("siteName", new I18nizableText(siteName)); 2096 i18nParams.put("login", new I18nizableText(login)); 2097 i18nParams.put("email", new I18nizableText(user.getEmail())); 2098 i18nParams.put("fullName", new I18nizableText(user.getFullName())); 2099 i18nParams.put("token", new I18nizableText(token)); 2100 2101 Page passwordPage = getPwdChangePage(siteName, language); 2102 if (passwordPage != null) 2103 { 2104 // Compute the confirmation URI and add it to the parameters. 2105 String confirmUri = getResetPasswordUri(passwordPage, login, population, token, true); 2106 i18nParams.put("confirmUri", new I18nizableText(confirmUri)); 2107 } 2108 else 2109 { 2110 throw new UserManagementException("No password change page found for site " + siteName + " and language " + language, StatusError.NO_PASSWORD_CHANGE_PAGE); 2111 } 2112 2113 // Add site information in the parameters. 2114 i18nParams.put("siteTitle", new I18nizableText(site.getTitle())); 2115 i18nParams.put("siteUrl", new I18nizableText(site.getUrl())); 2116 2117 String subject = _userSignUpConfiguration.getSubjectForResetPwdEmail(i18nParams, language); 2118 String textBody = _userSignUpConfiguration.getTextBodyForResetPwdEmail(i18nParams, language); 2119 String htmlBody = _userSignUpConfiguration.getHtmlBodyForResetPwdEmail(i18nParams, language); 2120 2121 try 2122 { 2123 String wrappedHTMLBody = null; 2124 try 2125 { 2126 wrappedHTMLBody = htmlBody != null ? StandardMailBodyHelper.newHTMLBody() 2127 .withTitle(subject) 2128 .withMessage(htmlBody) 2129 .withLink(site.getUrl(), new I18nizableText("plugin.web", "PLUGINS_WEB_USER_SIGNUP_SITE_LINK_TITLE")) 2130 .build() : null; 2131 } 2132 catch (IOException e) 2133 { 2134 wrappedHTMLBody = htmlBody; 2135 getLogger().warn("Failed to build wrapped HTML body for reset password email", e); 2136 } 2137 2138 // Send the e-mail. 2139 SendMailHelper.newMail() 2140 .withSubject(subject) 2141 .withHTMLBody(wrappedHTMLBody) 2142 .withTextBody(textBody) 2143 .withSender(from) 2144 .withRecipient(user.getEmail()) 2145 .sendMail(); 2146 } 2147 catch (MessagingException | IOException e) 2148 { 2149 throw new UserManagementException("Error sending a password reset e-mail.", StatusError.MAIL_ERROR, e); 2150 } 2151 } 2152 2153 /** 2154 * Get the URI to reset a password 2155 * @param passwordPage The passwodr page. Can not be null. 2156 * @param login the user login 2157 * @param population the user population 2158 * @param token the generated token 2159 * @param absolute true to get absolute uri 2160 * @return the computed uri 2161 */ 2162 public String getResetPasswordUri(Page passwordPage, String login, String population, String token, boolean absolute) 2163 { 2164 String encodedLogin = URIUtils.encodeParameter(login); 2165 String encodedPopulation = URIUtils.encodeParameter(population); 2166 2167 // Compute the confirmation URI and add it to the parameters. 2168 String confirmUri = ResolveURIComponent.resolve("page", passwordPage.getId(), false, absolute); 2169 return confirmUri + "?login=" + encodedLogin + "&population=" + encodedPopulation + "&token=" + token; 2170 } 2171 2172 /** 2173 * Bean representing a user sign-up request. 2174 */ 2175 public static class TempUser 2176 { 2177 /** The site name. */ 2178 protected String _site; 2179 2180 /** The user e-mail. */ 2181 protected String _email; 2182 2183 /** The user subscription date. */ 2184 protected Date _subscriptionDate; 2185 2186 /** The request token. */ 2187 protected String _token; 2188 2189 /** The id of the population */ 2190 protected String _population; 2191 2192 /** The id of the user directory of the population */ 2193 protected String _userDirectoryId; 2194 2195 /** The user last name */ 2196 protected String _lastname; 2197 2198 /** The user first name */ 2199 protected String _firstname; 2200 2201 /** The user origin */ 2202 protected TempUserOrigin _origin; 2203 2204 /** 2205 * Enumeration for temporary user origin 2206 * 2207 */ 2208 public enum TempUserOrigin 2209 { 2210 /** When the signup request comes from an invitation */ 2211 INVITATION, 2212 /** When the signup request is an user request */ 2213 USER_REQUEST, 2214 /** When the origin of signup request is unknown */ 2215 UNKNWON 2216 } 2217 2218 /** 2219 * Constructor. 2220 * @param site the site 2221 * @param email the user's email 2222 * @param subscriptionDate the date of subscription 2223 * @param token the user's token 2224 * @param population The id of the population 2225 * @param userDirectoryId The id of the user directory of the population 2226 * @param lastname the user's lastname 2227 * @param firstname the user's firstname 2228 * @param origin the origin of this temp user 2229 */ 2230 public TempUser(String site, String email, Date subscriptionDate, String token, String population, String userDirectoryId, String lastname, String firstname, String origin) 2231 { 2232 this._site = site; 2233 this._email = email; 2234 this._subscriptionDate = subscriptionDate; 2235 this._token = token; 2236 this._population = population; 2237 this._userDirectoryId = userDirectoryId; 2238 this._lastname = lastname; 2239 this._firstname = firstname; 2240 this._origin = StringUtils.isNotBlank(origin) ? TempUserOrigin.valueOf(origin) : TempUserOrigin.UNKNWON; 2241 } 2242 /** 2243 * Get the site. 2244 * @return the site 2245 */ 2246 public String getSite() 2247 { 2248 return _site; 2249 } 2250 /** 2251 * Set the site. 2252 * @param site the site to set 2253 */ 2254 public void setSite(String site) 2255 { 2256 this._site = site; 2257 } 2258 /** 2259 * Get the email. 2260 * @return _the email 2261 */ 2262 public String getEmail() 2263 { 2264 return _email; 2265 } 2266 /** 2267 * Set the email. 2268 * @param email the email to set 2269 */ 2270 public void setEmail(String email) 2271 { 2272 this._email = email; 2273 } 2274 /** 2275 * Get the subscriptionDate. 2276 * @return _the subscriptionDate 2277 */ 2278 public Date getSubscriptionDate() 2279 { 2280 return _subscriptionDate; 2281 } 2282 /** 2283 * Set the subscriptionDate. 2284 * @param subscriptionDate the subscriptionDate to set 2285 */ 2286 public void setSubscriptionDate(Date subscriptionDate) 2287 { 2288 this._subscriptionDate = subscriptionDate; 2289 } 2290 /** 2291 * Get the token. 2292 * @return _the token 2293 */ 2294 public String getToken() 2295 { 2296 return _token; 2297 } 2298 /** 2299 * Set the token. 2300 * @param token the token to set 2301 */ 2302 public void setToken(String token) 2303 { 2304 this._token = token; 2305 } 2306 /** 2307 * Get the population. 2308 * @return the population 2309 */ 2310 public String getPopulation() 2311 { 2312 return _population; 2313 } 2314 /** 2315 * Set the population. 2316 * @param population the population to set 2317 */ 2318 public void setPopulation(String population) 2319 { 2320 this._population = population; 2321 } 2322 /** 2323 * Get the user directory id. 2324 * @return the user directory id 2325 */ 2326 public String getUserDirectoryId() 2327 { 2328 return _userDirectoryId; 2329 } 2330 /** 2331 * Set the user directory index. 2332 * @param userDirectoryId the user directory id to set 2333 */ 2334 public void setUserDirectoryIndex(String userDirectoryId) 2335 { 2336 this._userDirectoryId = userDirectoryId; 2337 } 2338 2339 /** 2340 * Get the user firstname 2341 * @return the user firstname 2342 */ 2343 public String getFirstname() 2344 { 2345 return _firstname; 2346 } 2347 2348 /** 2349 * Set the user firstname 2350 * @param firstname the user firstname 2351 */ 2352 public void setFirstname(String firstname) 2353 { 2354 _firstname = firstname; 2355 } 2356 2357 /** 2358 * Get the user firstname 2359 * @return the user firstname 2360 */ 2361 public String getLastname() 2362 { 2363 return _lastname; 2364 } 2365 2366 /** 2367 * Set the user lastname 2368 * @param lastname the user lastname 2369 */ 2370 public void setLastname(String lastname) 2371 { 2372 _lastname = lastname; 2373 } 2374 2375 /** 2376 * Return the temp user origin 2377 * @return true the temp user origin 2378 */ 2379 public TempUserOrigin getOrigin() 2380 { 2381 return _origin; 2382 } 2383 2384 /** 2385 * Set the origin of this temporary user 2386 * @param origin the origin 2387 */ 2388 public void setOrigin(TempUserOrigin origin) 2389 { 2390 _origin = origin; 2391 } 2392 } 2393 2394}