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