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}