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