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.user.CurrentUserProvider; 055import org.ametys.core.user.InvalidModificationException; 056import org.ametys.core.user.User; 057import org.ametys.core.user.User.UserCreationOrigin; 058import org.ametys.core.user.UserIdentity; 059import org.ametys.core.user.UserManager; 060import org.ametys.core.user.directory.ModifiableUserDirectory; 061import org.ametys.core.user.directory.NotUniqueUserException; 062import org.ametys.core.user.directory.UserDirectory; 063import org.ametys.core.user.directory.UserDirectoryFactory; 064import org.ametys.core.user.directory.UserDirectoryModel; 065import org.ametys.core.user.population.PopulationContextHelper; 066import org.ametys.core.user.population.UserPopulation; 067import org.ametys.core.user.population.UserPopulationDAO; 068import org.ametys.core.util.I18nUtils; 069import org.ametys.core.util.URIUtils; 070import org.ametys.core.util.mail.SendMailHelper; 071import org.ametys.plugins.repository.AmetysObjectIterable; 072import org.ametys.plugins.repository.AmetysObjectResolver; 073import org.ametys.plugins.repository.AmetysRepositoryException; 074import org.ametys.plugins.repository.query.expression.Expression; 075import org.ametys.plugins.repository.query.expression.Expression.Operator; 076import org.ametys.runtime.i18n.I18nizableText; 077import org.ametys.runtime.i18n.I18nizableTextParameter; 078import org.ametys.runtime.parameter.Errors; 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, Errors> 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, Errors> usersManagerErrors = new HashMap<>(); //foUsersManager.validate(userInfos); 631 Map<String, Errors> 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 Map<String, Errors> 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)) 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, Errors> usersManagerErrors = ((ModifiableUserDirectory) userDirectory).validate(userInfos); 667 Map<String, Errors> errors = new HashMap<>(); 668 669 // Keep only errors related to the password field. 670 if (usersManagerErrors.containsKey("password")) 671 { 672 errors.put("password", 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 = userDirectory.getUser(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 // Send the e-mail. 1492 SendMailHelper.newMail() 1493 .withSubject(subject) 1494 .withHTMLBody(htmlBody) 1495 .withTextBody(textBody) 1496 .withSender(from) 1497 .withRecipient(email) 1498 .withErrorReport(errorReport) 1499 .sendMail(); 1500 1501 if (errorReport.contains(email)) 1502 { 1503 throw new UserManagementException("Error sending the sign-up confirmation mail.", StatusError.MAIL_ERROR); 1504 } 1505 } 1506 catch (MessagingException | IOException e) 1507 { 1508 throw new UserManagementException("Error sending the sign-up confirmation mail.", StatusError.MAIL_ERROR, e); 1509 } 1510 } 1511 1512 /** 1513 * Send a sign-up confirmation link by e-mail. 1514 * @param siteName the site name. 1515 * @param language the e-mail language. 1516 * @param user the created user 1517 * @throws UserManagementException if an error occurs. 1518 */ 1519 protected void sendSignupValidatedMail(String siteName, String language, User user) throws UserManagementException 1520 { 1521 Site site = _siteManager.getSite(siteName); 1522 String from = site.getValue("site-mail-from"); 1523 String email = user.getEmail(); 1524 1525 if (getLogger().isDebugEnabled()) 1526 { 1527 getLogger().debug("Sending signup validation e-mail to " + email); 1528 } 1529 1530 // Prepare mail. 1531 Map<String, I18nizableTextParameter> i18nParams = new HashMap<>(); 1532 i18nParams.put("siteName", new I18nizableText(siteName)); 1533 i18nParams.put("fullName", new I18nizableText(user.getFullName())); 1534 i18nParams.put("email", new I18nizableText(user.getEmail())); 1535 i18nParams.put("login", new I18nizableText(user.getIdentity().getLogin())); 1536 1537 // Add site information in the parameters. 1538 i18nParams.put("siteTitle", new I18nizableText(site.getTitle())); 1539 i18nParams.put("siteUrl", new I18nizableText(site.getUrl())); 1540 1541 String subject = _userSignUpConfiguration.getSubjectForSignUpValidatedEmail(i18nParams, language); 1542 String textBody = _userSignUpConfiguration.getTextBodyForSignUpValidatedEmail(i18nParams, language); 1543 String htmlBody = _userSignUpConfiguration.getHtmlBodyForSignUpValidatedEmail(i18nParams, language); 1544 1545 try 1546 { 1547 // Send the e-mail. 1548 SendMailHelper.newMail() 1549 .withSubject(subject) 1550 .withHTMLBody(htmlBody) 1551 .withTextBody(textBody) 1552 .withSender(from) 1553 .withRecipient(email) 1554 .sendMail(); 1555 } 1556 catch (MessagingException | IOException e) 1557 { 1558 throw new UserManagementException("Error sending the sign-up validation mail.", StatusError.MAIL_ERROR, e); 1559 } 1560 } 1561 1562 /** 1563 * Update the sign-up request token: reset the date and set a new token. 1564 * @param siteName the site name. 1565 * @param email the user e-mail. 1566 * @param newToken the new token. 1567 * @param population The id of the population 1568 * @param userDirectoryId The id of the user directory of the population 1569 * @throws UserManagementException if an error occurs. 1570 */ 1571 protected void updateTempToken(String siteName, String email, String newToken, String population, String userDirectoryId) throws UserManagementException 1572 { 1573 try (SqlSession sqlSession = getSession()) 1574 { 1575 String stmtId = "UserSignupManager.updateTempToken"; 1576 1577 Map<String, Object> params = new HashMap<>(); 1578 params.put("tempUsersTable", _tempUsersTable); 1579 1580 Timestamp now = new Timestamp(System.currentTimeMillis()); 1581 params.put("subscription_date", now); 1582 params.put("token", newToken); 1583 params.put("site", siteName); 1584 params.put("email", email); 1585 params.put("population", population); 1586 params.put("userDirectory", userDirectoryId); 1587 1588 sqlSession.update(stmtId, params); 1589 sqlSession.commit(); 1590 } 1591 catch (Exception e) 1592 { 1593 throw new UserManagementException("Database error while resetting the subscription for user [" + email + "]", StatusError.DATABASE_ERROR, e); 1594 } 1595 } 1596 1597 /** 1598 * Create a user password change request in the database. 1599 * @param siteName the site name. 1600 * @param login the user login. 1601 * @param token the generated token. 1602 * @param population the population 1603 * @throws UserManagementException if an error occurs. 1604 */ 1605 public void addPasswordToken(String siteName, String login, String token, String population) throws UserManagementException 1606 { 1607 try (SqlSession sqlSession = getSession()) 1608 { 1609 String stmtId = "UserSignupManager.hasToken"; 1610 1611 Map<String, Object> params = new HashMap<>(); 1612 params.put("pwdChangeTable", _pwdChangeTable); 1613 1614 params.put("site", siteName); 1615 params.put("login", login); 1616 params.put("population", population); 1617 1618 List<Object> pwdEntries = sqlSession.selectList(stmtId, params); 1619 1620 if (pwdEntries.isEmpty()) 1621 { 1622 // Insert a new token. 1623 stmtId = "UserSignupManager.addPasswordToken"; 1624 params = new HashMap<>(); 1625 params.put("pwdChangeTable", _pwdChangeTable); 1626 1627 Timestamp now = new Timestamp(System.currentTimeMillis()); 1628 params.put("site", siteName); 1629 params.put("login", login); 1630 params.put("request_date", now); 1631 params.put("token", token); 1632 params.put("population", population); 1633 1634 sqlSession.insert(stmtId, params); 1635 } 1636 else 1637 { 1638 // Update the existing token. 1639 stmtId = "UserSignupManager.updatePasswordToken"; 1640 params = new HashMap<>(); 1641 params.put("pwdChangeTable", _pwdChangeTable); 1642 1643 Timestamp now = new Timestamp(System.currentTimeMillis()); 1644 params.put("request_date", now); 1645 params.put("token", token); 1646 params.put("site", siteName); 1647 params.put("login", login); 1648 params.put("population", population); 1649 1650 sqlSession.update(stmtId, params); 1651 } 1652 1653 // commit insert or update 1654 sqlSession.commit(); 1655 } 1656 catch (Exception e) 1657 { 1658 throw new UserManagementException("Database error while inserting a password change token for " + login, StatusError.DATABASE_ERROR, e); 1659 } 1660 } 1661 1662 /** 1663 * Get a temporary user from his site name and e-mail. 1664 * @param siteName the site name. 1665 * @param email The temporary user e-mail. Cannot be null. 1666 * @param population the population 1667 * @param userDirectoryId the id of the user directory of the population 1668 * @return the temporary user or null if not found. 1669 * @throws UserManagementException if an error occurs. 1670 */ 1671 protected TempUser getTempUser(String siteName, String email, String population, String userDirectoryId) throws UserManagementException 1672 { 1673 return getTempUser(siteName, email, null, population, userDirectoryId); 1674 } 1675 1676 /** 1677 * Get temporary users properties 1678 * @param emails the emails of temporary users 1679 * @param siteName the site name of temporary users 1680 * @return The temporary users properties 1681 * @throws UserManagementException if an error occurs. 1682 */ 1683 @Callable 1684 public Map<String, Object> getTempUsersProperties(List<String> emails, String siteName) throws UserManagementException 1685 { 1686 List<Map<String, Object>> tempUsers = new ArrayList<>(); 1687 List<String> unknownTempUsers = new ArrayList<>(); 1688 1689 for (String email : emails) 1690 { 1691 TempUser tempUser = getTempUser(siteName, email, null, null); 1692 if (tempUser != null) 1693 { 1694 tempUsers.add(_tempUser2json(tempUser, false, true)); 1695 } 1696 else 1697 { 1698 unknownTempUsers.add(email); 1699 } 1700 } 1701 1702 return Map.of("tempusers", tempUsers, "unknownTempusers", unknownTempUsers); 1703 } 1704 1705 1706 /** 1707 * Get the temporary users from a given site matching the search parameters 1708 * @param siteName the site name 1709 * @param searchParameters the search parameters 1710 * @param offset index of the start of search 1711 * @param limit the maximum number of results 1712 * @param sorts The sorters 1713 * @return the temporary users as JSON object 1714 * @throws UserManagementException if an error occurs. 1715 */ 1716 @Callable 1717 public Map<String, Object> searchTempUsers(String siteName, Map<String, Object> searchParameters, int offset, int limit, List<Map<String, String>> sorts) throws UserManagementException 1718 { 1719 // Remove expired token from user request 1720 removeExpiredTokens(); 1721 1722 Map<String, Object> results = new HashMap<>(); 1723 1724 List<Map<String, Object>> tempUsers = getTempUsers(siteName, searchParameters, offset, limit, sorts).stream() 1725 .map(u -> _tempUser2json(u, true, false)) 1726 .collect(Collectors.toList()); 1727 1728 int totalCount = getTotalCount(siteName, searchParameters); 1729 1730 results.put("users", tempUsers); 1731 results.put("total", totalCount); 1732 1733 return results; 1734 } 1735 1736 private Map<String, Object> _tempUser2json(TempUser tempUser, boolean full, boolean withRights) 1737 { 1738 Map<String, Object> userInfos = new HashMap<>(); 1739 1740 String populationId = tempUser.getPopulation(); 1741 userInfos.put("population", populationId); 1742 1743 String udId = tempUser.getUserDirectoryId(); 1744 userInfos.put("userDirectory", udId); 1745 1746 if (full) 1747 { 1748 UserPopulation userPopulation = _userPopulationDAO.getUserPopulation(populationId); 1749 if (userPopulation != null) 1750 { 1751 userInfos.put("populationLabel", userPopulation.getLabel()); 1752 1753 UserDirectory userDirectory = userPopulation.getUserDirectory(udId); 1754 if (userDirectory != null) 1755 { 1756 String udLabel = userDirectory.getLabel(); 1757 if (StringUtils.isEmpty(udLabel)) 1758 { 1759 String udModelId = userDirectory.getUserDirectoryModelId(); 1760 UserDirectoryModel udModel = _userDirectoryFactory.getExtension(udModelId); 1761 userInfos.put("userDirectoryLabel", udModel.getLabel()); 1762 } 1763 else 1764 { 1765 userInfos.put("userDirectoryLabel", udLabel); 1766 } 1767 } 1768 } 1769 } 1770 1771 userInfos.put("email", tempUser.getEmail()); 1772 userInfos.put("firstname", tempUser.getFirstname()); 1773 userInfos.put("lastname", tempUser.getLastname()); 1774 userInfos.put("origin", tempUser.getOrigin().name()); 1775 userInfos.put("siteName", tempUser.getSite()); 1776 1777 Date rawSubscriptionDate = tempUser.getSubscriptionDate(); 1778 ZonedDateTime subscriptionDate = rawSubscriptionDate.toInstant().atZone(ZoneId.systemDefault()); 1779 ZonedDateTime expirationDate = subscriptionDate.toLocalDate().atStartOfDay(ZoneId.systemDefault()).plusDays(_userSignUpConfiguration.getTokenValidity()); 1780 1781 userInfos.put("subscriptionDate", subscriptionDate); 1782 userInfos.put("expirationDate", expirationDate); 1783 1784 userInfos.put("expired", expirationDate.toLocalDate().isBefore(LocalDate.now())); 1785 1786 if (withRights) 1787 { 1788 boolean canHandle = tempUser.getOrigin() == TempUserOrigin.INVITATION && getAllowedUserPopulationForInvitation(tempUser.getSite()).contains(tempUser.getPopulation()); 1789 userInfos.put("canHandle", canHandle); 1790 } 1791 1792 return userInfos; 1793 } 1794 1795 /** 1796 * Verify that current user is allowed to handle invitation for a given population 1797 * @param siteName the site name. 1798 * @param populationId The population id 1799 * @throws UserManagementException if user is not allowed 1800 */ 1801 protected void checkUserAllowedForInvitation(String siteName, String populationId) throws UserManagementException 1802 { 1803 UserIdentity currentUser = _currentUserProvider.getUser(); 1804 1805 boolean hasRight = _rightManager.hasRight(currentUser, "Web_Rights_HandleInvitations", "/cms") == RightResult.RIGHT_ALLOW 1806 || _rightManager.hasRight(currentUser, "Web_Rights_HandleInvitations_OwnPopulation", "/cms") == RightResult.RIGHT_ALLOW && currentUser.getPopulationId().equals(populationId); 1807 1808 if (!hasRight) 1809 { 1810 throw new UserManagementException("User is not allowed to handle invitations for population '" + populationId + "' and site name '" + siteName + "'", StatusError.USER_NOT_ALLOWED); 1811 } 1812 } 1813 1814 /** 1815 * Get the ids of user populations the current user can handle for invitation 1816 * @param siteName the site name 1817 * @return the ids of user populations handled by current users 1818 */ 1819 public Set<String> getAllowedUserPopulationForInvitation(String siteName) 1820 { 1821 UserIdentity currentUser = _currentUserProvider.getUser(); 1822 boolean canHandleAll = _rightManager.hasRight(currentUser, "Web_Rights_HandleInvitations", "/cms") == RightResult.RIGHT_ALLOW; 1823 1824 Set<String> populationIds = _populationContextHelper.getUserPopulationsOnContexts(List.of("/sites/" + siteName, "/sites-fo/" + siteName), false); 1825 return populationIds.stream() 1826 .filter(pId -> canHandleAll || _rightManager.hasRight(currentUser, "Web_Rights_HandleInvitations_OwnPopulation", "/cms") == RightResult.RIGHT_ALLOW && currentUser.getPopulationId().equals(pId)) 1827 .collect(Collectors.toSet()); 1828 } 1829 1830 /** 1831 * Get the temporary users from a given site matching the search parameters 1832 * @param siteName the site name. Can not be null 1833 * @param searchParameters The search parameters 1834 * @param offset index of the start of search 1835 * @param limit the maximum number of results 1836 * @param sorts The sorters. Can be null or empty 1837 * @return the temporary users 1838 * @throws UserManagementException if an error occurs. 1839 */ 1840 public List<TempUser> getTempUsers(String siteName, Map<String, Object> searchParameters, int offset, int limit, List<Map<String, String>> sorts) throws UserManagementException 1841 { 1842 try (SqlSession sqlSession = getSession(); Connection connection = sqlSession.getConnection();) 1843 { 1844 String stmtId = "UserSignupManager.searchTempUsers"; 1845 1846 Map<String, Object> params = new HashMap<>(); 1847 params.put("tempUsersTable", _tempUsersTable); 1848 params.put("databaseType", ConnectionHelper.getDatabaseType(connection)); 1849 1850 params.put("site", siteName); 1851 if (sorts != null && !sorts.isEmpty()) 1852 { 1853 params.put("sorts", sorts); 1854 } 1855 else 1856 { 1857 // default sort by email 1858 params.put("sorts", List.of(Map.of("property", "email", "direction", "ASC"))); 1859 } 1860 1861 for (Entry<String, Object> searchParameter : searchParameters.entrySet()) 1862 { 1863 String paramName = searchParameter.getKey(); 1864 Object value = searchParameter.getValue(); 1865 1866 if (value != null && !(value instanceof String) || value instanceof String strValue && StringUtils.isNotEmpty(strValue)) 1867 { 1868 params.put(paramName, "pattern".equals(paramName) ? StringUtils.lowerCase((String) value) : value); 1869 } 1870 } 1871 1872 return sqlSession.selectList(stmtId, params, new RowBounds(offset, limit)); 1873 } 1874 catch (Exception e) 1875 { 1876 throw new UserManagementException("Database error while getting temporay users for site '" + siteName + "'", StatusError.DATABASE_ERROR, e); 1877 } 1878 } 1879 1880 /** 1881 * Get the number of temporary users matching the search parameters 1882 * @param siteName the site name. Can not be null 1883 * @param searchParameters The search parameters 1884 * @return the total number of results 1885 * @throws UserManagementException if an error occurs. 1886 */ 1887 public int getTotalCount(String siteName, Map<String, Object> searchParameters) throws UserManagementException 1888 { 1889 try (SqlSession session = getSession(); Connection connection = session.getConnection();) 1890 { 1891 1892 Map<String, Object> params = new HashMap<>(); 1893 params.put("tempUsersTable", _tempUsersTable); 1894 params.put("databaseType", ConnectionHelper.getDatabaseType(connection)); 1895 1896 params.put("site", siteName); 1897 1898 for (Entry<String, Object> searchParameter : searchParameters.entrySet()) 1899 { 1900 String paramName = searchParameter.getKey(); 1901 Object value = searchParameter.getValue(); 1902 1903 if (value != null && !(value instanceof String) || value instanceof String strValue && StringUtils.isNotEmpty(strValue)) 1904 { 1905 params.put(paramName, value); 1906 } 1907 } 1908 return session.selectOne("UserSignupManager.getTotalCount", params); 1909 } 1910 catch (SQLException e) 1911 { 1912 throw new UserManagementException("Database error while getting temporay users for site '" + siteName + "'", StatusError.DATABASE_ERROR, e); 1913 } 1914 } 1915 1916 /** 1917 * Get a temporary user from his site name, e-mail and/or token. 1918 * At least one of e-mail and token must be provided. 1919 * @param siteName the site name. 1920 * @param email The temporary user e-mail. Can be null. 1921 * @param token The temporary user token. Can be null. 1922 * @param population the population. Must be not null if email is not null 1923 * @param userDirectoryId the id of the user directory of the population. Must be not null if email is not null 1924 * @return the temporary user or null if not found. 1925 * @throws UserManagementException if an error occurs. 1926 */ 1927 protected TempUser getTempUser(String siteName, String email, String token, String population, String userDirectoryId) throws UserManagementException 1928 { 1929 if (StringUtils.isEmpty(email) && StringUtils.isEmpty(token)) 1930 { 1931 throw new UserManagementException("Either e-mail or token must be provided."); 1932 } 1933 1934 try (SqlSession sqlSession = getSession()) 1935 { 1936 // Does this email already exists 1937 String stmtId = "UserSignupManager.getTempUser"; 1938 1939 Map<String, Object> params = new HashMap<>(); 1940 params.put("tempUsersTable", _tempUsersTable); 1941 1942 params.put("site", siteName); 1943 if (StringUtils.isNotEmpty(email)) 1944 { 1945 params.put("email", email); 1946 if (StringUtils.isNotEmpty(population)) 1947 { 1948 params.put("population", population); 1949 } 1950 if (StringUtils.isNotEmpty(userDirectoryId)) 1951 { 1952 params.put("userDirectory", userDirectoryId); 1953 } 1954 } 1955 1956 if (StringUtils.isNotEmpty(token)) 1957 { 1958 params.put("token", token); 1959 } 1960 1961 return sqlSession.selectOne(stmtId, params); 1962 } 1963 catch (Exception e) 1964 { 1965 throw new UserManagementException("Database error while getting the request for user [" + email + "] and token [" + token + "]", StatusError.DATABASE_ERROR, e); 1966 } 1967 } 1968 1969 /** 1970 * Remove the temporary . 1971 * @param siteName the site name. 1972 * @param email the user e-mail address. 1973 * @param token the request token. 1974 * @param population the population 1975 * @param userDirectoryId the id of the user directory of the population 1976 * @throws UserManagementException if an error occurs. 1977 */ 1978 protected void removeTempUser(String siteName, String email, String token, String population, String userDirectoryId) throws UserManagementException 1979 { 1980 try (SqlSession sqlSession = getSession()) 1981 { 1982 String stmtId = "UserSignupManager.removeTempUser"; 1983 1984 Map<String, Object> params = new HashMap<>(); 1985 params.put("tempUsersTable", _tempUsersTable); 1986 1987 params.put("site", siteName); 1988 params.put("email", email); 1989 1990 if (StringUtils.isNotBlank(token)) 1991 { 1992 params.put("token", token); 1993 } 1994 if (StringUtils.isNotBlank(population)) 1995 { 1996 params.put("population", population); 1997 } 1998 if (StringUtils.isNotBlank(userDirectoryId)) 1999 { 2000 params.put("userDirectory", userDirectoryId); 2001 } 2002 sqlSession.delete(stmtId, params); 2003 sqlSession.commit(); 2004 } 2005 catch (Exception e) 2006 { 2007 throw new UserManagementException("Database error while removing the token of user " + email, StatusError.DATABASE_ERROR, e); 2008 } 2009 } 2010 2011 /** 2012 * Remove the password change request. 2013 * @param siteName the site name. 2014 * @param login the user login. 2015 * @param token the request token. 2016 * @param population the population 2017 * @throws UserManagementException if an error occurs. 2018 */ 2019 protected void removePasswordToken(String siteName, String login, String token, String population) throws UserManagementException 2020 { 2021 try (SqlSession sqlSession = getSession()) 2022 { 2023 String stmtId = "UserSignupManager.removePasswordToken"; 2024 2025 Map<String, Object> params = new HashMap<>(); 2026 params.put("pwdChangeTable", _pwdChangeTable); 2027 2028 params.put("site", siteName); 2029 params.put("login", login); 2030 params.put("token", token); 2031 params.put("population", population); 2032 2033 sqlSession.delete(stmtId, params); 2034 sqlSession.commit(); 2035 } 2036 catch (Exception e) 2037 { 2038 throw new UserManagementException("Database error while removing the token of user " + login, StatusError.DATABASE_ERROR, e); 2039 } 2040 } 2041 2042 /** 2043 * Send a sign-up confirmation link by e-mail. 2044 * @param siteName the site name. 2045 * @param language the e-mail language. 2046 * @param user the user object. 2047 * @param token the generated token. 2048 * @throws UserManagementException if an error occurs. 2049 */ 2050 protected void sendResetPasswordMail(String siteName, String language, User user, String token) throws UserManagementException 2051 { 2052 String login = user.getIdentity().getLogin(); 2053 String population = user.getIdentity().getPopulationId(); 2054 2055 if (getLogger().isDebugEnabled()) 2056 { 2057 getLogger().debug("Sending reset password e-mail to " + login); 2058 } 2059 2060 Site site = _siteManager.getSite(siteName); 2061 String from = site.getValue("site-mail-from"); 2062 2063 // Prepare mail 2064 Map<String, I18nizableTextParameter> i18nParams = new HashMap<>(); 2065 i18nParams.put("siteName", new I18nizableText(siteName)); 2066 i18nParams.put("login", new I18nizableText(login)); 2067 i18nParams.put("email", new I18nizableText(user.getEmail())); 2068 i18nParams.put("fullName", new I18nizableText(user.getFullName())); 2069 i18nParams.put("token", new I18nizableText(token)); 2070 2071 Page passwordPage = getPwdChangePage(siteName, language); 2072 if (passwordPage != null) 2073 { 2074 // Compute the confirmation URI and add it to the parameters. 2075 String confirmUri = getResetPasswordUri(passwordPage, login, population, token, true); 2076 i18nParams.put("confirmUri", new I18nizableText(confirmUri)); 2077 } 2078 else 2079 { 2080 throw new UserManagementException("No password change page found for site " + siteName + " and language " + language, StatusError.NO_PASSWORD_CHANGE_PAGE); 2081 } 2082 2083 // Add site information in the parameters. 2084 i18nParams.put("siteTitle", new I18nizableText(site.getTitle())); 2085 i18nParams.put("siteUrl", new I18nizableText(site.getUrl())); 2086 2087 String subject = _userSignUpConfiguration.getSubjectForResetPwdEmail(i18nParams, language); 2088 String textBody = _userSignUpConfiguration.getTextBodyForResetPwdEmail(i18nParams, language); 2089 String htmlBody = _userSignUpConfiguration.getHtmlBodyForResetPwdEmail(i18nParams, language); 2090 2091 try 2092 { 2093 // Send the e-mail. 2094 SendMailHelper.newMail() 2095 .withSubject(subject) 2096 .withHTMLBody(htmlBody) 2097 .withTextBody(textBody) 2098 .withSender(from) 2099 .withRecipient(user.getEmail()) 2100 .sendMail(); 2101 } 2102 catch (MessagingException | IOException e) 2103 { 2104 throw new UserManagementException("Error sending a password reset e-mail.", StatusError.MAIL_ERROR, e); 2105 } 2106 } 2107 2108 /** 2109 * Get the URI to reset a password 2110 * @param passwordPage The passwodr page. Can not be null. 2111 * @param login the user login 2112 * @param population the user population 2113 * @param token the generated token 2114 * @param absolute true to get absolute uri 2115 * @return the computed uri 2116 */ 2117 public String getResetPasswordUri(Page passwordPage, String login, String population, String token, boolean absolute) 2118 { 2119 String encodedLogin = URIUtils.encodeParameter(login); 2120 String encodedPopulation = URIUtils.encodeParameter(population); 2121 2122 // Compute the confirmation URI and add it to the parameters. 2123 String confirmUri = ResolveURIComponent.resolve("page", passwordPage.getId(), false, absolute); 2124 return confirmUri + "?login=" + encodedLogin + "&population=" + encodedPopulation + "&token=" + token; 2125 } 2126 2127 /** 2128 * Bean representing a user sign-up request. 2129 */ 2130 public static class TempUser 2131 { 2132 /** The site name. */ 2133 protected String _site; 2134 2135 /** The user e-mail. */ 2136 protected String _email; 2137 2138 /** The user subscription date. */ 2139 protected Date _subscriptionDate; 2140 2141 /** The request token. */ 2142 protected String _token; 2143 2144 /** The id of the population */ 2145 protected String _population; 2146 2147 /** The id of the user directory of the population */ 2148 protected String _userDirectoryId; 2149 2150 /** The user last name */ 2151 protected String _lastname; 2152 2153 /** The user first name */ 2154 protected String _firstname; 2155 2156 /** The user origin */ 2157 protected TempUserOrigin _origin; 2158 2159 /** 2160 * Enumeration for temporary user origin 2161 * 2162 */ 2163 public enum TempUserOrigin 2164 { 2165 /** When the signup request comes from an invitation */ 2166 INVITATION, 2167 /** When the signup request is an user request */ 2168 USER_REQUEST, 2169 /** When the origin of signup request is unknown */ 2170 UNKNWON 2171 } 2172 2173 /** 2174 * Constructor. 2175 * @param site the site 2176 * @param email the user's email 2177 * @param subscriptionDate the date of subscription 2178 * @param token the user's token 2179 * @param population The id of the population 2180 * @param userDirectoryId The id of the user directory of the population 2181 * @param lastname the user's lastname 2182 * @param firstname the user's firstname 2183 * @param origin the origin of this temp user 2184 */ 2185 public TempUser(String site, String email, Date subscriptionDate, String token, String population, String userDirectoryId, String lastname, String firstname, String origin) 2186 { 2187 this._site = site; 2188 this._email = email; 2189 this._subscriptionDate = subscriptionDate; 2190 this._token = token; 2191 this._population = population; 2192 this._userDirectoryId = userDirectoryId; 2193 this._lastname = lastname; 2194 this._firstname = firstname; 2195 this._origin = StringUtils.isNotBlank(origin) ? TempUserOrigin.valueOf(origin) : TempUserOrigin.UNKNWON; 2196 } 2197 /** 2198 * Get the site. 2199 * @return the site 2200 */ 2201 public String getSite() 2202 { 2203 return _site; 2204 } 2205 /** 2206 * Set the site. 2207 * @param site the site to set 2208 */ 2209 public void setSite(String site) 2210 { 2211 this._site = site; 2212 } 2213 /** 2214 * Get the email. 2215 * @return _the email 2216 */ 2217 public String getEmail() 2218 { 2219 return _email; 2220 } 2221 /** 2222 * Set the email. 2223 * @param email the email to set 2224 */ 2225 public void setEmail(String email) 2226 { 2227 this._email = email; 2228 } 2229 /** 2230 * Get the subscriptionDate. 2231 * @return _the subscriptionDate 2232 */ 2233 public Date getSubscriptionDate() 2234 { 2235 return _subscriptionDate; 2236 } 2237 /** 2238 * Set the subscriptionDate. 2239 * @param subscriptionDate the subscriptionDate to set 2240 */ 2241 public void setSubscriptionDate(Date subscriptionDate) 2242 { 2243 this._subscriptionDate = subscriptionDate; 2244 } 2245 /** 2246 * Get the token. 2247 * @return _the token 2248 */ 2249 public String getToken() 2250 { 2251 return _token; 2252 } 2253 /** 2254 * Set the token. 2255 * @param token the token to set 2256 */ 2257 public void setToken(String token) 2258 { 2259 this._token = token; 2260 } 2261 /** 2262 * Get the population. 2263 * @return the population 2264 */ 2265 public String getPopulation() 2266 { 2267 return _population; 2268 } 2269 /** 2270 * Set the population. 2271 * @param population the population to set 2272 */ 2273 public void setPopulation(String population) 2274 { 2275 this._population = population; 2276 } 2277 /** 2278 * Get the user directory id. 2279 * @return the user directory id 2280 */ 2281 public String getUserDirectoryId() 2282 { 2283 return _userDirectoryId; 2284 } 2285 /** 2286 * Set the user directory index. 2287 * @param userDirectoryId the user directory id to set 2288 */ 2289 public void setUserDirectoryIndex(String userDirectoryId) 2290 { 2291 this._userDirectoryId = userDirectoryId; 2292 } 2293 2294 /** 2295 * Get the user firstname 2296 * @return the user firstname 2297 */ 2298 public String getFirstname() 2299 { 2300 return _firstname; 2301 } 2302 2303 /** 2304 * Set the user firstname 2305 * @param firstname the user firstname 2306 */ 2307 public void setFirstname(String firstname) 2308 { 2309 _firstname = firstname; 2310 } 2311 2312 /** 2313 * Get the user firstname 2314 * @return the user firstname 2315 */ 2316 public String getLastname() 2317 { 2318 return _lastname; 2319 } 2320 2321 /** 2322 * Set the user lastname 2323 * @param lastname the user lastname 2324 */ 2325 public void setLastname(String lastname) 2326 { 2327 _lastname = lastname; 2328 } 2329 2330 /** 2331 * Return the temp user origin 2332 * @return true the temp user origin 2333 */ 2334 public TempUserOrigin getOrigin() 2335 { 2336 return _origin; 2337 } 2338 2339 /** 2340 * Set the origin of this temporary user 2341 * @param origin the origin 2342 */ 2343 public void setOrigin(TempUserOrigin origin) 2344 { 2345 _origin = origin; 2346 } 2347 } 2348 2349}