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.core.api.event.CoreEventConstants.DOCUMENT_REFS;
021import static org.nuxeo.ecm.core.api.event.CoreEventConstants.REPOSITORY_NAME;
022import static org.nuxeo.ecm.core.api.event.DocumentEventTypes.ACE_STATUS_UPDATED;
023import static org.nuxeo.ecm.core.api.security.ACL.INHERITED_ACL;
024import static org.nuxeo.ecm.permissions.Constants.ACE_INFO_COMMENT;
025import static org.nuxeo.ecm.permissions.Constants.ACE_INFO_DIRECTORY;
026import static org.nuxeo.ecm.permissions.Constants.ACE_INFO_NOTIFIED;
027import static org.nuxeo.ecm.permissions.Constants.ACE_INFO_NOTIFY;
028import static org.nuxeo.ecm.permissions.Constants.ACE_KEY;
029import static org.nuxeo.ecm.permissions.Constants.ACL_NAME_KEY;
030import static org.nuxeo.ecm.permissions.Constants.COMMENT_KEY;
031import static org.nuxeo.ecm.permissions.Constants.PERMISSION_NOTIFICATION_EVENT;
032import static org.nuxeo.ecm.permissions.PermissionHelper.computeDirectoryId;
033
034import java.util.List;
035
036import org.nuxeo.ecm.core.api.CoreInstance;
037import org.nuxeo.ecm.core.api.CoreSession;
038import org.nuxeo.ecm.core.api.DocumentModel;
039import org.nuxeo.ecm.core.api.DocumentRef;
040import org.nuxeo.ecm.core.api.security.ACE;
041import org.nuxeo.ecm.core.api.security.ACL;
042import org.nuxeo.ecm.core.api.security.ACP;
043import org.nuxeo.ecm.core.event.Event;
044import org.nuxeo.ecm.core.event.EventBundle;
045import org.nuxeo.ecm.core.event.EventContext;
046import org.nuxeo.ecm.core.event.EventService;
047import org.nuxeo.ecm.core.event.PostCommitFilteringEventListener;
048import org.nuxeo.ecm.core.event.impl.DocumentEventContext;
049import org.nuxeo.ecm.directory.Session;
050import org.nuxeo.ecm.directory.api.DirectoryService;
051import org.nuxeo.runtime.api.Framework;
052
053/**
054 * Listener listening for {@code ACEStatusUpdated} event to send a notification for ACEs becoming effective.
055 *
056 * @since 7.4
057 */
058public class ACEStatusUpdatedListener implements PostCommitFilteringEventListener {
059
060    @Override
061    public void handleEvent(EventBundle events) {
062        for (Event event : events) {
063            handleEvent(event);
064        }
065    }
066
067    @SuppressWarnings("unchecked")
068    protected void handleEvent(Event event) {
069        EventContext ctx = event.getContext();
070        String repositoryName = (String) ctx.getProperty(REPOSITORY_NAME);
071        List<DocumentRef> docRefs = (List<DocumentRef>) ctx.getProperty(DOCUMENT_REFS);
072        if (repositoryName == null || docRefs == null) {
073            return;
074        }
075
076        try (CoreSession session = CoreInstance.openCoreSessionSystem(repositoryName)) {
077
078            for (DocumentRef ref : docRefs) {
079                if (session.exists(ref)) {
080                    DocumentModel doc = session.getDocument(ref);
081                    checkForEffectiveACE(session, doc);
082                }
083            }
084        }
085    }
086
087    protected void checkForEffectiveACE(CoreSession session, DocumentModel doc) {
088        DirectoryService directoryService = Framework.getService(DirectoryService.class);
089        ACP acp = doc.getACP();
090        for (ACL acl : acp.getACLs()) {
091            String aclName = acl.getName();
092            if (INHERITED_ACL.equals(aclName)) {
093                // ignore inherited acl
094                continue;
095            }
096
097            for (ACE ace : acl) {
098                if (ace.isGranted() && ace.isEffective()) {
099                    try (Session dirSession = directoryService.open(ACE_INFO_DIRECTORY)) {
100                        String id = computeDirectoryId(doc, acl.getName(), ace.getId());
101                        DocumentModel entry = dirSession.getEntry(id);
102                        if (entry != null) {
103                            boolean notify = (boolean) entry.getPropertyValue(ACE_INFO_NOTIFY);
104                            boolean notified = (boolean) entry.getPropertyValue(ACE_INFO_NOTIFIED);
105                            String comment = (String) entry.getPropertyValue(ACE_INFO_COMMENT);
106                            if (notify && !notified) {
107                                // send the event for the notification
108                                if (comment != null) {
109                                    ace.putContextData(COMMENT_KEY, comment);
110                                }
111                                firePermissionNotificationEvent(session, doc, aclName, ace);
112                            }
113                        }
114                    }
115                }
116            }
117        }
118    }
119
120    protected void firePermissionNotificationEvent(CoreSession session, DocumentModel doc, String aclName, ACE ace) {
121        DocumentEventContext docCtx = new DocumentEventContext(session, session.getPrincipal(), doc);
122        docCtx.setProperty(ACE_KEY, ace);
123        docCtx.setProperty(ACL_NAME_KEY, aclName);
124        EventService eventService = Framework.getService(EventService.class);
125        eventService.fireEvent(PERMISSION_NOTIFICATION_EVENT, docCtx);
126    }
127
128    @Override
129    public boolean acceptEvent(Event event) {
130        return ACE_STATUS_UPDATED.equals(event.getName());
131    }
132}