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 freemarker.template.Configuration; 048import freemarker.template.Template; 049import freemarker.template.TemplateException; 050import org.apache.commons.lang.StringUtils; 051import org.apache.commons.logging.Log; 052import org.apache.commons.logging.LogFactory; 053import org.nuxeo.ecm.core.api.CoreSession; 054import org.nuxeo.ecm.core.api.DocumentModel; 055import org.nuxeo.ecm.core.api.DocumentModelList; 056import org.nuxeo.ecm.core.api.DocumentRef; 057import org.nuxeo.ecm.core.api.IdRef; 058import org.nuxeo.ecm.core.api.NuxeoException; 059import org.nuxeo.ecm.core.api.NuxeoPrincipal; 060import org.nuxeo.ecm.core.api.PathRef; 061import org.nuxeo.ecm.core.api.PropertyException; 062import org.nuxeo.ecm.core.api.UnrestrictedSessionRunner; 063import org.nuxeo.ecm.core.api.impl.DocumentModelImpl; 064import org.nuxeo.ecm.core.api.impl.DocumentModelListImpl; 065import org.nuxeo.ecm.core.api.pathsegment.PathSegmentService; 066import org.nuxeo.ecm.core.api.repository.RepositoryManager; 067import org.nuxeo.ecm.core.api.security.ACE; 068import org.nuxeo.ecm.core.api.security.ACL; 069import org.nuxeo.ecm.core.api.security.ACP; 070import org.nuxeo.ecm.core.event.Event; 071import org.nuxeo.ecm.core.event.EventContext; 072import org.nuxeo.ecm.core.event.EventService; 073import org.nuxeo.ecm.core.event.impl.DocumentEventContext; 074import org.nuxeo.ecm.platform.rendering.api.RenderingException; 075import org.nuxeo.ecm.platform.usermanager.NuxeoPrincipalImpl; 076import org.nuxeo.ecm.platform.usermanager.UserConfig; 077import org.nuxeo.ecm.platform.usermanager.UserManager; 078import org.nuxeo.ecm.platform.usermanager.exceptions.UserAlreadyExistsException; 079import org.nuxeo.runtime.api.Framework; 080import org.nuxeo.runtime.model.ComponentInstance; 081import org.nuxeo.runtime.model.DefaultComponent; 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<String, UserRegistrationConfiguration>(); 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.getLocalService(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 { 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.getLocalService(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.getLocalService(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 targetPrincipal = userManager.getPrincipal((String) doc.getPropertyValue("userinfo:email")); 281 } 282 if (targetPrincipal != null) { 283 DocumentModel target = session.getDocument(new IdRef( 284 (String) doc.getPropertyValue("docinfo:documentId"))); 285 ACP acp = target.getACP(); 286 Map<String, Serializable> contextData = new HashMap<>(); 287 contextData.put("notify", true); 288 contextData.put("comment", doc.getPropertyValue("registration:comment")); 289 acp.addACE(ACL.LOCAL_ACL, 290 ACE.builder(targetPrincipal.getName(), (String) doc.getPropertyValue("docinfo:permission")) 291 .creator((String) doc.getPropertyValue("docinfo:creator")) 292 .contextData(contextData) 293 .build()); 294 target.setACP(acp, true); 295 // test Validation Method 296 } else if (StringUtils.equals(EMAIL.toString(), validationMethod)) { 297 sendValidationEmail(additionnalInfo, doc); 298 } 299 300 doc.setPropertyValue("registration:accepted", true); 301 if (doc.getAllowedStateTransitions().contains("approve")) { 302 doc.followTransition("approve"); 303 } 304 doc = session.saveDocument(doc); 305 session.save(); 306 307 sendEvent(session, doc, getNameEventRegistrationAccepted()); 308 } 309 } 310 311 protected class RegistrationRejector extends UnrestrictedSessionRunner { 312 313 protected String uuid; 314 315 protected Map<String, Serializable> additionnalInfo; 316 317 public RegistrationRejector(String registrationUuid, Map<String, Serializable> additionnalInfo) { 318 super(getTargetRepositoryName()); 319 uuid = registrationUuid; 320 this.additionnalInfo = additionnalInfo; 321 } 322 323 @Override 324 public void run() { 325 326 DocumentModel doc = session.getDocument(new IdRef(uuid)); 327 328 doc.setPropertyValue("registration:accepted", false); 329 if (doc.getAllowedStateTransitions().contains("reject")) { 330 doc.followTransition("reject"); 331 } 332 doc = session.saveDocument(doc); 333 session.save(); 334 335 sendEvent(session, doc, getNameEventRegistrationRejected()); 336 } 337 } 338 339 protected class RegistrationAcceptator extends UnrestrictedSessionRunner { 340 341 protected String uuid; 342 343 protected Map<String, Serializable> registrationData = new HashMap<String, Serializable>(); 344 345 protected Map<String, Serializable> additionnalInfo; 346 347 public RegistrationAcceptator(String uuid, Map<String, Serializable> additionnalInfo) { 348 super(getTargetRepositoryName()); 349 this.uuid = uuid; 350 this.additionnalInfo = additionnalInfo; 351 } 352 353 public Map<String, Serializable> getRegistrationData() { 354 return registrationData; 355 } 356 357 @Override 358 public void run() { 359 DocumentRef idRef = new IdRef(uuid); 360 361 DocumentModel registrationDoc = session.getDocument(idRef); 362 363 // additionnal infos 364 for (String key : additionnalInfo.keySet()) { 365 try { 366 if (DefaultInvitationUserFactory.PASSWORD_KEY.equals(key)) { 367 // add the password as a transient context data 368 registrationDoc.putContextData(DefaultInvitationUserFactory.PASSWORD_KEY, 369 additionnalInfo.get(key)); 370 } else { 371 registrationDoc.setPropertyValue(key, additionnalInfo.get(key)); 372 } 373 } catch (PropertyException e) { 374 // skip silently 375 } 376 } 377 378 if (registrationDoc.getLifeCyclePolicy().equals("registrationRequest")) { 379 if (registrationDoc.getCurrentLifeCycleState().equals("approved")) { 380 registrationDoc.followTransition("accept"); 381 } else { 382 if (registrationDoc.getCurrentLifeCycleState().equals("accepted")) { 383 throw new AlreadyProcessedRegistrationException( 384 "Registration request has already been processed"); 385 } else { 386 throw new UserRegistrationException("Registration request has not been accepted yet"); 387 } 388 } 389 } 390 391 session.saveDocument(registrationDoc); 392 session.save(); 393 EventContext evContext = sendEvent(session, registrationDoc, getNameEventRegistrationValidated()); 394 395 ((DocumentModelImpl) registrationDoc).detach(sessionIsAlreadyUnrestricted); 396 registrationData.put(REGISTRATION_DATA_DOC, registrationDoc); 397 registrationData.put(REGISTRATION_DATA_USER, evContext.getProperty("registeredUser")); 398 } 399 400 } 401 402 protected class RequestIdValidator extends UnrestrictedSessionRunner { 403 404 protected String uuid; 405 406 public RequestIdValidator(String uuid) { 407 super(getTargetRepositoryName()); 408 this.uuid = uuid; 409 } 410 411 @Override 412 public void run() { 413 DocumentRef idRef = new IdRef(uuid); 414 // Check if the id matches an existing document 415 if (!session.exists(idRef)) { 416 throw new UserRegistrationException("There is no existing registration request with id " + uuid); 417 } 418 419 // Check if the request has not been already validated 420 DocumentModel registrationDoc = session.getDocument(idRef); 421 if (registrationDoc.getCurrentLifeCycleState().equals("accepted")) { 422 throw new AlreadyProcessedRegistrationException("Registration request has already been processed"); 423 } 424 } 425 } 426 427 protected EventContext sendEvent(CoreSession session, DocumentModel source, String evName) 428 throws UserRegistrationException { 429 try { 430 EventService evService = Framework.getService(EventService.class); 431 EventContext evContext = new DocumentEventContext(session, session.getPrincipal(), source); 432 433 Event event = evContext.newEvent(evName); 434 435 evService.fireEvent(event); 436 437 return evContext; 438 } catch (UserRegistrationException ue) { 439 log.warn("Error during event processing", ue); 440 throw ue; 441 } 442 443 } 444 445 protected void sendValidationEmail(Map<String, Serializable> additionnalInfo, DocumentModel registrationDoc) 446 { 447 UserRegistrationConfiguration configuration = getConfiguration(registrationDoc); 448 sendEmail(additionnalInfo, registrationDoc, configuration.getValidationEmailTemplate(), 449 configuration.getValidationEmailTitle()); 450 } 451 452 protected void sendEmail(Map<String, Serializable> additionnalInfo, DocumentModel registrationDoc, 453 String emailTemplatePath, String emailTitle) { 454 UserRegistrationConfiguration configuration = getConfiguration(registrationDoc); 455 456 String emailAdress = (String) registrationDoc.getPropertyValue(configuration.getUserInfoEmailField()); 457 458 Map<String, Serializable> input = new HashMap<String, Serializable>(); 459 Map<String, Serializable> userinfo = new HashMap<String, Serializable>(); 460 userinfo.put("firstName", registrationDoc.getPropertyValue(configuration.getUserInfoFirstnameField())); 461 userinfo.put("lastName", registrationDoc.getPropertyValue(configuration.getUserInfoLastnameField())); 462 userinfo.put("login", registrationDoc.getPropertyValue(configuration.getUserInfoUsernameField())); 463 userinfo.put("id", registrationDoc.getId()); 464 465 String documentTitle = ""; 466 467 if (registrationDoc.hasSchema("docinfo")) { 468 documentTitle = (String) registrationDoc.getPropertyValue("docinfo:documentTitle"); 469 } 470 input.put("documentTitle", documentTitle); 471 input.put("configurationName", configuration.getName()); 472 input.put("comment", registrationDoc.getPropertyValue("registration:comment")); 473 input.put(UserInvitationService.REGISTRATION_CONFIGURATION_NAME, configuration.getName()); 474 input.put("userinfo", (Serializable) userinfo); 475 input.put("info", (Serializable) additionnalInfo); 476 input.put("userAlreadyExists", checkUserFromRegistrationExistence(registrationDoc)); 477 input.put("productName", Framework.getProperty("org.nuxeo.ecm.product.name")); 478 StringWriter writer = new StringWriter(); 479 480 try { 481 rh.getRenderingEngine().render(emailTemplatePath, input, writer); 482 } catch (RenderingException e) { 483 throw new NuxeoException("Error during rendering email", e); 484 } 485 486 // render custom email subject 487 emailTitle = renderSubjectTemplate(emailTitle, input); 488 489 String body = writer.getBuffer().toString(); 490 String copyTo = (String) registrationDoc.getPropertyValue("registration:copyTo"); 491 if (!isTestModeSet()) { 492 try { 493 generateMail(emailAdress, copyTo, emailTitle, body); 494 } catch (NamingException | MessagingException e) { 495 throw new NuxeoException("Error while sending mail: ", e); 496 } 497 } else { 498 testRendering = body; 499 } 500 } 501 502 private String renderSubjectTemplate(String emailTitle, Map<String, Serializable> input) { 503 Configuration stringCfg = rh.getEngineConfiguration(); 504 Writer out; 505 try { 506 Template templ = new Template("subjectTemplate", new StringReader(emailTitle), stringCfg); 507 out = new StringWriter(); 508 templ.process(input, out); 509 out.flush(); 510 } catch (IOException | TemplateException e) { 511 throw new NuxeoException("Error while rendering email subject: ", e); 512 } 513 return out.toString(); 514 } 515 516 protected static boolean isTestModeSet() { 517 return Framework.isTestModeSet() || !isBlank(Framework.getProperty("org.nuxeo.ecm.tester.name")); 518 } 519 520 protected boolean checkUserFromRegistrationExistence(DocumentModel registrationDoc) { 521 UserRegistrationConfiguration configuration = getConfiguration(registrationDoc); 522 return null != Framework.getLocalService(UserManager.class).getPrincipal( 523 (String) registrationDoc.getPropertyValue(configuration.getUserInfoUsernameField())); 524 } 525 526 protected void generateMail(String destination, String copy, String title, String content) throws NamingException, 527 MessagingException { 528 529 InitialContext ic = new InitialContext(); 530 Session session = (Session) ic.lookup(getJavaMailJndiName()); 531 532 MimeMessage msg = new MimeMessage(session); 533 msg.setFrom(new InternetAddress(session.getProperty("mail.from"))); 534 msg.setRecipients(Message.RecipientType.TO, InternetAddress.parse(destination, false)); 535 if (!isBlank(copy)) { 536 msg.addRecipient(Message.RecipientType.CC, new InternetAddress(copy, false)); 537 } 538 539 msg.setSubject(title, "UTF-8"); 540 msg.setSentDate(new Date()); 541 msg.setContent(content, "text/html; charset=utf-8"); 542 543 Transport.send(msg); 544 } 545 546 @Override 547 public String submitRegistrationRequest(DocumentModel userRegistrationModel, 548 Map<String, Serializable> additionnalInfo, ValidationMethod validationMethod, boolean autoAccept) 549 { 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.getLocalService(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 { 590 RegistrationCreator creator = new RegistrationCreator(configurationName, userRegistrationModel, 591 additionnalInfo, validationMethod); 592 creator.runUnrestricted(); 593 String registrationUuid = creator.getRegistrationUuid(); 594 595 UserRegistrationConfiguration currentConfig = getConfiguration(configurationName); 596 boolean userAlreadyExists = null != Framework.getLocalService(UserManager.class).getPrincipal( 597 (String) userRegistrationModel.getPropertyValue(currentConfig.getUserInfoUsernameField())); 598 599 if (!userAlreadyExists && isEmailExist(currentConfig, userRegistrationModel)) { 600 log.info("Trying to submit a registration from an existing email with a different username."); 601 throw new UserAlreadyExistsException(); 602 } 603 604 // Directly accept registration if the configuration allow it and the 605 // user already exists 606 RegistrationRules registrationRules = getRegistrationRules(configurationName); 607 boolean byPassAdminValidation = autoAccept; 608 byPassAdminValidation |= userAlreadyExists && registrationRules.allowDirectValidationForExistingUser(); 609 byPassAdminValidation |= registrationRules.allowDirectValidationForExistingUser() 610 && registrationRules.allowDirectValidationForNonExistingUser(); 611 if (byPassAdminValidation) { 612 // Build validationBaseUrl with nuxeo.url property as request is not 613 // accessible. 614 if (!additionnalInfo.containsKey("enterPasswordUrl")) { 615 additionnalInfo.put("enterPasswordUrl", buildEnterPasswordUrl(currentConfig)); 616 } 617 acceptRegistrationRequest(registrationUuid, additionnalInfo); 618 } 619 return registrationUuid; 620 } 621 622 protected String buildEnterPasswordUrl(UserRegistrationConfiguration configuration) { 623 String baseUrl = Framework.getProperty(NUXEO_URL_KEY); 624 625 baseUrl = isBlank(baseUrl) ? "/" : baseUrl; 626 if (!baseUrl.endsWith("/")) { 627 baseUrl += "/"; 628 } 629 return baseUrl.concat(configuration.getEnterPasswordUrl()); 630 } 631 632 @Override 633 public void acceptRegistrationRequest(String requestId, Map<String, Serializable> additionnalInfo) 634 throws UserRegistrationException { 635 RegistrationApprover acceptor = new RegistrationApprover(requestId, additionnalInfo); 636 acceptor.runUnrestricted(); 637 638 } 639 640 @Override 641 public void rejectRegistrationRequest(String requestId, Map<String, Serializable> additionnalInfo) 642 throws UserRegistrationException { 643 644 RegistrationRejector rejector = new RegistrationRejector(requestId, additionnalInfo); 645 rejector.runUnrestricted(); 646 647 } 648 649 @Override 650 public Map<String, Serializable> validateRegistration(String requestId, Map<String, Serializable> additionnalInfo) 651 throws UserRegistrationException { 652 RegistrationAcceptator validator = new RegistrationAcceptator(requestId, additionnalInfo); 653 validator.runUnrestricted(); 654 return validator.getRegistrationData(); 655 } 656 657 @Override 658 public Map<String, Serializable> validateRegistrationAndSendEmail(String requestId, 659 Map<String, Serializable> additionnalInfo) throws UserRegistrationException { 660 661 Map<String, Serializable> registrationInfo = validateRegistration(requestId, additionnalInfo); 662 663 Map<String, Serializable> input = new HashMap<String, Serializable>(); 664 input.putAll(registrationInfo); 665 input.put("info", (Serializable) additionnalInfo); 666 StringWriter writer = new StringWriter(); 667 668 UserRegistrationConfiguration configuration = getConfiguration((DocumentModel) registrationInfo.get(REGISTRATION_DATA_DOC)); 669 try { 670 rh.getRenderingEngine().render(configuration.getSuccessEmailTemplate(), input, writer); 671 } catch (RenderingException e) { 672 throw new NuxeoException("Error during rendering email", e); 673 } 674 675 String emailAdress = ((NuxeoPrincipalImpl) registrationInfo.get("registeredUser")).getEmail(); 676 String body = writer.getBuffer().toString(); 677 String title = configuration.getValidationEmailTitle(); 678 if (!Framework.isTestModeSet()) { 679 try { 680 generateMail(emailAdress, null, title, body); 681 } catch (NamingException | MessagingException e) { 682 throw new NuxeoException("Error while sending mail : ", e); 683 } 684 } else { 685 testRendering = body; 686 } 687 688 return registrationInfo; 689 } 690 691 @Override 692 public void registerContribution(Object contribution, String extensionPoint, ComponentInstance contributor) { 693 if ("configuration".equals(extensionPoint)) { 694 UserRegistrationConfiguration newConfig = (UserRegistrationConfiguration) contribution; 695 696 if (configurations.containsKey(newConfig.getName())) { 697 if (newConfig.isMerge()) { 698 configurations.get(newConfig.getName()).mergeWith(newConfig); 699 } else if (newConfig.isRemove()) { 700 configurations.remove(newConfig.getName()); 701 } else { 702 log.warn("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) throws 731 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 { 802 for (DocumentModel registrationDoc : registrationDocs) { 803 reviveRegistrationRequest(session, registrationDoc, new HashMap<String, Serializable>()); 804 } 805 } 806 807 protected void reviveRegistrationRequest(CoreSession session, DocumentModel registrationDoc, 808 Map<String, Serializable> additionalInfos) { 809 UserRegistrationConfiguration configuration = getConfiguration(registrationDoc); 810 // Build validationBaseUrl with nuxeo.url property as request is not 811 // accessible. 812 if (!additionalInfos.containsKey("enterPasswordUrl")) { 813 additionalInfos.put("enterPasswordUrl", buildEnterPasswordUrl(configuration)); 814 } 815 sendEmail(additionalInfos, registrationDoc, configuration.getReviveEmailTemplate(), 816 configuration.getReviveEmailTitle()); 817 } 818 819 @Override 820 public void deleteRegistrationRequests(CoreSession session, List<DocumentModel> registrationDocs) 821 { 822 for (DocumentModel registration : registrationDocs) { 823 UserRegistrationConfiguration configuration = getConfiguration(registration); 824 if (!registration.hasSchema(configuration.getUserInfoSchemaName())) { 825 throw new NuxeoException("Registration document do not contains needed schema"); 826 } 827 828 session.removeDocument(registration.getRef()); 829 } 830 } 831 832 @Override 833 public Set<String> getConfigurationsName() { 834 return configurations.keySet(); 835 } 836 837 @Override 838 public void checkRequestId(final String requestId) throws UserRegistrationException { 839 RequestIdValidator runner = new RequestIdValidator(requestId); 840 runner.runUnrestricted(); 841 } 842 843 @Override 844 public String getNameEventRegistrationSubmitted() { 845 return INVITATION_SUBMITTED_EVENT; 846 } 847 848 @Override 849 public String getNameEventRegistrationAccepted() { 850 return INVITATION_ACCEPTED_EVENT; 851 } 852 853 @Override 854 public String getNameEventRegistrationRejected() { 855 return INVITATION_REJECTED_EVENT; 856 } 857 858 @Override 859 public String getNameEventRegistrationValidated() { 860 return INVITATION_VALIDATED_EVENT; 861 } 862 863}