001/* 002 * (C) Copyright 2011-2014 Nuxeo SA (http://nuxeo.com/) and contributors. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the GNU Lesser General Public License 006 * (LGPL) version 2.1 which accompanies this distribution, and is available at 007 * http://www.gnu.org/licenses/lgpl-2.1.html 008 * 009 * This library is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 012 * Lesser General Public License for more details. 013 * Contributors: 014 * Nuxeo - initial API and implementation 015 */ 016 017package org.nuxeo.ecm.user.invite; 018 019import static org.apache.commons.lang.StringUtils.isBlank; 020import static org.nuxeo.ecm.user.invite.RegistrationRules.FACET_REGISTRATION_CONFIGURATION; 021import static org.nuxeo.ecm.user.invite.RegistrationRules.FIELD_CONFIGURATION_NAME; 022import static org.nuxeo.ecm.user.invite.UserInvitationService.ValidationMethod.EMAIL; 023import static org.nuxeo.ecm.user.invite.UserRegistrationConfiguration.DEFAULT_CONFIGURATION_NAME; 024 025import java.io.IOException; 026import java.io.Serializable; 027import java.io.StringReader; 028import java.io.StringWriter; 029import java.io.Writer; 030import java.util.Date; 031import java.util.HashMap; 032import java.util.List; 033import java.util.Map; 034import java.util.Set; 035 036import javax.mail.Message; 037import javax.mail.MessagingException; 038import javax.mail.Session; 039import javax.mail.Transport; 040import javax.mail.internet.InternetAddress; 041import javax.mail.internet.MimeMessage; 042import javax.naming.InitialContext; 043import javax.naming.NamingException; 044 045import freemarker.template.Configuration; 046import freemarker.template.Template; 047import freemarker.template.TemplateException; 048import org.apache.commons.lang.StringUtils; 049import org.apache.commons.logging.Log; 050import org.apache.commons.logging.LogFactory; 051import org.nuxeo.common.utils.IdUtils; 052import org.nuxeo.ecm.core.api.CoreSession; 053import org.nuxeo.ecm.core.api.DocumentModel; 054import org.nuxeo.ecm.core.api.DocumentModelList; 055import org.nuxeo.ecm.core.api.DocumentRef; 056import org.nuxeo.ecm.core.api.IdRef; 057import org.nuxeo.ecm.core.api.NuxeoException; 058import org.nuxeo.ecm.core.api.NuxeoPrincipal; 059import org.nuxeo.ecm.core.api.PathRef; 060import org.nuxeo.ecm.core.api.PropertyException; 061import org.nuxeo.ecm.core.api.UnrestrictedSessionRunner; 062import org.nuxeo.ecm.core.api.impl.DocumentModelImpl; 063import org.nuxeo.ecm.core.api.impl.DocumentModelListImpl; 064import org.nuxeo.ecm.core.api.pathsegment.PathSegmentService; 065import org.nuxeo.ecm.core.api.repository.RepositoryManager; 066import org.nuxeo.ecm.core.event.Event; 067import org.nuxeo.ecm.core.event.EventContext; 068import org.nuxeo.ecm.core.event.EventService; 069import org.nuxeo.ecm.core.event.impl.DocumentEventContext; 070import org.nuxeo.ecm.platform.rendering.api.RenderingException; 071import org.nuxeo.ecm.platform.usermanager.NuxeoPrincipalImpl; 072import org.nuxeo.ecm.platform.usermanager.UserConfig; 073import org.nuxeo.ecm.platform.usermanager.UserManager; 074import org.nuxeo.ecm.platform.usermanager.exceptions.UserAlreadyExistsException; 075import org.nuxeo.runtime.api.Framework; 076import org.nuxeo.runtime.model.ComponentInstance; 077import org.nuxeo.runtime.model.DefaultComponent; 078 079public class UserInvitationComponent extends DefaultComponent implements UserInvitationService { 080 081 protected static Log log = LogFactory.getLog(UserInvitationService.class); 082 083 public static final String NUXEO_URL_KEY = "nuxeo.url"; 084 085 protected String repoName = null; 086 087 protected String testRendering = null; 088 089 protected RenderingHelper rh = new RenderingHelper(); 090 091 protected Map<String, UserRegistrationConfiguration> configurations = new HashMap<String, UserRegistrationConfiguration>(); 092 093 private static final String INVITATION_SUBMITTED_EVENT = "invitationSubmitted"; 094 095 private static final String INVITATION_ACCEPTED_EVENT = "invitationAccepted"; 096 097 private static final String INVITATION_REJECTED_EVENT = "invitationRejected"; 098 099 private static final String INVITATION_VALIDATED_EVENT = "invitationValidated"; 100 101 public String getTestedRendering() { 102 return testRendering; 103 } 104 105 protected String getTargetRepositoryName() { 106 if (repoName == null) { 107 RepositoryManager rm = Framework.getService(RepositoryManager.class); 108 repoName = rm.getDefaultRepositoryName(); 109 } 110 return repoName; 111 } 112 113 protected boolean userAlreadyExists(UserRegistrationInfo userRegistrationInfo) { 114 DocumentModel user = Framework.getLocalService(UserManager.class).getUserModel(userRegistrationInfo.getLogin()); 115 return user != null; 116 } 117 118 protected String getJavaMailJndiName() { 119 return Framework.getProperty("jndi.java.mail", "java:/Mail"); 120 } 121 122 @Override 123 public DocumentModel getUserRegistrationModel(String configurationName) { 124 // Test if the configuration is defined 125 if (StringUtils.isEmpty(configurationName)) { 126 configurationName = DEFAULT_CONFIGURATION_NAME; 127 } 128 // Get the DocumentModel for the doctype defined in the configuration 129 UserRegistrationModelCreator creator = new UserRegistrationModelCreator(configurationName); 130 creator.runUnrestricted(); 131 return creator.getUserRegistrationModel(); 132 } 133 134 @Override 135 public DocumentModel getRegistrationRulesDocument(CoreSession session, String configurationName) 136 { 137 // By default, configuration is hold by the root request document 138 return getOrCreateRootDocument(session, configurationName); 139 } 140 141 public DocumentModel getOrCreateRootDocument(CoreSession session, String configurationName) { 142 UserRegistrationConfiguration configuration = getConfiguration(configurationName); 143 144 String targetPath = configuration.getContainerParentPath() + configuration.getContainerName(); 145 DocumentRef targetRef = new PathRef(targetPath); 146 DocumentModel root; 147 148 if (!session.exists(targetRef)) { 149 root = session.createDocumentModel(configuration.getContainerDocType()); 150 root.setPathInfo(configuration.getContainerParentPath(), configuration.getContainerName()); 151 root.setPropertyValue("dc:title", configuration.getContainerTitle()); 152 // XXX ACLs ?!!! 153 root = session.createDocument(root); 154 } else { 155 root = session.getDocument(targetRef); 156 } 157 158 // Add configuration facet 159 if (!root.hasFacet(FACET_REGISTRATION_CONFIGURATION)) { 160 root.addFacet(FACET_REGISTRATION_CONFIGURATION); 161 root.setPropertyValue(FIELD_CONFIGURATION_NAME, configuration.getName()); 162 root = session.saveDocument(root); 163 } 164 return root; 165 } 166 167 protected class UserRegistrationModelCreator extends UnrestrictedSessionRunner { 168 169 DocumentModel userRegistrationModel; 170 171 protected UserRegistrationConfiguration configuration; 172 173 public UserRegistrationModelCreator(String configurationName) { 174 super(getTargetRepositoryName()); 175 configuration = getConfiguration(configurationName); 176 } 177 178 @Override 179 public void run() { 180 userRegistrationModel = session.createDocumentModel(configuration.getRequestDocType()); 181 } 182 183 public DocumentModel getUserRegistrationModel() { 184 return userRegistrationModel; 185 } 186 } 187 188 protected class RegistrationCreator extends UnrestrictedSessionRunner { 189 190 protected Map<String, Serializable> additionnalInfo; 191 192 protected String registrationUuid; 193 194 protected ValidationMethod validationMethod; 195 196 protected DocumentModel userRegistrationModel; 197 198 protected UserRegistrationConfiguration configuration; 199 200 public String getRegistrationUuid() { 201 return registrationUuid; 202 } 203 204 public RegistrationCreator(String configurationName, DocumentModel userRegistrationModel, 205 Map<String, Serializable> additionnalInfo, ValidationMethod validationMethod) { 206 super(getTargetRepositoryName()); 207 this.userRegistrationModel = userRegistrationModel; 208 this.additionnalInfo = additionnalInfo; 209 this.validationMethod = validationMethod; 210 configuration = getConfiguration(configurationName); 211 } 212 213 @Override 214 public void run() { 215 216 String title = "registration request for " 217 + userRegistrationModel.getPropertyValue(configuration.getUserInfoUsernameField()) + " (" 218 + userRegistrationModel.getPropertyValue(configuration.getUserInfoEmailField()) + " " 219 + userRegistrationModel.getPropertyValue(configuration.getUserInfoCompanyField()) + ") "; 220 PathSegmentService pss = Framework.getLocalService(PathSegmentService.class); 221 String name = pss.generatePathSegment(title + "-" + System.currentTimeMillis()); 222 223 String targetPath = getOrCreateRootDocument(session, configuration.getName()).getPathAsString(); 224 225 userRegistrationModel.setPathInfo(targetPath, name); 226 userRegistrationModel.setPropertyValue("dc:title", title); 227 228 // validation method 229 userRegistrationModel.setPropertyValue("registration:validationMethod", validationMethod.toString()); 230 231 // additionnal infos 232 if (additionnalInfo != null && !additionnalInfo.isEmpty()) { 233 for (String key : additionnalInfo.keySet()) { 234 try { 235 userRegistrationModel.setPropertyValue(key, additionnalInfo.get(key)); 236 } catch (PropertyException e) { 237 // skip silently 238 } 239 } 240 } 241 242 userRegistrationModel = session.createDocument(userRegistrationModel); 243 244 registrationUuid = userRegistrationModel.getId(); 245 246 sendEvent(session, userRegistrationModel, getNameEventRegistrationSubmitted()); 247 248 session.save(); 249 } 250 251 } 252 253 protected class RegistrationApprover extends UnrestrictedSessionRunner { 254 255 protected String uuid; 256 257 protected Map<String, Serializable> additionnalInfo; 258 259 public RegistrationApprover(String registrationUuid, Map<String, Serializable> additionnalInfo) { 260 super(getTargetRepositoryName()); 261 uuid = registrationUuid; 262 this.additionnalInfo = additionnalInfo; 263 } 264 265 @Override 266 public void run() { 267 268 DocumentModel doc = session.getDocument(new IdRef(uuid)); 269 String validationMethod = (String) doc.getPropertyValue("registration:validationMethod"); 270 271 // test Validation Method 272 if (StringUtils.equals(EMAIL.toString(), validationMethod)) { 273 sendValidationEmail(additionnalInfo, doc); 274 } 275 276 doc.setPropertyValue("registration:accepted", true); 277 if (doc.getAllowedStateTransitions().contains("approve")) { 278 doc.followTransition("approve"); 279 } 280 doc = session.saveDocument(doc); 281 session.save(); 282 283 sendEvent(session, doc, getNameEventRegistrationAccepted()); 284 } 285 } 286 287 protected class RegistrationRejector extends UnrestrictedSessionRunner { 288 289 protected String uuid; 290 291 protected Map<String, Serializable> additionnalInfo; 292 293 public RegistrationRejector(String registrationUuid, Map<String, Serializable> additionnalInfo) { 294 super(getTargetRepositoryName()); 295 uuid = registrationUuid; 296 this.additionnalInfo = additionnalInfo; 297 } 298 299 @Override 300 public void run() { 301 302 DocumentModel doc = session.getDocument(new IdRef(uuid)); 303 304 doc.setPropertyValue("registration:accepted", false); 305 if (doc.getAllowedStateTransitions().contains("reject")) { 306 doc.followTransition("reject"); 307 } 308 doc = session.saveDocument(doc); 309 session.save(); 310 311 sendEvent(session, doc, getNameEventRegistrationRejected()); 312 } 313 } 314 315 protected class RegistrationAcceptator extends UnrestrictedSessionRunner { 316 317 protected String uuid; 318 319 protected Map<String, Serializable> registrationData = new HashMap<String, Serializable>(); 320 321 protected Map<String, Serializable> additionnalInfo; 322 323 public RegistrationAcceptator(String uuid, Map<String, Serializable> additionnalInfo) { 324 super(getTargetRepositoryName()); 325 this.uuid = uuid; 326 this.additionnalInfo = additionnalInfo; 327 } 328 329 public Map<String, Serializable> getRegistrationData() { 330 return registrationData; 331 } 332 333 @Override 334 public void run() { 335 DocumentRef idRef = new IdRef(uuid); 336 337 DocumentModel registrationDoc = session.getDocument(idRef); 338 339 // additionnal infos 340 for (String key : additionnalInfo.keySet()) { 341 try { 342 if (DefaultInvitationUserFactory.PASSWORD_KEY.equals(key)) { 343 // add the password as a transient context data 344 registrationDoc.putContextData(DefaultInvitationUserFactory.PASSWORD_KEY, 345 additionnalInfo.get(key)); 346 } else { 347 registrationDoc.setPropertyValue(key, additionnalInfo.get(key)); 348 } 349 } catch (PropertyException e) { 350 // skip silently 351 } 352 } 353 354 if (registrationDoc.getLifeCyclePolicy().equals("registrationRequest")) { 355 if (registrationDoc.getCurrentLifeCycleState().equals("approved")) { 356 registrationDoc.followTransition("accept"); 357 } else { 358 if (registrationDoc.getCurrentLifeCycleState().equals("accepted")) { 359 throw new AlreadyProcessedRegistrationException( 360 "Registration request has already been processed."); 361 } else { 362 throw new UserRegistrationException("Registration request has not been accepted yet."); 363 } 364 } 365 } 366 367 session.saveDocument(registrationDoc); 368 session.save(); 369 EventContext evContext = sendEvent(session, registrationDoc, getNameEventRegistrationValidated()); 370 371 ((DocumentModelImpl) registrationDoc).detach(sessionIsAlreadyUnrestricted); 372 registrationData.put(REGISTRATION_DATA_DOC, registrationDoc); 373 registrationData.put(REGISTRATION_DATA_USER, evContext.getProperty("registeredUser")); 374 } 375 376 } 377 378 protected class RequestIdValidator extends UnrestrictedSessionRunner { 379 380 protected String uuid; 381 382 public RequestIdValidator(String uuid) { 383 super(getTargetRepositoryName()); 384 this.uuid = uuid; 385 } 386 387 @Override 388 public void run() { 389 DocumentRef idRef = new IdRef(uuid); 390 // Check if the id matches an existing document 391 if (!session.exists(idRef)) { 392 throw new UserRegistrationException("There is no existing registration request with id " + uuid); 393 } 394 395 // Check if the request has not been already validated 396 DocumentModel registrationDoc = session.getDocument(idRef); 397 if (registrationDoc.getCurrentLifeCycleState().equals("accepted")) { 398 throw new AlreadyProcessedRegistrationException("Registration request has already been processed."); 399 } 400 } 401 } 402 403 protected EventContext sendEvent(CoreSession session, DocumentModel source, String evName) 404 throws UserRegistrationException { 405 try { 406 EventService evService = Framework.getService(EventService.class); 407 EventContext evContext = new DocumentEventContext(session, session.getPrincipal(), source); 408 409 Event event = evContext.newEvent(evName); 410 411 evService.fireEvent(event); 412 413 return evContext; 414 } catch (UserRegistrationException ue) { 415 log.warn("Error during event processing", ue); 416 throw ue; 417 } 418 419 } 420 421 protected void sendValidationEmail(Map<String, Serializable> additionnalInfo, DocumentModel registrationDoc) 422 { 423 UserRegistrationConfiguration configuration = getConfiguration(registrationDoc); 424 sendEmail(additionnalInfo, registrationDoc, configuration.getValidationEmailTemplate(), 425 configuration.getValidationEmailTitle()); 426 } 427 428 protected void sendEmail(Map<String, Serializable> additionnalInfo, DocumentModel registrationDoc, 429 String emailTemplatePath, String emailTitle) { 430 UserRegistrationConfiguration configuration = getConfiguration(registrationDoc); 431 432 String emailAdress = (String) registrationDoc.getPropertyValue(configuration.getUserInfoEmailField()); 433 434 Map<String, Serializable> input = new HashMap<String, Serializable>(); 435 Map<String, Serializable> userinfo = new HashMap<String, Serializable>(); 436 userinfo.put("firstName", registrationDoc.getPropertyValue(configuration.getUserInfoFirstnameField())); 437 userinfo.put("lastName", registrationDoc.getPropertyValue(configuration.getUserInfoLastnameField())); 438 userinfo.put("login", registrationDoc.getPropertyValue(configuration.getUserInfoUsernameField())); 439 userinfo.put("id", registrationDoc.getId()); 440 441 String documentTitle = ""; 442 443 if (registrationDoc.hasSchema("docinfo")) { 444 documentTitle = (String) registrationDoc.getPropertyValue("docinfo:documentTitle"); 445 } 446 input.put("documentTitle", documentTitle); 447 input.put("configurationName", configuration.getName()); 448 input.put("comment", registrationDoc.getPropertyValue("registration:comment")); 449 input.put(UserInvitationService.REGISTRATION_CONFIGURATION_NAME, configuration.getName()); 450 input.put("userinfo", (Serializable) userinfo); 451 input.put("info", (Serializable) additionnalInfo); 452 input.put("userAlreadyExists", checkUserFromRegistrationExistence(registrationDoc)); 453 input.put("productName", Framework.getProperty("org.nuxeo.ecm.product.name")); 454 StringWriter writer = new StringWriter(); 455 456 try { 457 rh.getRenderingEngine().render(emailTemplatePath, input, writer); 458 } catch (RenderingException e) { 459 throw new NuxeoException("Error during rendering email", e); 460 } 461 462 // render custom email subject 463 emailTitle = renderSubjectTemplate(emailTitle, input); 464 465 String body = writer.getBuffer().toString(); 466 String copyTo = (String) registrationDoc.getPropertyValue("registration:copyTo"); 467 if (!isTestModeSet()) { 468 try { 469 generateMail(emailAdress, copyTo, emailTitle, body); 470 } catch (NamingException | MessagingException e) { 471 throw new NuxeoException("Error while sending mail: ", e); 472 } 473 } else { 474 testRendering = body; 475 } 476 } 477 478 private String renderSubjectTemplate(String emailTitle, Map<String, Serializable> input) { 479 Configuration stringCfg = rh.getEngineConfiguration(); 480 Writer out; 481 try { 482 Template templ = new Template("subjectTemplate", new StringReader(emailTitle), stringCfg); 483 out = new StringWriter(); 484 templ.process(input, out); 485 out.flush(); 486 } catch (IOException | TemplateException e) { 487 throw new NuxeoException("Error while rendering email subject: ", e); 488 } 489 return out.toString(); 490 } 491 492 protected static boolean isTestModeSet() { 493 return Framework.isTestModeSet() || !isBlank(Framework.getProperty("org.nuxeo.ecm.tester.name")); 494 } 495 496 protected boolean checkUserFromRegistrationExistence(DocumentModel registrationDoc) { 497 UserRegistrationConfiguration configuration = getConfiguration(registrationDoc); 498 return null != Framework.getLocalService(UserManager.class).getPrincipal( 499 (String) registrationDoc.getPropertyValue(configuration.getUserInfoUsernameField())); 500 } 501 502 protected void generateMail(String destination, String copy, String title, String content) throws NamingException, 503 MessagingException { 504 505 InitialContext ic = new InitialContext(); 506 Session session = (Session) ic.lookup(getJavaMailJndiName()); 507 508 MimeMessage msg = new MimeMessage(session); 509 msg.setFrom(new InternetAddress(session.getProperty("mail.from"))); 510 msg.setRecipients(Message.RecipientType.TO, InternetAddress.parse(destination, false)); 511 if (!isBlank(copy)) { 512 msg.addRecipient(Message.RecipientType.CC, new InternetAddress(copy, false)); 513 } 514 515 msg.setSubject(title, "UTF-8"); 516 msg.setSentDate(new Date()); 517 msg.setContent(content, "text/html; charset=utf-8"); 518 519 Transport.send(msg); 520 } 521 522 @Override 523 public String submitRegistrationRequest(DocumentModel userRegistrationModel, 524 Map<String, Serializable> additionnalInfo, ValidationMethod validationMethod, boolean autoAccept) 525 { 526 return submitRegistrationRequest(DEFAULT_CONFIGURATION_NAME, userRegistrationModel, additionnalInfo, 527 validationMethod, autoAccept); 528 } 529 530 @Override 531 public DocumentModelList getRegistrationsForUser(final String docId, final String username, 532 final String configurationName) { 533 final DocumentModelList registrationDocs = new DocumentModelListImpl(); 534 new UnrestrictedSessionRunner(getTargetRepositoryName()) { 535 @Override 536 public void run() { 537 String query = "SELECT * FROM Document WHERE ecm:currentLifeCycleState != 'validated' AND" 538 + " ecm:mixinType = '" + getConfiguration(configurationName).getRequestDocType() 539 + "' AND docinfo:documentId = '%s' AND" 540 + getConfiguration(configurationName).getUserInfoUsernameField() 541 + " = '%s' AND ecm:isCheckedInVersion = 0"; 542 query = String.format(query, docId, username); 543 registrationDocs.addAll(session.query(query)); 544 } 545 }.runUnrestricted(); 546 return registrationDocs; 547 } 548 549 protected static boolean isEmailExist(UserRegistrationConfiguration configuration, DocumentModel userRegistration) { 550 String email = (String) userRegistration.getPropertyValue(configuration.getUserInfoEmailField()); 551 if (isBlank(email)) { 552 return false; 553 } 554 555 Map<String, Serializable> filter = new HashMap<>(1); 556 filter.put(UserConfig.EMAIL_COLUMN, email); 557 558 DocumentModelList users = Framework.getLocalService(UserManager.class).searchUsers(filter, null); 559 return !users.isEmpty(); 560 } 561 562 @Override 563 public String submitRegistrationRequest(String configurationName, DocumentModel userRegistrationModel, 564 Map<String, Serializable> additionnalInfo, ValidationMethod validationMethod, boolean autoAccept) 565 { 566 RegistrationCreator creator = new RegistrationCreator(configurationName, userRegistrationModel, 567 additionnalInfo, validationMethod); 568 creator.runUnrestricted(); 569 String registrationUuid = creator.getRegistrationUuid(); 570 571 UserRegistrationConfiguration currentConfig = getConfiguration(configurationName); 572 boolean userAlreadyExists = null != Framework.getLocalService(UserManager.class).getPrincipal( 573 (String) userRegistrationModel.getPropertyValue(currentConfig.getUserInfoUsernameField())); 574 575 if (!userAlreadyExists && isEmailExist(currentConfig, userRegistrationModel)) { 576 log.info("Trying to submit a registration from an existing email with a different username."); 577 throw new UserAlreadyExistsException(); 578 } 579 580 // Directly accept registration if the configuration allow it and the 581 // user already exists 582 RegistrationRules registrationRules = getRegistrationRules(configurationName); 583 boolean byPassAdminValidation = autoAccept; 584 byPassAdminValidation |= userAlreadyExists && registrationRules.allowDirectValidationForExistingUser(); 585 byPassAdminValidation |= registrationRules.allowDirectValidationForExistingUser() 586 && registrationRules.allowDirectValidationForNonExistingUser(); 587 if (byPassAdminValidation) { 588 // Build validationBaseUrl with nuxeo.url property as request is not 589 // accessible. 590 if (!additionnalInfo.containsKey("enterPasswordUrl")) { 591 additionnalInfo.put("enterPasswordUrl", buildEnterPasswordUrl(currentConfig)); 592 } 593 acceptRegistrationRequest(registrationUuid, additionnalInfo); 594 } 595 return registrationUuid; 596 } 597 598 protected String buildEnterPasswordUrl(UserRegistrationConfiguration configuration) { 599 String baseUrl = Framework.getProperty(NUXEO_URL_KEY); 600 601 baseUrl = isBlank(baseUrl) ? "/" : baseUrl; 602 if (!baseUrl.endsWith("/")) { 603 baseUrl += "/"; 604 } 605 return baseUrl.concat(configuration.getEnterPasswordUrl()); 606 } 607 608 @Override 609 public void acceptRegistrationRequest(String requestId, Map<String, Serializable> additionnalInfo) 610 throws UserRegistrationException { 611 RegistrationApprover acceptor = new RegistrationApprover(requestId, additionnalInfo); 612 acceptor.runUnrestricted(); 613 614 } 615 616 @Override 617 public void rejectRegistrationRequest(String requestId, Map<String, Serializable> additionnalInfo) 618 throws UserRegistrationException { 619 620 RegistrationRejector rejector = new RegistrationRejector(requestId, additionnalInfo); 621 rejector.runUnrestricted(); 622 623 } 624 625 @Override 626 public Map<String, Serializable> validateRegistration(String requestId, Map<String, Serializable> additionnalInfo) 627 throws UserRegistrationException { 628 RegistrationAcceptator validator = new RegistrationAcceptator(requestId, additionnalInfo); 629 validator.runUnrestricted(); 630 return validator.getRegistrationData(); 631 } 632 633 @Override 634 public Map<String, Serializable> validateRegistrationAndSendEmail(String requestId, 635 Map<String, Serializable> additionnalInfo) throws UserRegistrationException { 636 637 Map<String, Serializable> registrationInfo = validateRegistration(requestId, additionnalInfo); 638 639 Map<String, Serializable> input = new HashMap<String, Serializable>(); 640 input.putAll(registrationInfo); 641 input.put("info", (Serializable) additionnalInfo); 642 StringWriter writer = new StringWriter(); 643 644 UserRegistrationConfiguration configuration = getConfiguration((DocumentModel) registrationInfo.get(REGISTRATION_DATA_DOC)); 645 try { 646 rh.getRenderingEngine().render(configuration.getSuccessEmailTemplate(), input, writer); 647 } catch (RenderingException e) { 648 throw new NuxeoException("Error during rendering email", e); 649 } 650 651 String emailAdress = ((NuxeoPrincipalImpl) registrationInfo.get("registeredUser")).getEmail(); 652 String body = writer.getBuffer().toString(); 653 String title = configuration.getValidationEmailTitle(); 654 if (!Framework.isTestModeSet()) { 655 try { 656 generateMail(emailAdress, null, title, body); 657 } catch (NamingException | MessagingException e) { 658 throw new NuxeoException("Error while sending mail : ", e); 659 } 660 } else { 661 testRendering = body; 662 } 663 664 return registrationInfo; 665 } 666 667 @Override 668 public void registerContribution(Object contribution, String extensionPoint, ComponentInstance contributor) { 669 if ("configuration".equals(extensionPoint)) { 670 UserRegistrationConfiguration newConfig = (UserRegistrationConfiguration) contribution; 671 672 if (configurations.containsKey(newConfig.getName())) { 673 if (newConfig.isMerge()) { 674 configurations.get(newConfig.getName()).mergeWith(newConfig); 675 } else if (newConfig.isRemove()) { 676 configurations.remove(newConfig.getName()); 677 } else { 678 log.warn("Trying to register an existing userRegistration configuration without removing or merging it, in: " 679 + contributor.getName()); 680 } 681 } else { 682 configurations.put(newConfig.getName(), newConfig); 683 } 684 } 685 } 686 687 protected InvitationUserFactory getRegistrationUserFactory(UserRegistrationConfiguration configuration) { 688 InvitationUserFactory factory = null; 689 Class<? extends InvitationUserFactory> factoryClass = configuration.getRegistrationUserFactory(); 690 if (factoryClass != null) { 691 try { 692 factory = factoryClass.newInstance(); 693 } catch (InstantiationException e) { 694 log.warn("Failed to instanciate RegistrationUserFactory", e); 695 } catch (IllegalAccessException e) { 696 log.warn("Failed to instanciate RegistrationUserFactory", e); 697 } 698 } 699 if (factory == null) { 700 factory = new DefaultInvitationUserFactory(); 701 } 702 return factory; 703 } 704 705 @Override 706 public NuxeoPrincipal createUser(CoreSession session, DocumentModel registrationDoc) throws 707 UserRegistrationException { 708 UserRegistrationConfiguration configuration = getConfiguration(registrationDoc); 709 return getRegistrationUserFactory(configuration).doCreateUser(session, registrationDoc, configuration); 710 } 711 712 protected class RootDocumentGetter extends UnrestrictedSessionRunner { 713 714 protected DocumentModel doc; 715 716 protected String configurationName; 717 718 protected RootDocumentGetter(String configurationName) { 719 super(getTargetRepositoryName()); 720 this.configurationName = configurationName; 721 } 722 723 @Override 724 public void run() { 725 doc = getOrCreateRootDocument(session, configurationName); 726 ((DocumentModelImpl) doc).detach(true); 727 } 728 729 public DocumentModel getDoc() { 730 return doc; 731 } 732 } 733 734 @Override 735 public UserRegistrationConfiguration getConfiguration() { 736 return getConfiguration(DEFAULT_CONFIGURATION_NAME); 737 } 738 739 @Override 740 public UserRegistrationConfiguration getConfiguration(DocumentModel requestDoc) { 741 try { 742 DocumentModel parent = requestDoc.getCoreSession().getDocument(requestDoc.getParentRef()); 743 String configurationName = DEFAULT_CONFIGURATION_NAME; 744 if (parent.hasFacet(FACET_REGISTRATION_CONFIGURATION)) { 745 configurationName = (String) parent.getPropertyValue(FIELD_CONFIGURATION_NAME); 746 } else if (requestDoc.hasFacet(FACET_REGISTRATION_CONFIGURATION)) { 747 configurationName = (String) requestDoc.getPropertyValue(FIELD_CONFIGURATION_NAME); 748 } 749 750 if (!configurations.containsKey(configurationName)) { 751 throw new NuxeoException("Configuration " + configurationName + " is not registered"); 752 } 753 return configurations.get(configurationName); 754 } catch (NuxeoException e) { 755 log.info("Unable to get request parent document: " + e.getMessage()); 756 throw e; 757 } 758 } 759 760 @Override 761 public UserRegistrationConfiguration getConfiguration(String name) { 762 if (!configurations.containsKey(name)) { 763 throw new NuxeoException("Trying to get unknown user registration configuration."); 764 } 765 return configurations.get(name); 766 } 767 768 @Override 769 public RegistrationRules getRegistrationRules(String configurationName) { 770 RootDocumentGetter rdg = new RootDocumentGetter(configurationName); 771 rdg.runUnrestricted(); 772 return rdg.getDoc().getAdapter(RegistrationRules.class); 773 } 774 775 @Override 776 public void reviveRegistrationRequests(CoreSession session, List<DocumentModel> registrationDocs) 777 { 778 for (DocumentModel registrationDoc : registrationDocs) { 779 reviveRegistrationRequest(session, registrationDoc, new HashMap<String, Serializable>()); 780 } 781 } 782 783 protected void reviveRegistrationRequest(CoreSession session, DocumentModel registrationDoc, 784 Map<String, Serializable> additionalInfos) { 785 UserRegistrationConfiguration configuration = getConfiguration(registrationDoc); 786 // Build validationBaseUrl with nuxeo.url property as request is not 787 // accessible. 788 if (!additionalInfos.containsKey("enterPasswordUrl")) { 789 additionalInfos.put("enterPasswordUrl", buildEnterPasswordUrl(configuration)); 790 } 791 sendEmail(additionalInfos, registrationDoc, configuration.getReviveEmailTemplate(), 792 configuration.getReviveEmailTitle()); 793 } 794 795 @Override 796 public void deleteRegistrationRequests(CoreSession session, List<DocumentModel> registrationDocs) 797 { 798 for (DocumentModel registration : registrationDocs) { 799 UserRegistrationConfiguration configuration = getConfiguration(registration); 800 if (!registration.hasSchema(configuration.getUserInfoSchemaName())) { 801 throw new NuxeoException("Registration document do not contains needed schema"); 802 } 803 804 session.removeDocument(registration.getRef()); 805 } 806 } 807 808 @Override 809 public Set<String> getConfigurationsName() { 810 return configurations.keySet(); 811 } 812 813 @Override 814 public void checkRequestId(final String requestId) throws UserRegistrationException { 815 RequestIdValidator runner = new RequestIdValidator(requestId); 816 runner.runUnrestricted(); 817 } 818 819 @Override 820 public String getNameEventRegistrationSubmitted() { 821 return INVITATION_SUBMITTED_EVENT; 822 } 823 824 @Override 825 public String getNameEventRegistrationAccepted() { 826 return INVITATION_ACCEPTED_EVENT; 827 } 828 829 @Override 830 public String getNameEventRegistrationRejected() { 831 return INVITATION_REJECTED_EVENT; 832 } 833 834 @Override 835 public String getNameEventRegistrationValidated() { 836 return INVITATION_VALIDATED_EVENT; 837 } 838 839}