001/* 002 * (C) Copyright 2011-2018 Nuxeo (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * Contributors: 016 * Nuxeo - initial API and implementation 017 */ 018 019package org.nuxeo.ecm.user.registration; 020 021import java.io.Serializable; 022import java.io.StringWriter; 023import java.util.HashMap; 024import java.util.Map; 025 026import javax.mail.MessagingException; 027import javax.naming.NamingException; 028 029import org.apache.commons.lang3.StringUtils; 030import org.nuxeo.common.utils.IdUtils; 031import org.nuxeo.ecm.core.api.CoreSession; 032import org.nuxeo.ecm.core.api.DocumentModel; 033import org.nuxeo.ecm.core.api.NuxeoException; 034import org.nuxeo.ecm.core.api.PropertyException; 035import org.nuxeo.ecm.core.api.UnrestrictedSessionRunner; 036import org.nuxeo.ecm.core.api.security.ACE; 037import org.nuxeo.ecm.core.api.security.ACL; 038import org.nuxeo.ecm.core.api.security.ACP; 039import org.nuxeo.ecm.core.api.security.SecurityConstants; 040import org.nuxeo.ecm.core.api.security.impl.ACLImpl; 041import org.nuxeo.ecm.core.api.security.impl.ACPImpl; 042import org.nuxeo.ecm.platform.rendering.api.RenderingException; 043import org.nuxeo.ecm.platform.usermanager.NuxeoPrincipalImpl; 044import org.nuxeo.ecm.platform.usermanager.UserManager; 045import org.nuxeo.ecm.user.invite.RegistrationRules; 046import org.nuxeo.ecm.user.invite.UserInvitationComponent; 047import org.nuxeo.ecm.user.invite.UserRegistrationConfiguration; 048import org.nuxeo.ecm.user.invite.UserRegistrationException; 049import org.nuxeo.ecm.user.invite.UserRegistrationInfo; 050import org.nuxeo.runtime.api.Framework; 051 052public class UserRegistrationComponent extends UserInvitationComponent implements UserRegistrationService { 053 054 private static final String REGISTRATION_SUBMITTED_EVENT = "registrationSubmitted"; 055 056 private static final String REGISTRATION_ACCEPTED_EVENT = "registrationAccepted"; 057 058 private static final String REGISTRATION_REJECTED_EVENT = "registrationRejected"; 059 060 private static final String REGISTRATION_VALIDATED_EVENT = "registrationValidated"; 061 062 protected class RegistrationCreator extends UnrestrictedSessionRunner { 063 064 protected UserRegistrationInfo userInfo; 065 066 protected DocumentRegistrationInfo docInfo; 067 068 protected Map<String, Serializable> additionnalInfo; 069 070 protected String registrationUuid; 071 072 protected String principalName; 073 074 protected ValidationMethod validationMethod; 075 076 protected UserRegistrationConfiguration configuration; 077 078 public String getRegistrationUuid() { 079 return registrationUuid; 080 } 081 082 public RegistrationCreator(String configurationName, UserRegistrationInfo userInfo, 083 DocumentRegistrationInfo docInfo, Map<String, Serializable> additionnalInfo, 084 ValidationMethod validationMethod, String principalName) { 085 super(getTargetRepositoryName()); 086 this.userInfo = userInfo; 087 this.additionnalInfo = additionnalInfo; 088 this.validationMethod = validationMethod; 089 this.docInfo = docInfo; 090 this.configuration = getConfiguration(configurationName); 091 this.principalName = principalName; 092 } 093 094 @Override 095 public void run() { 096 097 // Check if login is defined - if not define it with email 098 userInfo.setLogin(userInfo.getLogin() == null ? userInfo.getEmail() : userInfo.getLogin()); 099 100 String title = "registration request for " + userInfo.getLogin() + " (" + userInfo.getEmail() + " " 101 + userInfo.getCompany() + ") "; 102 String name = IdUtils.generateId(title + "-" + System.currentTimeMillis(), "-", true, 24); 103 104 String targetPath = getOrCreateRootDocument(session, configuration.getName()).getPathAsString(); 105 106 DocumentModel doc = session.createDocumentModel(configuration.getRequestDocType()); 107 doc.setPathInfo(targetPath, name); 108 doc.setPropertyValue("dc:title", title); 109 110 // store userinfo 111 doc.setPropertyValue(configuration.getUserInfoUsernameField(), userInfo.getLogin()); 112 doc.setPropertyValue(configuration.getUserInfoFirstnameField(), userInfo.getFirstName()); 113 doc.setPropertyValue(configuration.getUserInfoLastnameField(), userInfo.getLastName()); 114 doc.setPropertyValue(configuration.getUserInfoEmailField(), userInfo.getEmail()); 115 doc.setPropertyValue(configuration.getUserInfoCompanyField(), userInfo.getCompany()); 116 doc.setPropertyValue(configuration.getUserInfoGroupsField(), (Serializable) userInfo.getGroups()); 117 doc.setPropertyValue(configuration.getUserInfoTenantIdField(), userInfo.getTenantId()); 118 119 // validation method 120 doc.setPropertyValue("registration:validationMethod", validationMethod.toString()); 121 122 // Document info 123 doc.setPropertyValue(DocumentRegistrationInfo.DOCUMENT_ID_FIELD, docInfo.getDocumentId()); 124 doc.setPropertyValue(DocumentRegistrationInfo.DOCUMENT_RIGHT_FIELD, docInfo.getPermission()); 125 doc.setPropertyValue(DocumentRegistrationInfo.DOCUMENT_TITLE_FIELD, docInfo.getDocumentTitle()); 126 doc.setPropertyValue(DocumentRegistrationInfo.DOCUMENT_BEGIN_FIELD, docInfo.getBegin()); 127 doc.setPropertyValue(DocumentRegistrationInfo.DOCUMENT_END_FIELD, docInfo.getEnd()); 128 129 // additionnal infos 130 for (String key : additionnalInfo.keySet()) { 131 try { 132 doc.setPropertyValue(key, additionnalInfo.get(key)); 133 } catch (PropertyException e) { 134 // skip silently 135 } 136 } 137 138 doc = session.createDocument(doc); 139 140 // Set the ACP for the UserRegistration object 141 ACP acp = new ACPImpl(); 142 ACE denyEverything = new ACE(SecurityConstants.EVERYONE, SecurityConstants.EVERYTHING, false); 143 ACE allowEverything = new ACE(principalName, SecurityConstants.EVERYTHING, true); 144 ACL acl = new ACLImpl(); 145 acl.setACEs(new ACE[] { allowEverything, denyEverything }); 146 acp.addACL(acl); 147 doc.setACP(acp, true); 148 149 registrationUuid = doc.getId(); 150 151 sendEvent(session, doc, getNameEventRegistrationSubmitted()); 152 153 session.save(); 154 } 155 156 } 157 158 @Override 159 public String submitRegistrationRequest(UserRegistrationInfo userInfo, Map<String, Serializable> additionnalInfo, 160 ValidationMethod validationMethod, boolean autoAccept, String principalName) { 161 return submitRegistrationRequest(CONFIGURATION_NAME, userInfo, new DocumentRegistrationInfo(), additionnalInfo, 162 validationMethod, autoAccept, principalName); 163 } 164 165 @Override 166 public String submitRegistrationRequest(String configurationName, UserRegistrationInfo userInfo, 167 DocumentRegistrationInfo docInfo, Map<String, Serializable> additionnalInfo, 168 ValidationMethod validationMethod, boolean autoAccept, String principalName) 169 throws UserRegistrationException { 170 171 // First check that we have the originating user for that request 172 if (StringUtils.isEmpty((String) additionnalInfo.get(PARAM_ORIGINATING_USER))) { 173 throw new IllegalArgumentException("Originating user should be provided in a registration request"); 174 } 175 176 RegistrationCreator creator = new RegistrationCreator(configurationName, userInfo, docInfo, additionnalInfo, 177 validationMethod, principalName); 178 creator.runUnrestricted(); 179 String registrationUuid = creator.getRegistrationUuid(); 180 181 boolean userAlreadyExists = null != Framework.getService(UserManager.class).getPrincipal(userInfo.getLogin()); 182 // Directly accept registration if the configuration allow it and the 183 // user already exists 184 RegistrationRules registrationRules = getRegistrationRules(configurationName); 185 boolean byPassAdminValidation = autoAccept; 186 byPassAdminValidation |= userAlreadyExists && registrationRules.allowDirectValidationForExistingUser(); 187 byPassAdminValidation |= !userAlreadyExists && registrationRules.allowDirectValidationForNonExistingUser(); 188 if (byPassAdminValidation) { 189 // Build validationBaseUrl with nuxeo.url property as request is 190 // not accessible. 191 if (!additionnalInfo.containsKey("enterPasswordUrl")) { 192 String baseUrl = Framework.getProperty(NUXEO_URL_KEY); 193 194 baseUrl = StringUtils.isBlank(baseUrl) ? "/" : baseUrl; 195 if (!baseUrl.endsWith("/")) { 196 baseUrl += "/"; 197 } 198 String enterPasswordUrl = getConfiguration(configurationName).getEnterPasswordUrl(); 199 if (enterPasswordUrl.startsWith("/")) { 200 enterPasswordUrl = enterPasswordUrl.substring(1); 201 } 202 additionnalInfo.put("enterPasswordUrl", baseUrl.concat(enterPasswordUrl)); 203 } 204 acceptRegistrationRequest(registrationUuid, additionnalInfo); 205 } 206 return registrationUuid; 207 } 208 209 @Override 210 public Map<String, Serializable> validateRegistrationAndSendEmail(String requestId, 211 Map<String, Serializable> additionnalInfo) throws UserRegistrationException { 212 213 Map<String, Serializable> registrationInfo = validateRegistration(requestId, additionnalInfo); 214 215 Map<String, Serializable> input = new HashMap<>(); 216 input.putAll(registrationInfo); 217 input.put("info", (Serializable) additionnalInfo); 218 StringWriter writer = new StringWriter(); 219 220 UserRegistrationConfiguration configuration = getConfiguration( 221 (DocumentModel) registrationInfo.get(REGISTRATION_DATA_DOC)); 222 try { 223 rh.getRenderingEngine().render(configuration.getSuccessEmailTemplate(), input, writer); 224 } catch (RenderingException e) { 225 throw new NuxeoException("Error during rendering email", e); 226 } 227 228 String emailAdress = ((NuxeoPrincipalImpl) registrationInfo.get("registeredUser")).getEmail(); 229 String body = writer.getBuffer().toString(); 230 String title = configuration.getValidationEmailTitle(); 231 if (!Framework.isTestModeSet()) { 232 try { 233 generateMail(emailAdress, null, title, body); 234 } catch (NamingException | MessagingException e) { 235 throw new NuxeoException("Error while sending mail", e); 236 } 237 } else { 238 testRendering = body; 239 } 240 241 return registrationInfo; 242 } 243 244 @Override 245 public void addRightsOnDoc(CoreSession session, DocumentModel registrationDoc) { 246 UserRegistrationConfiguration configuration = getConfiguration(registrationDoc); 247 DocumentModel document = ((DefaultRegistrationUserFactory) getRegistrationUserFactory( 248 configuration)).doAddDocumentPermission(session, registrationDoc, configuration); 249 if (document != null) { 250 ((RegistrationUserFactory) getRegistrationUserFactory(configuration)).doPostAddDocumentPermission(session, 251 registrationDoc, document); 252 } 253 } 254 255 @Override 256 public String getNameEventRegistrationSubmitted() { 257 return REGISTRATION_SUBMITTED_EVENT; 258 } 259 260 @Override 261 public String getNameEventRegistrationAccepted() { 262 return REGISTRATION_ACCEPTED_EVENT; 263 } 264 265 @Override 266 public String getNameEventRegistrationRejected() { 267 return REGISTRATION_REJECTED_EVENT; 268 } 269 270 @Override 271 public String getNameEventRegistrationValidated() { 272 return REGISTRATION_VALIDATED_EVENT; 273 } 274}