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.UnsupportedEncodingException; 019import java.net.URLEncoder; 020import java.sql.Timestamp; 021import java.time.LocalDate; 022import java.time.ZoneId; 023import java.time.ZonedDateTime; 024import java.util.Date; 025import java.util.HashMap; 026import java.util.Iterator; 027import java.util.List; 028import java.util.Map; 029import java.util.UUID; 030 031import javax.mail.MessagingException; 032 033import org.apache.avalon.framework.configuration.Configuration; 034import org.apache.avalon.framework.configuration.ConfigurationException; 035import org.apache.avalon.framework.service.ServiceException; 036import org.apache.avalon.framework.service.ServiceManager; 037import org.apache.commons.lang.StringUtils; 038import org.apache.ibatis.session.SqlSession; 039 040import org.ametys.cms.transformation.URIResolver; 041import org.ametys.cms.transformation.URIResolverExtensionPoint; 042import org.ametys.core.datasource.AbstractMyBatisDAO; 043import org.ametys.core.user.InvalidModificationException; 044import org.ametys.core.user.User; 045import org.ametys.core.user.UserManager; 046import org.ametys.core.user.directory.ModifiableUserDirectory; 047import org.ametys.core.user.directory.UserDirectory; 048import org.ametys.core.user.population.UserPopulation; 049import org.ametys.core.user.population.UserPopulationDAO; 050import org.ametys.core.util.I18nUtils; 051import org.ametys.core.util.mail.SendMailHelper; 052import org.ametys.plugins.repository.AmetysObjectIterable; 053import org.ametys.plugins.repository.AmetysObjectResolver; 054import org.ametys.plugins.repository.query.expression.Expression; 055import org.ametys.plugins.repository.query.expression.Expression.Operator; 056import org.ametys.runtime.i18n.I18nizableText; 057import org.ametys.runtime.parameter.Errors; 058import org.ametys.web.repository.page.Page; 059import org.ametys.web.repository.page.PageQueryHelper; 060import org.ametys.web.repository.site.Site; 061import org.ametys.web.repository.site.SiteManager; 062import org.ametys.web.site.SiteConfigurationExtensionPoint; 063import org.ametys.web.tags.TagExpression; 064 065/** 066 * Manages registration and password recovery of users. 067 */ 068public class UserSignupManager extends AbstractMyBatisDAO 069{ 070 071 /** The component role. */ 072 public static final String ROLE = UserSignupManager.class.getName(); 073 074 /** Return code which indicates no error. */ 075 public static final int SIGNUP_NO_ERROR = 0; 076 077 /** Temporary signup return code: a user tried to sign-up but the e-mail already exists in the temporary table. */ 078 public static final int SIGNUP_ERROR_TEMP_EMAIL_ALREADY_EXISTS = 1; 079 080 /** Temporary signup return code: a user tried to sign-up but the FO UsersManager already possesses a user with this e-mail as login. */ 081 public static final int SIGNUP_ERROR_USER_ALREADY_EXISTS = 2; 082 083 /** Token return code: a user provided a token, but it doesn't exist. */ 084 public static final int SIGNUP_TOKEN_UNKNOWN = 3; 085 086 /** Token return code: a user provided a token, but it itn't valid anymore. */ 087 public static final int SIGNUP_TOKEN_EXPIRED = 4; 088 089 /** Reset token return code: a user asked for a new token, but no subscription request can be found with this e-mail. */ 090 public static final int SIGNUP_RESET_ERROR_EMAIL_UNKNOWN = 5; 091 092 /** Lost password return code: the login or e-mail the user provided doesn't correspond to a population. */ 093 public static final int LOST_PASSWORD_USER_UNKNOWN = 5; 094 095 /** Lost password return code: the population the user provided doesn't correspond to a user. */ 096 public static final int LOST_PASSWORD_POPULATION_UNKNOWN = 6; 097 098 /** Lost password return code: the user provided belongs to an unmodifiable user directory */ 099 public static final int LOST_PASSWORD_UNMODIFIABLE_USER_DIRECTORY = 7; 100 101 /** Lost password return code: the user provided have an empty email */ 102 public static final int LOST_PASSWORD_EMPTY_EMAIL = 8; 103 104 /** The user manager. */ 105 protected UserManager _userManager; 106 107 /** The DAO for user populations */ 108 protected UserPopulationDAO _userPopulationDAO; 109 110 /** The site manager. */ 111 protected SiteManager _siteManager; 112 113 /** The site configuration extension point. */ 114 protected SiteConfigurationExtensionPoint _siteConf; 115 116 /** The ametys object resolver. */ 117 protected AmetysObjectResolver _resolver; 118 119 /** The ametys object resolver. */ 120 protected URIResolverExtensionPoint _uriResolverEP; 121 122 /** The page URI resolver. */ 123 protected URIResolver _pageUriResolver; 124 125 /** The i18n utils. */ 126 protected I18nUtils _i18nUtils; 127 128 /** The user sign up configuration */ 129 protected UserSignUpConfiguration _userSignUpConfiguration; 130 131 /** The temporary users table. */ 132 protected String _tempUsersTable; 133 134 /** The password change table. */ 135 protected String _pwdChangeTable; 136 137 @Override 138 public void service(ServiceManager serviceManager) throws ServiceException 139 { 140 super.service(serviceManager); 141 142 _userManager = (UserManager) serviceManager.lookup(UserManager.ROLE); 143 _userPopulationDAO = (UserPopulationDAO) serviceManager.lookup(UserPopulationDAO.ROLE); 144 _siteManager = (SiteManager) serviceManager.lookup(SiteManager.ROLE); 145 _siteConf = (SiteConfigurationExtensionPoint) serviceManager.lookup(SiteConfigurationExtensionPoint.ROLE); 146 _resolver = (AmetysObjectResolver) serviceManager.lookup(AmetysObjectResolver.ROLE); 147 _uriResolverEP = (URIResolverExtensionPoint) serviceManager.lookup(URIResolverExtensionPoint.ROLE); 148 _i18nUtils = (I18nUtils) serviceManager.lookup(I18nUtils.ROLE); 149 _userSignUpConfiguration = (UserSignUpConfiguration) serviceManager.lookup(UserSignUpConfiguration.ROLE); 150 } 151 152 @Override 153 public void configure(Configuration configuration) throws ConfigurationException 154 { 155 super.configure(configuration); 156 157 // Configure pool and tables. 158 _tempUsersTable = configuration.getChild("temp-users-table").getValue(); 159 _pwdChangeTable = configuration.getChild("pwd-change-table").getValue(); 160 } 161 162 /** 163 * Test if public signup is allowed in a site. 164 * @param siteName the site to test. 165 * @return true if public signup is allowed, false otherwise. 166 */ 167 public boolean isPublicSignupAllowed(String siteName) 168 { 169 Boolean publicSignup = _siteConf.getValueAsBoolean(siteName, "public-signup"); 170 171 return publicSignup != null && publicSignup; 172 } 173 174 /** 175 * Get the sign-up page in a given site and sitemap.<br> 176 * If more than one page are tagged "sign-up", return the first. 177 * @param siteName the site name. 178 * @param language the sitemap name. 179 * @return the sign-up page or null if not found. 180 */ 181 public Page getSignupPage(String siteName, String language) 182 { 183 Page page = null; 184 185 try (AmetysObjectIterable<Page> pages = getSignupPages(siteName, language);) 186 { 187 Iterator<Page> it = pages.iterator(); 188 if (it.hasNext()) 189 { 190 page = it.next(); 191 } 192 } 193 194 return page; 195 } 196 197 /** 198 * Get all the pages tagged "sign-up". 199 * @param siteName the site name. 200 * @param language the sitemap name. 201 * @return an iterable on all the pages tagged "sign-up". 202 */ 203 public AmetysObjectIterable<Page> getSignupPages(String siteName, String language) 204 { 205 Expression expression = new TagExpression(Operator.EQ, "USER_SIGNUP"); 206 String query = PageQueryHelper.getPageXPathQuery(siteName, language, null, expression, null); 207 208 return _resolver.query(query); 209 } 210 211 /** 212 * Get the password change page in a given site and sitemap. 213 * If more than one page are tagged "password change", return the first. 214 * @param siteName the site name. 215 * @param language the sitemap name. 216 * @return the password change page or null if not found. 217 */ 218 public Page getPwdChangePage(String siteName, String language) 219 { 220 Page page = null; 221 222 try (AmetysObjectIterable<Page> pages = getPwdChangePages(siteName, language);) 223 { 224 Iterator<Page> it = pages.iterator(); 225 if (it.hasNext()) 226 { 227 page = it.next(); 228 } 229 } 230 231 return page; 232 } 233 234 /** 235 * Get all the pages tagged "password change". 236 * @param siteName the site name. 237 * @param language the sitemap name. 238 * @return an iterable on all the pages tagged "password change". 239 */ 240 public AmetysObjectIterable<Page> getPwdChangePages(String siteName, String language) 241 { 242 Expression expression = new TagExpression(Operator.EQ, "USER_PASSWORD_CHANGE"); 243 String query = PageQueryHelper.getPageXPathQuery(siteName, language, null, expression, null); 244 245 return _resolver.query(query); 246 } 247 248 /** 249 * Get the user main preferences page in a given site and sitemap. 250 * If more than one page are tagged "user main preferences", return the first. 251 * @param siteName the site name. 252 * @param language the sitemap name. 253 * @return the user main preferences page or null if not found. 254 */ 255 public Page getUserMainPrefsPage(String siteName, String language) 256 { 257 Page page = null; 258 259 try (AmetysObjectIterable<Page> pages = getUserMainPrefsPages(siteName, language);) 260 { 261 Iterator<Page> it = pages.iterator(); 262 if (it.hasNext()) 263 { 264 page = it.next(); 265 } 266 } 267 268 return page; 269 } 270 271 /** 272 * Get all the pages tagged "user main preferences". 273 * @param siteName the site name. 274 * @param language the sitemap name. 275 * @return an iterable on all the pages tagged "user main preferences". 276 */ 277 public AmetysObjectIterable<Page> getUserMainPrefsPages(String siteName, String language) 278 { 279 Expression expression = new TagExpression(Operator.EQ, "USER_PREFS_MAIN"); 280 String query = PageQueryHelper.getPageXPathQuery(siteName, language, null, expression, null); 281 282 return _resolver.query(query); 283 } 284 285 /** 286 * Tests if the FO users manager knows of the user with the given e-mail as the login. 287 * @param email the e-mail to test. 288 * @param population The id of the population 289 * @return true if the user exists, false otherwise. 290 * @throws UserManagementException if an error occurs. 291 */ 292 public boolean userExists(String email, String population) throws UserManagementException 293 { 294 return _userManager.getUser(population, email) != null; 295 } 296 297 /** 298 * Validate the user subscription data. 299 * @param siteName the site name. 300 * @param email the user e-mail. 301 * @param firstName the user first name. 302 * @param lastName the user last name. 303 * @param additionalValues the additional user values. 304 * @return a Map of the Errors by field. 305 * @throws UserManagementException if an error occurs. 306 */ 307 public Map<String, Errors> validate(String siteName, String email, String firstName, String lastName, Map<String, String> additionalValues) throws UserManagementException 308 { 309 Map<String, String> userInfos = new HashMap<>(); 310 311 userInfos.putAll(additionalValues); 312 313 // Standard info for user (provide a dummy password, as we do not know it yet). 314 userInfos.put("login", email); 315 userInfos.put("email", email); 316 userInfos.put("firstname", firstName); 317 userInfos.put("lastname", lastName); 318 userInfos.put("password", "password"); 319 320 Map<String, Errors> usersManagerErrors = new HashMap<>(); //foUsersManager.validate(userInfos); 321 Map<String, Errors> errors = new HashMap<>(usersManagerErrors); 322 323 // If there are errors on the login, do not return it except if 324 if (errors.containsKey("login")) 325 { 326 if (!errors.containsKey("email")) 327 { 328 errors.put("email", errors.get("login")); 329 } 330 errors.remove("login"); 331 } 332 333 return errors; 334 } 335 336 /** 337 * Validate the user password. 338 * @param siteName the site name. 339 * @param password the password to validate. 340 * @param login the login of the user 341 * @param population The id of the population 342 * @return a Map of the Errors by field. 343 * @throws UserManagementException if an error occurs. 344 */ 345 public Map<String, Errors> validatePassword(String siteName, String password, String login, String population) throws UserManagementException 346 { 347 Map<String, String> userInfos = new HashMap<>(); 348 349 userInfos.put("password", password); 350 351 UserDirectory userDirectory = _userManager.getUserDirectory(population, login); 352 if (!(userDirectory instanceof ModifiableUserDirectory)) 353 { 354 throw new UserManagementException("The user subscription feature can't be used, as the UserDirectory is not modifiable."); 355 } 356 Map<String, Errors> usersManagerErrors = ((ModifiableUserDirectory) userDirectory).validate(userInfos); 357 Map<String, Errors> errors = new HashMap<>(); 358 359 // Keep only errors related to the password field. 360 if (usersManagerErrors.containsKey("password")) 361 { 362 errors.put("password", usersManagerErrors.get("password")); 363 } 364 365 return errors; 366 } 367 368 /** 369 * Validate and store a sign-up request and send a confirmation e-mail. 370 * @param siteName the site name. 371 * @param language the sitemap name. 372 * @param email the user e-mail address. 373 * @param firstName the user first name. 374 * @param lastName the user last name. 375 * @param population the population 376 * @param userDirectoryId the id of the user directory of the population 377 * @return a status code indicating success or error. 378 * @throws UserManagementException if an error occurs. 379 */ 380 public int temporarySignup(String siteName, String language, String email, String firstName, String lastName, String population, String userDirectoryId) throws UserManagementException 381 { 382 // Verify that public sign-up is allowed and throw an exception otherwise. 383 checkPublicSignup(siteName); 384 385 if (getLogger().isDebugEnabled()) 386 { 387 getLogger().debug("A user is requesting a sign-up: " + firstName + " " + lastName + " (" + email + ")."); 388 } 389 390 // Generate unique token. 391 String token = UUID.randomUUID().toString().replace("-", ""); 392 393 int status = addTemporaryUser(siteName, email, firstName, lastName, token, population, userDirectoryId); 394 395 // Send validation e-mail with token. 396 if (status == SIGNUP_NO_ERROR) 397 { 398 sendSignupConfirmMail(siteName, language, email, firstName, lastName, token); 399 } 400 401 return status; 402 } 403 404 /** 405 * Reset a sign-up request: generate a new token and re-send the confirmation e-mail. 406 * @param siteName the site name. 407 * @param language the sitemap name. 408 * @param email the user e-mail address. 409 * @param population The id of the population 410 * @param userDirectoryId The id of the user directory of the population 411 * @return a status code indicating success or error. 412 * @throws UserManagementException if an error occurs. 413 */ 414 public int resetTempSignup(String siteName, String language, String email, String population, String userDirectoryId) throws UserManagementException 415 { 416 // Verify that public sign-up is allowed and throw an exception otherwise. 417 checkPublicSignup(siteName); 418 419 if (getLogger().isDebugEnabled()) 420 { 421 getLogger().debug("Resetting temporary signup for email: " + email + " in site " + siteName); 422 } 423 424 // Generate a new token. 425 String newToken = UUID.randomUUID().toString().replace("-", ""); 426 427 // Test if the subscription request really exists. 428 TempUser tempUser = getTempUser(siteName, email, population, userDirectoryId); 429 430 if (tempUser == null) 431 { 432 // No subscription request with this e-mail. 433 return SIGNUP_RESET_ERROR_EMAIL_UNKNOWN; 434 } 435 436 updateTempToken(siteName, email, newToken, population, userDirectoryId); 437 438 sendSignupConfirmMail(siteName, language, email, tempUser.getFirstname(), tempUser.getLastname(), newToken); 439 440 return SIGNUP_NO_ERROR; 441 } 442 443 444 /** 445 * Create the user in the FO UsersManager from his temporary request. 446 * @param siteName the site name. 447 * @param email the user e-mail address. 448 * @param token the request token. 449 * @param password the user password. 450 * @param population The id of the population 451 * @param userDirectoryId The id of the user directory of the population 452 * @return a status code indicating success or error. 453 * @throws UserManagementException if an error occurs. 454 */ 455 public int signup(String siteName, String email, String token, String password, String population, String userDirectoryId) throws UserManagementException 456 { 457 // Verify that public sign-up is allowed and throw an exception otherwise. 458 checkPublicSignup(siteName); 459 460 Map<String, String> userInfos = new HashMap<>(); 461 462 TempUser tempUser = getTempUser(siteName, email, token, population, userDirectoryId); 463 464 if (tempUser == null) 465 { 466 return SIGNUP_TOKEN_UNKNOWN; 467 } 468 469 // Standard info for user. 470 userInfos.put("login", email); 471 userInfos.put("email", email); 472 userInfos.put("firstname", tempUser.getFirstname()); 473 userInfos.put("lastname", tempUser.getLastname()); 474 userInfos.put("password", password); 475 476 try 477 { 478 // Add the user. 479 ModifiableUserDirectory userDirectory = (ModifiableUserDirectory) _userPopulationDAO.getUserPopulation(population).getUserDirectory(userDirectoryId); 480 userDirectory.add(userInfos); 481 482 // Remove the temporary user. 483 removeTempUser(siteName, email, token, population, userDirectoryId); 484 485 return SIGNUP_NO_ERROR; 486 } 487 catch (InvalidModificationException e) 488 { 489 throw new UserManagementException("An error occurred signing up the user.", e); 490 } 491 } 492 493 /** 494 * Create a reset password request and send a confirmation e-mail. 495 * @param siteName the site name. 496 * @param language the sitemap name. 497 * @param login the user login. 498 * @param populationId the population 499 * @return a status code indicating success or error. 500 * @throws UserManagementException if an error occurs. 501 */ 502 public int resetPassword(String siteName, String language, String login, String populationId) throws UserManagementException 503 { 504 // Check if the population exists 505 UserPopulation population = _userPopulationDAO.getUserPopulation(populationId); 506 if (population == null) 507 { 508 return LOST_PASSWORD_POPULATION_UNKNOWN; 509 } 510 511 // Check if the user exists and get it 512 User user = _userManager.getUser(populationId, login); 513 if (user == null) 514 { 515 // No user with this e-mail as login. 516 return LOST_PASSWORD_USER_UNKNOWN; 517 } 518 519 // Check if the user directory is modifiable 520 if (!(user.getUserDirectory() instanceof ModifiableUserDirectory)) 521 { 522 return LOST_PASSWORD_UNMODIFIABLE_USER_DIRECTORY; 523 } 524 525 if (StringUtils.isEmpty(user.getEmail())) 526 { 527 return LOST_PASSWORD_EMPTY_EMAIL; 528 } 529 530 // Generate a new token. 531 String token = UUID.randomUUID().toString().replace("-", ""); 532 533 // Insert the token in the database. 534 addPasswordToken(siteName, login, token, populationId); 535 536 // Send the e-mail. 537 sendResetPasswordMail(siteName, language, user, token); 538 539 return SIGNUP_NO_ERROR; 540 } 541 542 /** 543 * Change the user password. 544 * @param siteName the site name. 545 * @param login the user login. 546 * @param token the password change request token. 547 * @param newPassword the new password. 548 * @param population the population 549 * @return a status code indicating success or error. 550 * @throws UserManagementException if an error occurs. 551 */ 552 public int changeUserPassword(String siteName, String login, String token, String newPassword, String population) throws UserManagementException 553 { 554 int tokenStatus = checkPasswordToken(siteName, login, token, population); 555 556 if (tokenStatus != SIGNUP_NO_ERROR) 557 { 558 return tokenStatus; 559 } 560 561 Map<String, String> userInfos = new HashMap<>(); 562 563 userInfos.put("login", login); 564 userInfos.put("password", newPassword); 565 566 try 567 { 568 UserDirectory userDirectory = _userManager.getUserDirectory(population, login); 569 if (!(userDirectory instanceof ModifiableUserDirectory)) 570 { 571 throw new UserManagementException("The user's password can't be changed, as the UserDirectory is not modifiable."); 572 } 573 ((ModifiableUserDirectory) userDirectory).update(userInfos); 574 575 removePasswordToken(siteName, login, token, population); 576 577 return SIGNUP_NO_ERROR; 578 } 579 catch (InvalidModificationException e) 580 { 581 throw new UserManagementException("An error occurred signing up the user.", e); 582 } 583 } 584 585 /** 586 * Check the sign-up request token. 587 * @param siteName the site name. 588 * @param email the user e-mail. 589 * @param token the sign-up request token. 590 * @param population The id of the population 591 * @param userDirectoryId The id of the user directory of the population 592 * @return a status code indicating success or error. 593 * @throws UserManagementException if an error occurs. 594 */ 595 public int checkToken(String siteName, String email, String token, String population, String userDirectoryId) throws UserManagementException 596 { 597 removeExpiredTokens(); 598 599 try (SqlSession sqlSession = getSession()) 600 { 601 String stmtId = "UserSignupManager.getSubscriptionDate"; 602 603 Map<String, Object> params = new HashMap<>(); 604 params.put("tempUsersTable", _tempUsersTable); 605 606 params.put("site", siteName); 607 params.put("email", email); 608 params.put("token", token); 609 params.put("population", population); 610 params.put("userDirectory", userDirectoryId); 611 612 Date rawSubscriptionDate = sqlSession.selectOne(stmtId, params); 613 614 // Date verification. 615 if (rawSubscriptionDate == null) 616 { 617 return SIGNUP_TOKEN_UNKNOWN; 618 } 619 620 // The validity limit 621 ZonedDateTime limit = LocalDate.now().atStartOfDay(ZoneId.systemDefault()).minusDays(_userSignUpConfiguration.getTokenValidity()); 622 ZonedDateTime subscriptionDate = rawSubscriptionDate.toInstant().atZone(ZoneId.systemDefault()); 623 624 if (subscriptionDate.isBefore(limit)) 625 { 626 return SIGNUP_TOKEN_EXPIRED; 627 } 628 629 return SIGNUP_NO_ERROR; 630 } 631 catch (Exception e) 632 { 633 throw new UserManagementException("Database error while testing the token for user [" + email + "]", e); 634 } 635 } 636 637 /** 638 * Check the password change request token. 639 * @param siteName the site name. 640 * @param login the user login. 641 * @param token the password change request token. 642 * @param population the population 643 * @return a status code indicating success or error. 644 * @throws UserManagementException if an error occurs. 645 */ 646 public int checkPasswordToken(String siteName, String login, String token, String population) throws UserManagementException 647 { 648 removeExpiredPasswordTokens(); 649 650 try (SqlSession sqlSession = getSession()) 651 { 652 String stmtId = "UserSignupManager.getRequestDate"; 653 654 Map<String, Object> params = new HashMap<>(); 655 params.put("pwdChangeTable", _pwdChangeTable); 656 657 params.put("site", siteName); 658 params.put("login", login); 659 params.put("token", token); 660 params.put("population", population); 661 662 Date rawRequestDate = sqlSession.selectOne(stmtId, params); 663 664 // Date verification. 665 if (rawRequestDate == null) 666 { 667 return SIGNUP_TOKEN_UNKNOWN; 668 } 669 670 // Check the validity. 671 ZonedDateTime limit = LocalDate.now().atStartOfDay(ZoneId.systemDefault()).minusDays(_userSignUpConfiguration.getTokenValidity()); 672 ZonedDateTime requestDate = rawRequestDate.toInstant().atZone(ZoneId.systemDefault()); 673 674 if (requestDate.isBefore(limit)) 675 { 676 return SIGNUP_TOKEN_EXPIRED; 677 } 678 679 return SIGNUP_NO_ERROR; 680 } 681 catch (Exception e) 682 { 683 throw new UserManagementException("Database error while testing the password token for user " + login, e); 684 } 685 } 686 687 /** 688 * Remove the expired sign-up request tokens. 689 * @throws UserManagementException if an error occurs. 690 */ 691 public void removeExpiredTokens() throws UserManagementException 692 { 693 try (SqlSession sqlSession = getSession()) 694 { 695 String stmtId = "UserSignupManager.deleteExpiredTokens"; 696 697 Map<String, Object> params = new HashMap<>(); 698 params.put("tempUsersTable", _tempUsersTable); 699 700 ZonedDateTime limit = LocalDate.now().atStartOfDay(ZoneId.systemDefault()).minusDays(_userSignUpConfiguration.getTokenValidity()); 701 Timestamp limitTimestamp = Timestamp.from(limit.toInstant()); 702 params.put("threshold", limitTimestamp); 703 704 sqlSession.delete(stmtId, params); 705 sqlSession.commit(); 706 } 707 catch (Exception e) 708 { 709 throw new UserManagementException("Database error while removing the expired tokens.", e); 710 } 711 } 712 713 /** 714 * Remove the expired change password request tokens. 715 * @throws UserManagementException if an error occurs. 716 */ 717 public void removeExpiredPasswordTokens() throws UserManagementException 718 { 719 try (SqlSession sqlSession = getSession()) 720 { 721 String stmtId = "UserSignupManager.deleteExpiredPasswordTokens"; 722 723 Map<String, Object> params = new HashMap<>(); 724 params.put("pwdChangeTable", _pwdChangeTable); 725 726 ZonedDateTime limit = LocalDate.now().atStartOfDay(ZoneId.systemDefault()).minusDays(_userSignUpConfiguration.getTokenValidity()); 727 Timestamp limitTimestamp = Timestamp.from(limit.toInstant()); 728 params.put("threshold", limitTimestamp); 729 730 sqlSession.delete(stmtId, params); 731 sqlSession.commit(); 732 } 733 catch (Exception e) 734 { 735 throw new UserManagementException("Database error while removing the expired tokens.", e); 736 } 737 } 738 739 /** 740 * Verify that public sign-up is allowed. If not, throw an exception. 741 * @param siteName the site name. 742 * @throws UserManagementException if public sign-up is not enabled. 743 */ 744 protected void checkPublicSignup(String siteName) throws UserManagementException 745 { 746 if (!isPublicSignupAllowed(siteName)) 747 { 748 throw new UserManagementException("Public signup is disabled for this site."); 749 } 750 } 751 752 /** 753 * Create a user sign-up request ("temporary" user) in the database. 754 * @param siteName the site name. 755 * @param email the user e-mail. 756 * @param firstName the user first name. 757 * @param lastName the user last name. 758 * @param token the generated token. 759 * @param population the population 760 * @param userDirectoryId the id of the user directory of the population 761 * @return a status code indicating success or error. 762 * @throws UserManagementException if an error occurs. 763 */ 764 protected int addTemporaryUser(String siteName, String email, String firstName, String lastName, String token, String population, String userDirectoryId) throws UserManagementException 765 { 766 try (SqlSession sqlSession = getSession()) 767 { 768 // Does this email already exists 769 String stmtId = "UserSignupManager.tempEmailExists"; 770 771 Map<String, Object> params = new HashMap<>(); 772 params.put("tempUsersTable", _tempUsersTable); 773 774 params.put("site", siteName); 775 params.put("email", email); 776 params.put("population", population); 777 params.put("userDirectory", userDirectoryId); 778 779 List<String> emails = sqlSession.selectList(stmtId, params); 780 781 if (!emails.isEmpty()) 782 { 783 return SIGNUP_ERROR_TEMP_EMAIL_ALREADY_EXISTS; 784 } 785 786 // Add temp user 787 stmtId = "UserSignupManager.addTempUser"; 788 params = new HashMap<>(); 789 params.put("tempUsersTable", _tempUsersTable); 790 791 Timestamp now = new Timestamp(System.currentTimeMillis()); 792 793 params.put("site", siteName); 794 params.put("email", email); 795 params.put("population", population); 796 params.put("userDirectory", userDirectoryId); 797 params.put("firstname", firstName); 798 params.put("lastname", lastName); 799 params.put("subscription_date", now); 800 params.put("token", token); 801 802 sqlSession.insert(stmtId, params); 803 sqlSession.commit(); 804 805 return SIGNUP_NO_ERROR; 806 } 807 catch (Exception e) 808 { 809 throw new UserManagementException("Database error while signing up a new user [" + email + "]", e); 810 } 811 } 812 813 /** 814 * Send a sign-up confirmation link by e-mail. 815 * @param siteName the site name. 816 * @param language the e-mail language. 817 * @param email the user e-mail. 818 * @param firstName the user first name. 819 * @param lastName the user last name. 820 * @param token the generated token. 821 * @throws UserManagementException if an error occurs. 822 */ 823 protected void sendSignupConfirmMail(String siteName, String language, String email, String firstName, String lastName, String token) throws UserManagementException 824 { 825 if (getLogger().isDebugEnabled()) 826 { 827 getLogger().debug("Sending signup confirmation e-mail to " + email); 828 } 829 830 String from = _siteConf.getValueAsString(siteName, "site-mail-from"); 831 832 // Prepare mail. 833 Map<String, I18nizableText> i18nParams = new HashMap<>(); 834 i18nParams.put("siteName", new I18nizableText(siteName)); 835 i18nParams.put("email", new I18nizableText(email)); 836 i18nParams.put("firstName", new I18nizableText(firstName)); 837 i18nParams.put("lastName", new I18nizableText(lastName)); 838 i18nParams.put("token", new I18nizableText(token)); 839 840 Page signupPage = getSignupPage(siteName, language); 841 if (signupPage != null) 842 { 843 if (_pageUriResolver == null) 844 { 845 _pageUriResolver = _uriResolverEP.getResolverForType("page"); 846 } 847 848 String encodedEmail; 849 try 850 { 851 encodedEmail = URLEncoder.encode(email, "UTF-8"); 852 } 853 catch (UnsupportedEncodingException e) 854 { 855 // Should never happen. 856 throw new UserManagementException("Encoding error while sending a sign-up confirmation e-mail.", e); 857 } 858 859 // Compute the confirmation URI and add it to the parameters. 860 String confirmUri = _pageUriResolver.resolve(signupPage.getId(), false, true, false); 861 confirmUri = confirmUri + "?email=" + encodedEmail + "&token=" + token; 862 863 i18nParams.put("confirmUri", new I18nizableText(confirmUri)); 864 } 865 866 // Add site information in the parameters. 867 Site site = _siteManager.getSite(siteName); 868 if (site != null) 869 { 870 i18nParams.put("siteTitle", new I18nizableText(site.getTitle())); 871 i18nParams.put("siteUrl", new I18nizableText(site.getUrl())); 872 } 873 874 String subject = _userSignUpConfiguration.getSubjectForSignUpEmail(i18nParams, language); 875 String textBody = _userSignUpConfiguration.getTextBodyForSignUpEmail(i18nParams, language); 876 String htmlBody = _userSignUpConfiguration.getHtmlBodyForSignUpEmail(i18nParams, language); 877 878 try 879 { 880 // Send the e-mail. 881 SendMailHelper.sendMail(subject, htmlBody, textBody, email, from); 882 } 883 catch (MessagingException e) 884 { 885 throw new UserManagementException("Error sending the sign-up confirmation mail.", e); 886 } 887 } 888 889 /** 890 * Update the sign-up request token: reset the date and set a new token. 891 * @param siteName the site name. 892 * @param email the user e-mail. 893 * @param newToken the new token. 894 * @param population The id of the population 895 * @param userDirectoryId The id of the user directory of the population 896 * @throws UserManagementException if an error occurs. 897 */ 898 protected void updateTempToken(String siteName, String email, String newToken, String population, String userDirectoryId) throws UserManagementException 899 { 900 try (SqlSession sqlSession = getSession()) 901 { 902 String stmtId = "UserSignupManager.updateTempToken"; 903 904 Map<String, Object> params = new HashMap<>(); 905 params.put("tempUsersTable", _tempUsersTable); 906 907 Timestamp now = new Timestamp(System.currentTimeMillis()); 908 params.put("subscription_date", now); 909 params.put("token", newToken); 910 params.put("site", siteName); 911 params.put("email", email); 912 params.put("population", population); 913 params.put("userDirectory", userDirectoryId); 914 915 sqlSession.update(stmtId, params); 916 sqlSession.commit(); 917 } 918 catch (Exception e) 919 { 920 throw new UserManagementException("Database error while resetting the subscription for user [" + email + "]", e); 921 } 922 } 923 924 /** 925 * Create a user password change request in the database. 926 * @param siteName the site name. 927 * @param login the user login. 928 * @param token the generated token. 929 * @param population the population 930 * @throws UserManagementException if an error occurs. 931 */ 932 protected void addPasswordToken(String siteName, String login, String token, String population) throws UserManagementException 933 { 934 try (SqlSession sqlSession = getSession()) 935 { 936 String stmtId = "UserSignupManager.hasToken"; 937 938 Map<String, Object> params = new HashMap<>(); 939 params.put("pwdChangeTable", _pwdChangeTable); 940 941 params.put("site", siteName); 942 params.put("login", login); 943 params.put("population", population); 944 945 List<Object> pwdEntries = sqlSession.selectList(stmtId, params); 946 947 if (pwdEntries.isEmpty()) 948 { 949 // Insert a new token. 950 stmtId = "UserSignupManager.addPasswordToken"; 951 params = new HashMap<>(); 952 params.put("pwdChangeTable", _pwdChangeTable); 953 954 Timestamp now = new Timestamp(System.currentTimeMillis()); 955 params.put("site", siteName); 956 params.put("login", login); 957 params.put("request_date", now); 958 params.put("token", token); 959 params.put("population", population); 960 961 sqlSession.insert(stmtId, params); 962 } 963 else 964 { 965 // Update the existing token. 966 stmtId = "UserSignupManager.updatePasswordToken"; 967 params = new HashMap<>(); 968 params.put("pwdChangeTable", _pwdChangeTable); 969 970 Timestamp now = new Timestamp(System.currentTimeMillis()); 971 params.put("request_date", now); 972 params.put("token", token); 973 params.put("site", siteName); 974 params.put("login", login); 975 params.put("population", population); 976 977 sqlSession.update(stmtId, params); 978 } 979 980 // commit insert or update 981 sqlSession.commit(); 982 } 983 catch (Exception e) 984 { 985 throw new UserManagementException("Database error while inserting a password change token for " + login, e); 986 } 987 } 988 989 /** 990 * Get a temporary user from his site name and e-mail. 991 * @param siteName the site name. 992 * @param email The temporary user e-mail. Cannot be null. 993 * @param population the population 994 * @param userDirectoryId the id of the user directory of the population 995 * @return the temporary user or null if not found. 996 * @throws UserManagementException if an error occurs. 997 */ 998 protected TempUser getTempUser(String siteName, String email, String population, String userDirectoryId) throws UserManagementException 999 { 1000 return getTempUser(siteName, email, null, population, userDirectoryId); 1001 } 1002 1003 /** 1004 * Get a temporary user from his site name, e-mail and/or token. 1005 * At least one of e-mail and token must be provided. 1006 * @param siteName the site name. 1007 * @param email The temporary user e-mail. Can be null. 1008 * @param token The temporary user token. Can be null. 1009 * @param population the population. Must be not null if email is not null 1010 * @param userDirectoryId the id of the user directory of the population. Must be not null if email is not null 1011 * @return the temporary user or null if not found. 1012 * @throws UserManagementException if an error occurs. 1013 */ 1014 protected TempUser getTempUser(String siteName, String email, String token, String population, String userDirectoryId) throws UserManagementException 1015 { 1016 if (StringUtils.isEmpty(email) && StringUtils.isEmpty(token)) 1017 { 1018 throw new UserManagementException("Either e-mail or token must be provided."); 1019 } 1020 1021 try (SqlSession sqlSession = getSession()) 1022 { 1023 // Does this email already exists 1024 String stmtId = "UserSignupManager.getTempUser"; 1025 1026 Map<String, Object> params = new HashMap<>(); 1027 params.put("tempUsersTable", _tempUsersTable); 1028 1029 params.put("site", siteName); 1030 if (StringUtils.isNotEmpty(email)) 1031 { 1032 params.put("email", email); 1033 params.put("population", population); 1034 params.put("userDirectory", userDirectoryId); 1035 } 1036 if (StringUtils.isNotEmpty(token)) 1037 { 1038 params.put("token", token); 1039 } 1040 1041 return sqlSession.selectOne(stmtId, params); 1042 } 1043 catch (Exception e) 1044 { 1045 throw new UserManagementException("Database error while getting the request for user [" + email + "] and token [" + token + "]", e); 1046 } 1047 } 1048 1049 /** 1050 * Remove the temporary . 1051 * @param siteName the site name. 1052 * @param email the user e-mail address. 1053 * @param token the request token. 1054 * @param population the population 1055 * @param userDirectoryId the id of the user directory of the population 1056 * @throws UserManagementException if an error occurs. 1057 */ 1058 protected void removeTempUser(String siteName, String email, String token, String population, String userDirectoryId) throws UserManagementException 1059 { 1060 try (SqlSession sqlSession = getSession()) 1061 { 1062 String stmtId = "UserSignupManager.removeTempUser"; 1063 1064 Map<String, Object> params = new HashMap<>(); 1065 params.put("tempUsersTable", _tempUsersTable); 1066 1067 params.put("site", siteName); 1068 params.put("email", email); 1069 params.put("token", token); 1070 params.put("population", population); 1071 params.put("userDirectory", userDirectoryId); 1072 1073 sqlSession.delete(stmtId, params); 1074 sqlSession.commit(); 1075 } 1076 catch (Exception e) 1077 { 1078 throw new UserManagementException("Database error while removing the token of user " + email, e); 1079 } 1080 } 1081 1082 /** 1083 * Remove the password change request. 1084 * @param siteName the site name. 1085 * @param login the user login. 1086 * @param token the request token. 1087 * @param population the population 1088 * @throws UserManagementException if an error occurs. 1089 */ 1090 protected void removePasswordToken(String siteName, String login, String token, String population) throws UserManagementException 1091 { 1092 try (SqlSession sqlSession = getSession()) 1093 { 1094 String stmtId = "UserSignupManager.removePasswordToken"; 1095 1096 Map<String, Object> params = new HashMap<>(); 1097 params.put("pwdChangeTable", _pwdChangeTable); 1098 1099 params.put("site", siteName); 1100 params.put("login", login); 1101 params.put("token", token); 1102 params.put("population", population); 1103 1104 sqlSession.delete(stmtId, params); 1105 sqlSession.commit(); 1106 } 1107 catch (Exception e) 1108 { 1109 throw new UserManagementException("Database error while removing the token of user " + login, e); 1110 } 1111 } 1112 1113 /** 1114 * Send a sign-up confirmation link by e-mail. 1115 * @param siteName the site name. 1116 * @param language the e-mail language. 1117 * @param user the user object. 1118 * @param token the generated token. 1119 * @throws UserManagementException if an error occurs. 1120 */ 1121 protected void sendResetPasswordMail(String siteName, String language, User user, String token) throws UserManagementException 1122 { 1123 String login = user.getIdentity().getLogin(); 1124 String population = user.getIdentity().getPopulationId(); 1125 1126 if (getLogger().isDebugEnabled()) 1127 { 1128 getLogger().debug("Sending reset password e-mail to " + login); 1129 } 1130 1131 String from = _siteConf.getValueAsString(siteName, "site-mail-from"); 1132 1133 // Prepare mail 1134 Map<String, I18nizableText> i18nParams = new HashMap<>(); 1135 i18nParams.put("siteName", new I18nizableText(siteName)); 1136 i18nParams.put("login", new I18nizableText(login)); 1137 i18nParams.put("email", new I18nizableText(user.getEmail())); 1138 i18nParams.put("fullName", new I18nizableText(user.getFullName())); 1139 i18nParams.put("token", new I18nizableText(token)); 1140 1141 Page passwordPage = getPwdChangePage(siteName, language); 1142 if (passwordPage != null) 1143 { 1144 if (_pageUriResolver == null) 1145 { 1146 _pageUriResolver = _uriResolverEP.getResolverForType("page"); 1147 } 1148 1149 String encodedLogin; 1150 String encodedPopulation; 1151 try 1152 { 1153 encodedLogin = URLEncoder.encode(login, "UTF-8"); 1154 encodedPopulation = URLEncoder.encode(population, "UTF-8"); 1155 } 1156 catch (UnsupportedEncodingException e) 1157 { 1158 // Should never happen. 1159 throw new UserManagementException("Encoding error while sending a password reset confirmation e-mail.", e); 1160 } 1161 1162 // Compute the confirmation URI and add it to the parameters. 1163 String confirmUri = _pageUriResolver.resolve(passwordPage.getId(), false, true, false); 1164 confirmUri = confirmUri + "?login=" + encodedLogin + "&population=" + encodedPopulation + "&token=" + token; 1165 1166 i18nParams.put("confirmUri", new I18nizableText(confirmUri)); 1167 } 1168 1169 // Add site information in the parameters. 1170 Site site = _siteManager.getSite(siteName); 1171 if (site != null) 1172 { 1173 i18nParams.put("siteTitle", new I18nizableText(site.getTitle())); 1174 i18nParams.put("siteUrl", new I18nizableText(site.getUrl())); 1175 } 1176 1177 String subject = _userSignUpConfiguration.getSubjectForResetPwdEmail(i18nParams, language); 1178 String textBody = _userSignUpConfiguration.getTextBodyForResetPwdEmail(i18nParams, language); 1179 String htmlBody = _userSignUpConfiguration.getHtmlBodyForResetPwdEmail(i18nParams, language); 1180 1181 try 1182 { 1183 // Send the e-mail. 1184 SendMailHelper.sendMail(subject, htmlBody, textBody, user.getEmail(), from); 1185 } 1186 catch (MessagingException e) 1187 { 1188 throw new UserManagementException("Error sending a password reset e-mail.", e); 1189 } 1190 } 1191 1192 /** 1193 * Bean representing a user sign-up request. 1194 */ 1195 public static class TempUser 1196 { 1197 /** The site name. */ 1198 protected String _site; 1199 1200 /** The user e-mail. */ 1201 protected String _email; 1202 1203 /** The user first name. */ 1204 protected String _firstname; 1205 1206 /** The user last name. */ 1207 protected String _lastname; 1208 1209 /** The user subscription date. */ 1210 protected Date _subscriptionDate; 1211 1212 /** The request token. */ 1213 protected String _token; 1214 1215 /** The id of the population */ 1216 protected String _population; 1217 1218 /** The id of the user directory of the population */ 1219 protected String _userDirectoryId; 1220 1221 /** 1222 * Constructor. 1223 * @param site the site 1224 * @param email the user's email 1225 * @param firstname the user's firstname 1226 * @param lastname the user's lastname 1227 * @param subscriptionDate the date of subscription 1228 * @param token the user's token 1229 * @param population The id of the population 1230 * @param userDirectoryId The id of the user directory of the population 1231 */ 1232 public TempUser(String site, String email, String firstname, String lastname, Date subscriptionDate, String token, String population, String userDirectoryId) 1233 { 1234 this._site = site; 1235 this._email = email; 1236 this._firstname = firstname; 1237 this._lastname = lastname; 1238 this._subscriptionDate = subscriptionDate; 1239 this._token = token; 1240 this._population = population; 1241 this._userDirectoryId = userDirectoryId; 1242 } 1243 /** 1244 * Get the site. 1245 * @return the site 1246 */ 1247 public String getSite() 1248 { 1249 return _site; 1250 } 1251 /** 1252 * Set the site. 1253 * @param site the site to set 1254 */ 1255 public void setSite(String site) 1256 { 1257 this._site = site; 1258 } 1259 /** 1260 * Get the email. 1261 * @return _the email 1262 */ 1263 public String getEmail() 1264 { 1265 return _email; 1266 } 1267 /** 1268 * Set the email. 1269 * @param email the email to set 1270 */ 1271 public void setEmail(String email) 1272 { 1273 this._email = email; 1274 } 1275 /** 1276 * Get the firstname. 1277 * @return _the firstname 1278 */ 1279 public String getFirstname() 1280 { 1281 return _firstname; 1282 } 1283 /** 1284 * Set the firstname. 1285 * @param firstname the firstname to set 1286 */ 1287 public void setFirstname(String firstname) 1288 { 1289 this._firstname = firstname; 1290 } 1291 /** 1292 * Get the lastname. 1293 * @return _the lastname 1294 */ 1295 public String getLastname() 1296 { 1297 return _lastname; 1298 } 1299 /** 1300 * Set the lastname. 1301 * @param lastname the lastname to set 1302 */ 1303 public void setLastname(String lastname) 1304 { 1305 this._lastname = lastname; 1306 } 1307 /** 1308 * Get the subscriptionDate. 1309 * @return _the subscriptionDate 1310 */ 1311 public Date getSubscriptionDate() 1312 { 1313 return _subscriptionDate; 1314 } 1315 /** 1316 * Set the subscriptionDate. 1317 * @param subscriptionDate the subscriptionDate to set 1318 */ 1319 public void setSubscriptionDate(Date subscriptionDate) 1320 { 1321 this._subscriptionDate = subscriptionDate; 1322 } 1323 /** 1324 * Get the token. 1325 * @return _the token 1326 */ 1327 public String getToken() 1328 { 1329 return _token; 1330 } 1331 /** 1332 * Set the token. 1333 * @param token the token to set 1334 */ 1335 public void setToken(String token) 1336 { 1337 this._token = token; 1338 } 1339 /** 1340 * Get the population. 1341 * @return the population 1342 */ 1343 public String getPopulation() 1344 { 1345 return _population; 1346 } 1347 /** 1348 * Set the population. 1349 * @param population the population to set 1350 */ 1351 public void setPopulation(String population) 1352 { 1353 this._population = population; 1354 } 1355 /** 1356 * Get the user directory id. 1357 * @return the user directory id 1358 */ 1359 public String getUserDirectoryId() 1360 { 1361 return _userDirectoryId; 1362 } 1363 /** 1364 * Set the user directory index. 1365 * @param userDirectoryId the user directory id to set 1366 */ 1367 public void setUserDirectoryIndex(String userDirectoryId) 1368 { 1369 this._userDirectoryId = userDirectoryId; 1370 } 1371 1372 } 1373 1374}