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