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