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