001/*
002 * (C) Copyright 2015 Nuxeo SA (http://nuxeo.com/) and others.
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 *
014 * Contributors:
015 *     Thomas Roger
016 */
017
018package org.nuxeo.ecm.permissions;
019
020import static org.nuxeo.ecm.permissions.Constants.ACE_GRANTED_TEMPLATE;
021import static org.nuxeo.ecm.permissions.Constants.ACE_INFO_COMMENT;
022import static org.nuxeo.ecm.permissions.Constants.ACE_INFO_DIRECTORY;
023import static org.nuxeo.ecm.permissions.Constants.ACE_KEY;
024import static org.nuxeo.ecm.permissions.Constants.ACL_NAME_KEY;
025import static org.nuxeo.ecm.permissions.Constants.PERMISSION_NOTIFICATION_EVENT;
026
027import java.util.Collections;
028
029import org.apache.commons.lang.StringEscapeUtils;
030import org.apache.commons.logging.Log;
031import org.apache.commons.logging.LogFactory;
032import org.nuxeo.ecm.automation.AutomationService;
033import org.nuxeo.ecm.automation.OperationChain;
034import org.nuxeo.ecm.automation.OperationContext;
035import org.nuxeo.ecm.automation.OperationException;
036import org.nuxeo.ecm.automation.core.operations.notification.SendMail;
037import org.nuxeo.ecm.automation.core.scripting.Expression;
038import org.nuxeo.ecm.automation.core.scripting.Scripting;
039import org.nuxeo.ecm.automation.core.util.StringList;
040import org.nuxeo.ecm.automation.features.PlatformFunctions;
041import org.nuxeo.ecm.core.api.CoreSession;
042import org.nuxeo.ecm.core.api.DocumentModel;
043import org.nuxeo.ecm.core.api.NuxeoGroup;
044import org.nuxeo.ecm.core.api.NuxeoPrincipal;
045import org.nuxeo.ecm.core.api.security.ACE;
046import org.nuxeo.ecm.core.event.Event;
047import org.nuxeo.ecm.core.event.EventBundle;
048import org.nuxeo.ecm.core.event.EventContext;
049import org.nuxeo.ecm.core.event.PostCommitFilteringEventListener;
050import org.nuxeo.ecm.core.event.impl.DocumentEventContext;
051import org.nuxeo.ecm.directory.Session;
052import org.nuxeo.ecm.directory.api.DirectoryService;
053import org.nuxeo.ecm.platform.ec.notification.service.NotificationService;
054import org.nuxeo.ecm.platform.ec.notification.service.NotificationServiceHelper;
055import org.nuxeo.ecm.platform.ui.web.tag.fn.Functions;
056import org.nuxeo.ecm.platform.usermanager.UserManager;
057import org.nuxeo.runtime.api.Framework;
058
059/**
060 * Listener sending an email notification for a granted ACE.
061 * <p>
062 * This listener checks only if the ACE is granted. It assumes that other checks (such as the ACE becomes effective)
063 * have been done before.
064 *
065 * @since 7.4
066 */
067public class PermissionGrantedNotificationListener implements PostCommitFilteringEventListener {
068
069    private static final Log log = LogFactory.getLog(PermissionGrantedNotificationListener.class);
070
071    public static final String SUBJECT_FORMAT = "%s %s %s";
072
073    @Override
074    public void handleEvent(EventBundle events) {
075        for (Event event : events) {
076            handleEvent(event);
077        }
078    }
079
080    protected void handleEvent(Event event) {
081        EventContext eventCtx = event.getContext();
082        if (!(eventCtx instanceof DocumentEventContext)) {
083            return;
084        }
085
086        DocumentEventContext docCtx = (DocumentEventContext) eventCtx;
087        CoreSession coreSession = docCtx.getCoreSession();
088        DocumentModel doc = docCtx.getSourceDocument();
089        if (doc == null || !coreSession.exists(doc.getRef())) {
090            return;
091        }
092
093        ACE ace = (ACE) docCtx.getProperty(ACE_KEY);
094        String aclName = (String) docCtx.getProperty(ACL_NAME_KEY);
095        if (ace == null || ace.isDenied() || aclName == null) {
096            return;
097        }
098
099        StringList to = getRecipients(ace.getUsername());
100        if (to == null) {
101            // no recipient
102            return;
103        }
104
105        Expression from = Scripting.newExpression("Env[\"mail.from\"]");
106        NotificationService notificationService = NotificationServiceHelper.getNotificationService();
107        String subject = String.format(SUBJECT_FORMAT, notificationService.getEMailSubjectPrefix(),
108                "New permission on", doc.getTitle());
109
110        DirectoryService directoryService = Framework.getService(DirectoryService.class);
111        try (Session session = directoryService.open(ACE_INFO_DIRECTORY)) {
112            String id = PermissionHelper.computeDirectoryId(doc, aclName, ace.getId());
113            DocumentModel entry = session.getEntry(id);
114
115            OperationContext ctx = new OperationContext(coreSession);
116            ctx.setInput(doc);
117            ctx.put("ace", ace);
118            if (entry != null) {
119                String comment = (String) entry.getPropertyValue(ACE_INFO_COMMENT);
120                if (comment != null) {
121                    comment = StringEscapeUtils.escapeHtml(comment);
122                    comment = comment.replaceAll("\n", "<br/>");
123                    ctx.put("comment", comment);
124                }
125            }
126            String aceCreator = ace.getCreator();
127            if (aceCreator != null) {
128                UserManager userManager = Framework.getService(UserManager.class);
129                NuxeoPrincipal creator = userManager.getPrincipal(aceCreator);
130                if (creator != null) {
131                    ctx.put("aceCreator",
132                            String.format("%s (%s)", Functions.principalFullName(creator), creator.getName()));
133                }
134            }
135
136            OperationChain chain = new OperationChain("SendMail");
137            chain.add(SendMail.ID)
138                 .set("from", from)
139                 .set("to", to)
140                 .set("HTML", true)
141                 .set("subject", subject)
142                 .set("message", ACE_GRANTED_TEMPLATE);
143            Framework.getService(AutomationService.class).run(ctx, chain);
144        } catch (OperationException e) {
145            log.warn("Unable to notify user", e);
146            log.debug(e, e);
147        }
148    }
149
150    protected StringList getRecipients(String username) {
151        UserManager userManager = Framework.getService(UserManager.class);
152        NuxeoPrincipal principal = userManager.getPrincipal(username);
153        StringList to = null;
154        if (principal != null) {
155            to = new StringList(Collections.singletonList(principal.getEmail()));
156        } else {
157            NuxeoGroup group = userManager.getGroup(username);
158            if (group != null) {
159                PlatformFunctions platformFunctions = new PlatformFunctions();
160                to = platformFunctions.getEmailsFromGroup(group.getName());
161            }
162        }
163        return to;
164    }
165
166    @Override
167    public boolean acceptEvent(Event event) {
168        String eventName = event.getName();
169        return PERMISSION_NOTIFICATION_EVENT.equals(eventName);
170    }
171}