001/* 002 * (C) Copyright 2011-2019 Nuxeo (http://nuxeo.com/) and others. 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 * Contributors: 016 * Nuxeo - initial API and implementation 017 */ 018 019package org.nuxeo.ecm.user.invite; 020 021import static org.apache.commons.lang3.StringUtils.isBlank; 022import static org.nuxeo.ecm.user.invite.RegistrationRules.FACET_REGISTRATION_CONFIGURATION; 023import static org.nuxeo.ecm.user.invite.RegistrationRules.FIELD_CONFIGURATION_NAME; 024import static org.nuxeo.ecm.user.invite.UserInvitationService.ValidationMethod.EMAIL; 025import static org.nuxeo.ecm.user.invite.UserRegistrationConfiguration.DEFAULT_CONFIGURATION_NAME; 026 027import java.io.IOException; 028import java.io.Serializable; 029import java.io.StringReader; 030import java.io.StringWriter; 031import java.io.Writer; 032import java.util.Date; 033import java.util.HashMap; 034import java.util.List; 035import java.util.Map; 036import java.util.Set; 037 038import javax.mail.Message; 039import javax.mail.MessagingException; 040import javax.mail.Session; 041import javax.mail.Transport; 042import javax.mail.internet.InternetAddress; 043import javax.mail.internet.MimeMessage; 044 045import org.apache.commons.lang3.StringUtils; 046import org.apache.commons.logging.Log; 047import org.apache.commons.logging.LogFactory; 048import org.nuxeo.ecm.core.api.CoreSession; 049import org.nuxeo.ecm.core.api.DocumentModel; 050import org.nuxeo.ecm.core.api.DocumentModelList; 051import org.nuxeo.ecm.core.api.DocumentRef; 052import org.nuxeo.ecm.core.api.IdRef; 053import org.nuxeo.ecm.core.api.NuxeoException; 054import org.nuxeo.ecm.core.api.NuxeoPrincipal; 055import org.nuxeo.ecm.core.api.PathRef; 056import org.nuxeo.ecm.core.api.PropertyException; 057import org.nuxeo.ecm.core.api.UnrestrictedSessionRunner; 058import org.nuxeo.ecm.core.api.impl.DocumentModelListImpl; 059import org.nuxeo.ecm.core.api.pathsegment.PathSegmentService; 060import org.nuxeo.ecm.core.api.repository.RepositoryManager; 061import org.nuxeo.ecm.core.api.security.ACE; 062import org.nuxeo.ecm.core.api.security.ACL; 063import org.nuxeo.ecm.core.api.security.ACP; 064import org.nuxeo.ecm.core.event.Event; 065import org.nuxeo.ecm.core.event.EventContext; 066import org.nuxeo.ecm.core.event.EventService; 067import org.nuxeo.ecm.core.event.impl.DocumentEventContext; 068import org.nuxeo.ecm.platform.rendering.api.RenderingException; 069import org.nuxeo.ecm.platform.usermanager.NuxeoPrincipalImpl; 070import org.nuxeo.ecm.platform.usermanager.UserConfig; 071import org.nuxeo.ecm.platform.usermanager.UserManager; 072import org.nuxeo.ecm.platform.usermanager.exceptions.UserAlreadyExistsException; 073import org.nuxeo.mail.MailSessionBuilder; 074import org.nuxeo.runtime.api.Framework; 075import org.nuxeo.runtime.model.ComponentInstance; 076import org.nuxeo.runtime.model.DefaultComponent; 077 078import freemarker.template.Configuration; 079import freemarker.template.Template; 080import freemarker.template.TemplateException; 081 082public class UserInvitationComponent extends DefaultComponent implements UserInvitationService { 083 084 public static final String PARAM_ORIGINATING_USER = "registration:originatingUser"; 085 086 protected static Log log = LogFactory.getLog(UserInvitationService.class); 087 088 public static final String NUXEO_URL_KEY = "nuxeo.url"; 089 090 protected String repoName = null; 091 092 protected String testRendering = null; 093 094 protected RenderingHelper rh = new RenderingHelper(); 095 096 protected Map<String, UserRegistrationConfiguration> configurations = new HashMap<>(); 097 098 private static final String INVITATION_SUBMITTED_EVENT = "invitationSubmitted"; 099 100 private static final String INVITATION_ACCEPTED_EVENT = "invitationAccepted"; 101 102 private static final String INVITATION_REJECTED_EVENT = "invitationRejected"; 103 104 private static final String INVITATION_VALIDATED_EVENT = "invitationValidated"; 105 106 public String getTestedRendering() { 107 return testRendering; 108 } 109 110 protected String getTargetRepositoryName() { 111 if (repoName == null) { 112 RepositoryManager rm = Framework.getService(RepositoryManager.class); 113 repoName = rm.getDefaultRepositoryName(); 114 } 115 return repoName; 116 } 117 118 protected boolean userAlreadyExists(UserRegistrationInfo userRegistrationInfo) { 119 DocumentModel user = Framework.getService(UserManager.class).getUserModel(userRegistrationInfo.getLogin()); 120 return user != null; 121 } 122 123 @Override 124 public DocumentModel getUserRegistrationModel(String configurationName) { 125 // Test if the configuration is defined 126 if (StringUtils.isEmpty(configurationName)) { 127 configurationName = DEFAULT_CONFIGURATION_NAME; 128 } 129 // Get the DocumentModel for the doctype defined in the configuration 130 UserRegistrationModelCreator creator = new UserRegistrationModelCreator(configurationName); 131 creator.runUnrestricted(); 132 return creator.getUserRegistrationModel(); 133 } 134 135 @Override 136 public DocumentModel getRegistrationRulesDocument(CoreSession session, String configurationName) { 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.getService(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 private final UserManager userManager; 256 257 protected String uuid; 258 259 protected Map<String, Serializable> additionnalInfo; 260 261 public RegistrationApprover(String registrationUuid, Map<String, Serializable> additionnalInfo) { 262 super(getTargetRepositoryName()); 263 uuid = registrationUuid; 264 this.additionnalInfo = additionnalInfo; 265 this.userManager = Framework.getService(UserManager.class); 266 } 267 268 @Override 269 public void run() { 270 271 DocumentModel doc = session.getDocument(new IdRef(uuid)); 272 String validationMethod = (String) doc.getPropertyValue("registration:validationMethod"); 273 274 NuxeoPrincipal targetPrincipal = userManager.getPrincipal((String) doc.getPropertyValue("userinfo:login")); 275 if (targetPrincipal != null) { 276 throw new UserAlreadyExistsException(); 277 } 278 279 targetPrincipal = userManager.getPrincipal((String) doc.getPropertyValue("userinfo:email")); 280 if (targetPrincipal != null) { 281 DocumentModel target = session.getDocument( 282 new IdRef((String) doc.getPropertyValue("docinfo:documentId"))); 283 ACP acp = target.getACP(); 284 Map<String, Serializable> contextData = new HashMap<>(); 285 contextData.put("notify", true); 286 contextData.put("comment", doc.getPropertyValue("registration:comment")); 287 acp.addACE(ACL.LOCAL_ACL, 288 ACE.builder(targetPrincipal.getName(), (String) doc.getPropertyValue("docinfo:permission")) 289 .creator((String) doc.getPropertyValue("docinfo:creator")) 290 .contextData(contextData) 291 .build()); 292 target.setACP(acp, true); 293 // test Validation Method 294 } else if (StringUtils.equals(EMAIL.toString(), validationMethod)) { 295 sendValidationEmail(additionnalInfo, doc); 296 } 297 298 doc.setPropertyValue("registration:accepted", true); 299 if (doc.getAllowedStateTransitions().contains("approve")) { 300 doc.followTransition("approve"); 301 } 302 doc = session.saveDocument(doc); 303 session.save(); 304 305 sendEvent(session, doc, getNameEventRegistrationAccepted()); 306 } 307 } 308 309 protected class RegistrationRejector extends UnrestrictedSessionRunner { 310 311 protected String uuid; 312 313 protected Map<String, Serializable> additionnalInfo; 314 315 public RegistrationRejector(String registrationUuid, Map<String, Serializable> additionnalInfo) { 316 super(getTargetRepositoryName()); 317 uuid = registrationUuid; 318 this.additionnalInfo = additionnalInfo; 319 } 320 321 @Override 322 public void run() { 323 324 DocumentModel doc = session.getDocument(new IdRef(uuid)); 325 326 doc.setPropertyValue("registration:accepted", false); 327 if (doc.getAllowedStateTransitions().contains("reject")) { 328 doc.followTransition("reject"); 329 } 330 doc = session.saveDocument(doc); 331 session.save(); 332 333 sendEvent(session, doc, getNameEventRegistrationRejected()); 334 } 335 } 336 337 protected class RegistrationAcceptator extends UnrestrictedSessionRunner { 338 339 protected String uuid; 340 341 protected Map<String, Serializable> registrationData = new HashMap<>(); 342 343 protected Map<String, Serializable> additionnalInfo; 344 345 public RegistrationAcceptator(String uuid, Map<String, Serializable> additionnalInfo) { 346 super(getTargetRepositoryName()); 347 this.uuid = uuid; 348 this.additionnalInfo = additionnalInfo; 349 } 350 351 public Map<String, Serializable> getRegistrationData() { 352 return registrationData; 353 } 354 355 @Override 356 public void run() { 357 DocumentRef idRef = new IdRef(uuid); 358 359 DocumentModel registrationDoc = session.getDocument(idRef); 360 361 // additionnal infos 362 for (var entry : additionnalInfo.entrySet()) { 363 try { 364 if (DefaultInvitationUserFactory.PASSWORD_KEY.equals(entry.getKey())) { 365 // add the password as a transient context data 366 registrationDoc.putContextData(DefaultInvitationUserFactory.PASSWORD_KEY, 367 additionnalInfo.get(entry.getKey())); 368 } else { 369 registrationDoc.setPropertyValue(entry.getKey(), entry.getValue()); 370 } 371 } catch (PropertyException e) { 372 // skip silently 373 } 374 } 375 376 NuxeoPrincipal principal = null; 377 if (registrationDoc.getLifeCyclePolicy().equals("registrationRequest")) { 378 if (registrationDoc.getCurrentLifeCycleState().equals("approved")) { 379 try { 380 UserInvitationService userRegistrationService = Framework.getService( 381 UserInvitationService.class); 382 UserRegistrationConfiguration config = userRegistrationService.getConfiguration( 383 registrationDoc); 384 RegistrationRules rules = userRegistrationService.getRegistrationRules(config.getName()); 385 if (rules.allowUserCreation()) { 386 principal = userRegistrationService.createUser(session, registrationDoc); 387 } 388 registrationDoc.followTransition("accept"); 389 } catch (NuxeoException e) { 390 e.addInfo("Unable to complete registration"); 391 throw e; 392 } 393 } else { 394 if (registrationDoc.getCurrentLifeCycleState().equals("accepted")) { 395 throw new AlreadyProcessedRegistrationException( 396 "Registration request has already been processed"); 397 } else { 398 throw new UserRegistrationException("Registration request has not been accepted yet"); 399 } 400 } 401 } 402 403 session.saveDocument(registrationDoc); 404 session.save(); 405 sendEvent(session, registrationDoc, getNameEventRegistrationValidated()); 406 registrationDoc.detach(sessionIsAlreadyUnrestricted); 407 registrationData.put(REGISTRATION_DATA_DOC, registrationDoc); 408 registrationData.put(REGISTRATION_DATA_USER, principal); 409 } 410 411 } 412 413 protected class RequestIdValidator extends UnrestrictedSessionRunner { 414 415 protected String uuid; 416 417 public RequestIdValidator(String uuid) { 418 super(getTargetRepositoryName()); 419 this.uuid = uuid; 420 } 421 422 @Override 423 public void run() { 424 DocumentRef idRef = new IdRef(uuid); 425 // Check if the id matches an existing document 426 if (!session.exists(idRef)) { 427 throw new UserRegistrationException("There is no existing registration request with id " + uuid); 428 } 429 430 // Check if the request has not been already validated 431 DocumentModel registrationDoc = session.getDocument(idRef); 432 if (registrationDoc.getCurrentLifeCycleState().equals("accepted")) { 433 throw new AlreadyProcessedRegistrationException("Registration request has already been processed"); 434 } 435 } 436 } 437 438 protected EventContext sendEvent(CoreSession session, DocumentModel source, String evName) 439 throws UserRegistrationException { 440 try { 441 EventService evService = Framework.getService(EventService.class); 442 EventContext evContext = new DocumentEventContext(session, session.getPrincipal(), source); 443 444 Event event = evContext.newEvent(evName); 445 446 evService.fireEvent(event); 447 448 return evContext; 449 } catch (UserRegistrationException ue) { 450 log.warn("Error during event processing", ue); 451 throw ue; 452 } 453 454 } 455 456 protected void sendValidationEmail(Map<String, Serializable> additionnalInfo, DocumentModel registrationDoc) { 457 UserRegistrationConfiguration configuration = getConfiguration(registrationDoc); 458 sendEmail(additionnalInfo, registrationDoc, configuration.getValidationEmailTemplate(), 459 configuration.getValidationEmailTitle()); 460 } 461 462 protected void sendEmail(Map<String, Serializable> additionnalInfo, DocumentModel registrationDoc, 463 String emailTemplatePath, String emailTitle) { 464 UserRegistrationConfiguration configuration = getConfiguration(registrationDoc); 465 466 String emailAdress = (String) registrationDoc.getPropertyValue(configuration.getUserInfoEmailField()); 467 468 Map<String, Serializable> input = new HashMap<>(); 469 Map<String, Serializable> userinfo = new HashMap<>(); 470 userinfo.put("firstName", registrationDoc.getPropertyValue(configuration.getUserInfoFirstnameField())); 471 userinfo.put("lastName", registrationDoc.getPropertyValue(configuration.getUserInfoLastnameField())); 472 userinfo.put("login", registrationDoc.getPropertyValue(configuration.getUserInfoUsernameField())); 473 userinfo.put("id", registrationDoc.getId()); 474 475 String documentTitle = ""; 476 477 if (registrationDoc.hasSchema("docinfo")) { 478 documentTitle = (String) registrationDoc.getPropertyValue("docinfo:documentTitle"); 479 } 480 input.put("documentTitle", documentTitle); 481 input.put("comment", registrationDoc.getPropertyValue("registration:comment")); 482 input.put(UserInvitationService.REGISTRATION_CONFIGURATION_NAME, configuration.getName()); 483 input.put("userinfo", (Serializable) userinfo); 484 input.put("info", (Serializable) additionnalInfo); 485 input.put("userAlreadyExists", checkUserFromRegistrationExistence(registrationDoc)); 486 input.put("productName", Framework.getProperty("org.nuxeo.ecm.product.name")); 487 StringWriter writer = new StringWriter(); 488 489 try { 490 rh.getRenderingEngine().render(emailTemplatePath, input, writer); 491 } catch (RenderingException e) { 492 throw new NuxeoException("Error during rendering email", e); 493 } 494 495 // render custom email subject 496 emailTitle = renderSubjectTemplate(emailTitle, input); 497 498 String body = writer.getBuffer().toString(); 499 String copyTo = (String) registrationDoc.getPropertyValue("registration:copyTo"); 500 if (!isTestModeSet()) { 501 try { 502 generateMail(emailAdress, copyTo, emailTitle, body); 503 } catch (MessagingException e) { 504 throw new NuxeoException("Error while sending mail: ", e); 505 } 506 } else { 507 testRendering = body; 508 } 509 } 510 511 private String renderSubjectTemplate(String emailTitle, Map<String, Serializable> input) { 512 Configuration stringCfg = rh.getEngineConfiguration(); 513 Writer out; 514 try { 515 Template templ = new Template("subjectTemplate", new StringReader(emailTitle), stringCfg); 516 out = new StringWriter(); 517 templ.process(input, out); 518 out.flush(); 519 } catch (IOException | TemplateException e) { 520 throw new NuxeoException("Error while rendering email subject: ", e); 521 } 522 return out.toString(); 523 } 524 525 protected static boolean isTestModeSet() { 526 return Framework.isTestModeSet() || !isBlank(Framework.getProperty("org.nuxeo.ecm.tester.name")); 527 } 528 529 protected boolean checkUserFromRegistrationExistence(DocumentModel registrationDoc) { 530 UserRegistrationConfiguration configuration = getConfiguration(registrationDoc); 531 return null != Framework.getService(UserManager.class) 532 .getPrincipal((String) registrationDoc.getPropertyValue( 533 configuration.getUserInfoUsernameField())); 534 } 535 536 protected void generateMail(String destination, String copy, String title, String content) 537 throws MessagingException { 538 539 Session session = MailSessionBuilder.fromNuxeoConf().build(); 540 541 MimeMessage msg = new MimeMessage(session); 542 msg.setFrom(new InternetAddress(session.getProperty("mail.from"))); 543 msg.setRecipients(Message.RecipientType.TO, InternetAddress.parse(destination, false)); 544 if (!isBlank(copy)) { 545 msg.addRecipient(Message.RecipientType.CC, new InternetAddress(copy, false)); 546 } 547 548 msg.setSubject(title, "UTF-8"); 549 msg.setSentDate(new Date()); 550 msg.setContent(content, "text/html; charset=utf-8"); 551 552 Transport.send(msg); 553 } 554 555 @Override 556 public String submitRegistrationRequest(DocumentModel userRegistrationModel, 557 Map<String, Serializable> additionnalInfo, ValidationMethod validationMethod, boolean autoAccept) { 558 return submitRegistrationRequest(DEFAULT_CONFIGURATION_NAME, userRegistrationModel, additionnalInfo, 559 validationMethod, autoAccept); 560 } 561 562 @Override 563 public DocumentModelList getRegistrationsForUser(final String docId, final String username, 564 final String configurationName) { 565 final DocumentModelList registrationDocs = new DocumentModelListImpl(); 566 new UnrestrictedSessionRunner(getTargetRepositoryName()) { 567 @Override 568 public void run() { 569 String query = "SELECT * FROM Document WHERE ecm:currentLifeCycleState != 'validated'" 570 + " AND ecm:mixinType = '%s' AND docinfo:documentId = '%s'" 571 + " AND %s = '%s' AND ecm:isVersion = 0"; 572 573 UserRegistrationConfiguration configuration = getConfiguration(configurationName); 574 query = String.format(query, configuration.getRequestDocType(), docId, 575 configuration.getUserInfoUsernameField(), username); 576 577 registrationDocs.addAll(session.query(query)); 578 } 579 }.runUnrestricted(); 580 return registrationDocs; 581 } 582 583 protected static boolean isEmailExist(UserRegistrationConfiguration configuration, DocumentModel userRegistration) { 584 String email = (String) userRegistration.getPropertyValue(configuration.getUserInfoEmailField()); 585 if (isBlank(email)) { 586 return false; 587 } 588 589 Map<String, Serializable> filter = new HashMap<>(1); 590 filter.put(UserConfig.EMAIL_COLUMN, email); 591 592 DocumentModelList users = Framework.getService(UserManager.class).searchUsers(filter, null); 593 return !users.isEmpty(); 594 } 595 596 @Override 597 public String submitRegistrationRequest(String configurationName, DocumentModel userRegistrationModel, 598 Map<String, Serializable> additionnalInfo, ValidationMethod validationMethod, boolean autoAccept) { 599 600 // First check that we have the originating user for that request 601 if (StringUtils.isBlank((String) additionnalInfo.get(PARAM_ORIGINATING_USER))) { 602 throw new IllegalArgumentException("Originating user should be provided in a registration request"); 603 } 604 UserRegistrationConfiguration currentConfig = getConfiguration(configurationName); 605 boolean userAlreadyExists = null != Framework.getService(UserManager.class) 606 .getPrincipal((String) userRegistrationModel.getPropertyValue( 607 currentConfig.getUserInfoUsernameField())); 608 609 if (!userAlreadyExists && isEmailExist(currentConfig, userRegistrationModel)) { 610 log.info("Trying to submit a registration from an existing email with a different username."); 611 throw new UserAlreadyExistsException(); 612 } 613 RegistrationCreator creator = new RegistrationCreator(configurationName, userRegistrationModel, additionnalInfo, 614 validationMethod); 615 creator.runUnrestricted(); 616 String registrationUuid = creator.getRegistrationUuid(); 617 618 // Directly accept registration if the configuration allow it and the 619 // user already exists 620 RegistrationRules registrationRules = getRegistrationRules(configurationName); 621 boolean byPassAdminValidation = autoAccept; 622 byPassAdminValidation |= userAlreadyExists && registrationRules.allowDirectValidationForExistingUser(); 623 byPassAdminValidation |= !userAlreadyExists && registrationRules.allowDirectValidationForNonExistingUser(); 624 if (byPassAdminValidation) { 625 // Build validationBaseUrl with nuxeo.url property as request is not 626 // accessible. 627 if (!additionnalInfo.containsKey("enterPasswordUrl")) { 628 additionnalInfo.put("enterPasswordUrl", buildEnterPasswordUrl(currentConfig)); 629 } 630 acceptRegistrationRequest(registrationUuid, additionnalInfo); 631 } 632 return registrationUuid; 633 } 634 635 protected String buildEnterPasswordUrl(UserRegistrationConfiguration configuration) { 636 String baseUrl = Framework.getProperty(NUXEO_URL_KEY); 637 638 baseUrl = isBlank(baseUrl) ? "/" : baseUrl; 639 if (!baseUrl.endsWith("/")) { 640 baseUrl += "/"; 641 } 642 return baseUrl.concat(configuration.getEnterPasswordUrl()); 643 } 644 645 @Override 646 public void acceptRegistrationRequest(String requestId, Map<String, Serializable> additionnalInfo) 647 throws UserRegistrationException { 648 RegistrationApprover acceptor = new RegistrationApprover(requestId, additionnalInfo); 649 acceptor.runUnrestricted(); 650 651 } 652 653 @Override 654 public void rejectRegistrationRequest(String requestId, Map<String, Serializable> additionnalInfo) 655 throws UserRegistrationException { 656 657 RegistrationRejector rejector = new RegistrationRejector(requestId, additionnalInfo); 658 rejector.runUnrestricted(); 659 660 } 661 662 @Override 663 public Map<String, Serializable> validateRegistration(String requestId, Map<String, Serializable> additionnalInfo) 664 throws UserRegistrationException { 665 RegistrationAcceptator validator = new RegistrationAcceptator(requestId, additionnalInfo); 666 validator.runUnrestricted(); 667 return validator.getRegistrationData(); 668 } 669 670 @Override 671 public Map<String, Serializable> validateRegistrationAndSendEmail(String requestId, 672 Map<String, Serializable> additionnalInfo) throws UserRegistrationException { 673 674 Map<String, Serializable> registrationInfo = validateRegistration(requestId, additionnalInfo); 675 676 Map<String, Serializable> input = new HashMap<>(registrationInfo); 677 input.put("info", (Serializable) additionnalInfo); 678 StringWriter writer = new StringWriter(); 679 680 UserRegistrationConfiguration configuration = getConfiguration( 681 (DocumentModel) registrationInfo.get(REGISTRATION_DATA_DOC)); 682 try { 683 rh.getRenderingEngine().render(configuration.getSuccessEmailTemplate(), input, writer); 684 } catch (RenderingException e) { 685 throw new NuxeoException("Error during rendering email", e); 686 } 687 688 String emailAdress = ((NuxeoPrincipalImpl) registrationInfo.get("registeredUser")).getEmail(); 689 String body = writer.getBuffer().toString(); 690 String title = configuration.getValidationEmailTitle(); 691 if (!Framework.isTestModeSet()) { 692 try { 693 generateMail(emailAdress, null, title, body); 694 } catch (MessagingException e) { 695 throw new NuxeoException("Error while sending mail : ", e); 696 } 697 } else { 698 testRendering = body; 699 } 700 701 return registrationInfo; 702 } 703 704 @Override 705 public void registerContribution(Object contribution, String extensionPoint, ComponentInstance contributor) { 706 if ("configuration".equals(extensionPoint)) { 707 UserRegistrationConfiguration newConfig = (UserRegistrationConfiguration) contribution; 708 709 if (configurations.containsKey(newConfig.getName())) { 710 if (newConfig.isMerge()) { 711 configurations.get(newConfig.getName()).mergeWith(newConfig); 712 } else if (newConfig.isRemove()) { 713 configurations.remove(newConfig.getName()); 714 } else { 715 log.warn( 716 "Trying to register an existing userRegistration configuration without removing or merging it, in: " 717 + contributor.getName()); 718 } 719 } else { 720 configurations.put(newConfig.getName(), newConfig); 721 } 722 } 723 } 724 725 protected InvitationUserFactory getRegistrationUserFactory(UserRegistrationConfiguration configuration) { 726 InvitationUserFactory factory = null; 727 Class<? extends InvitationUserFactory> factoryClass = configuration.getRegistrationUserFactory(); 728 if (factoryClass != null) { 729 try { 730 factory = factoryClass.getConstructor().newInstance(); 731 } catch (ReflectiveOperationException e) { 732 log.warn("Failed to instanciate RegistrationUserFactory", e); 733 } 734 } 735 if (factory == null) { 736 factory = new DefaultInvitationUserFactory(); 737 } 738 return factory; 739 } 740 741 @Override 742 public NuxeoPrincipal createUser(CoreSession session, DocumentModel registrationDoc) 743 throws UserRegistrationException { 744 UserRegistrationConfiguration configuration = getConfiguration(registrationDoc); 745 return getRegistrationUserFactory(configuration).doCreateUser(session, registrationDoc, configuration); 746 } 747 748 protected class RootDocumentGetter extends UnrestrictedSessionRunner { 749 750 protected DocumentModel doc; 751 752 protected String configurationName; 753 754 protected RootDocumentGetter(String configurationName) { 755 super(getTargetRepositoryName()); 756 this.configurationName = configurationName; 757 } 758 759 @Override 760 public void run() { 761 doc = getOrCreateRootDocument(session, configurationName); 762 doc.detach(true); 763 } 764 765 public DocumentModel getDoc() { 766 return doc; 767 } 768 } 769 770 @Override 771 public UserRegistrationConfiguration getConfiguration() { 772 return getConfiguration(DEFAULT_CONFIGURATION_NAME); 773 } 774 775 @Override 776 public UserRegistrationConfiguration getConfiguration(DocumentModel requestDoc) { 777 try { 778 DocumentModel parent = requestDoc.getCoreSession().getDocument(requestDoc.getParentRef()); 779 String configurationName = DEFAULT_CONFIGURATION_NAME; 780 if (parent.hasFacet(FACET_REGISTRATION_CONFIGURATION)) { 781 configurationName = (String) parent.getPropertyValue(FIELD_CONFIGURATION_NAME); 782 } else if (requestDoc.hasFacet(FACET_REGISTRATION_CONFIGURATION)) { 783 configurationName = (String) requestDoc.getPropertyValue(FIELD_CONFIGURATION_NAME); 784 } 785 786 if (!configurations.containsKey(configurationName)) { 787 throw new NuxeoException("Configuration " + configurationName + " is not registered"); 788 } 789 return configurations.get(configurationName); 790 } catch (NuxeoException e) { 791 log.info("Unable to get request parent document: " + e.getMessage()); 792 throw e; 793 } 794 } 795 796 @Override 797 public UserRegistrationConfiguration getConfiguration(String name) { 798 if (!configurations.containsKey(name)) { 799 throw new NuxeoException("Trying to get unknown user registration configuration."); 800 } 801 return configurations.get(name); 802 } 803 804 @Override 805 public RegistrationRules getRegistrationRules(String configurationName) { 806 RootDocumentGetter rdg = new RootDocumentGetter(configurationName); 807 rdg.runUnrestricted(); 808 return rdg.getDoc().getAdapter(RegistrationRules.class); 809 } 810 811 @Override 812 public void reviveRegistrationRequests(CoreSession session, List<DocumentModel> registrationDocs) { 813 for (DocumentModel registrationDoc : registrationDocs) { 814 reviveRegistrationRequest(session, registrationDoc, new HashMap<>()); 815 } 816 } 817 818 protected void reviveRegistrationRequest(CoreSession session, DocumentModel registrationDoc, 819 Map<String, Serializable> additionalInfos) { 820 UserRegistrationConfiguration configuration = getConfiguration(registrationDoc); 821 // Build validationBaseUrl with nuxeo.url property as request is not 822 // accessible. 823 if (!additionalInfos.containsKey("enterPasswordUrl")) { 824 additionalInfos.put("enterPasswordUrl", buildEnterPasswordUrl(configuration)); 825 } 826 sendEmail(additionalInfos, registrationDoc, configuration.getReviveEmailTemplate(), 827 configuration.getReviveEmailTitle()); 828 } 829 830 @Override 831 public void deleteRegistrationRequests(CoreSession session, List<DocumentModel> registrationDocs) { 832 for (DocumentModel registration : registrationDocs) { 833 UserRegistrationConfiguration configuration = getConfiguration(registration); 834 if (!registration.hasSchema(configuration.getUserInfoSchemaName())) { 835 throw new NuxeoException("Registration document do not contains needed schema"); 836 } 837 838 session.removeDocument(registration.getRef()); 839 } 840 } 841 842 @Override 843 public Set<String> getConfigurationsName() { 844 return configurations.keySet(); 845 } 846 847 @Override 848 public void checkRequestId(final String requestId) throws UserRegistrationException { 849 RequestIdValidator runner = new RequestIdValidator(requestId); 850 runner.runUnrestricted(); 851 } 852 853 @Override 854 public String getNameEventRegistrationSubmitted() { 855 return INVITATION_SUBMITTED_EVENT; 856 } 857 858 @Override 859 public String getNameEventRegistrationAccepted() { 860 return INVITATION_ACCEPTED_EVENT; 861 } 862 863 @Override 864 public String getNameEventRegistrationRejected() { 865 return INVITATION_REJECTED_EVENT; 866 } 867 868 @Override 869 public String getNameEventRegistrationValidated() { 870 return INVITATION_VALIDATED_EVENT; 871 } 872 873}