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