001/*
002 * (C) Copyright 2015 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 *
016 * Contributors:
017 *     Thomas Roger
018 */
019
020package org.nuxeo.ecm.permissions;
021
022import static org.nuxeo.ecm.core.api.event.CoreEventConstants.CHANGED_ACL_NAME;
023import static org.nuxeo.ecm.core.api.event.CoreEventConstants.DOCUMENT_REFS;
024import static org.nuxeo.ecm.core.api.event.CoreEventConstants.REPOSITORY_NAME;
025import static org.nuxeo.ecm.core.api.event.DocumentEventTypes.ACE_STATUS_UPDATED;
026import static org.nuxeo.ecm.permissions.Constants.ACE_INFO_COMMENT;
027import static org.nuxeo.ecm.permissions.Constants.ACE_INFO_DIRECTORY;
028import static org.nuxeo.ecm.permissions.Constants.ACE_INFO_NOTIFY;
029import static org.nuxeo.ecm.permissions.Constants.ACE_KEY;
030import static org.nuxeo.ecm.permissions.Constants.ACL_NAME_KEY;
031import static org.nuxeo.ecm.permissions.Constants.COMMENT_KEY;
032import static org.nuxeo.ecm.permissions.Constants.PERMISSION_NOTIFICATION_EVENT;
033import static org.nuxeo.ecm.permissions.PermissionHelper.computeDirectoryId;
034
035import java.util.List;
036import java.util.Map;
037
038import org.nuxeo.ecm.core.api.CloseableCoreSession;
039import org.nuxeo.ecm.core.api.CoreInstance;
040import org.nuxeo.ecm.core.api.CoreSession;
041import org.nuxeo.ecm.core.api.DocumentModel;
042import org.nuxeo.ecm.core.api.DocumentRef;
043import org.nuxeo.ecm.core.api.security.ACE;
044import org.nuxeo.ecm.core.event.Event;
045import org.nuxeo.ecm.core.event.EventBundle;
046import org.nuxeo.ecm.core.event.EventContext;
047import org.nuxeo.ecm.core.event.EventService;
048import org.nuxeo.ecm.core.event.PostCommitFilteringEventListener;
049import org.nuxeo.ecm.core.event.impl.DocumentEventContext;
050import org.nuxeo.ecm.directory.Session;
051import org.nuxeo.ecm.directory.api.DirectoryService;
052import org.nuxeo.runtime.api.Framework;
053
054/**
055 * Listener listening for {@code ACEStatusUpdated} event to send a notification for ACEs becoming effective.
056 *
057 * @since 7.4
058 */
059public class ACEStatusUpdatedListener implements PostCommitFilteringEventListener {
060
061    @Override
062    public void handleEvent(EventBundle events) {
063        for (Event event : events) {
064            handleEvent(event);
065        }
066    }
067
068    @SuppressWarnings("unchecked")
069    protected void handleEvent(Event event) {
070        EventContext ctx = event.getContext();
071        String repositoryName = (String) ctx.getProperty(REPOSITORY_NAME);
072        Map<DocumentRef, List<ACE>> refsToACEs = (Map<DocumentRef, List<ACE>>) ctx.getProperty(DOCUMENT_REFS);
073        if (repositoryName == null || refsToACEs == null) {
074            return;
075        }
076
077        try (CloseableCoreSession session = CoreInstance.openCoreSessionSystem(repositoryName)) {
078            refsToACEs.keySet().stream().filter(session::exists).forEach(ref -> {
079                DocumentModel doc = session.getDocument(ref);
080                checkForEffectiveACE(session, doc, refsToACEs.get(ref));
081            });
082        }
083    }
084
085    protected void checkForEffectiveACE(CoreSession session, DocumentModel doc, List<ACE> aces) {
086        DirectoryService directoryService = Framework.getService(DirectoryService.class);
087
088        for (ACE ace : aces) {
089            if (!ace.isGranted()) {
090                continue;
091            }
092
093            switch (ace.getStatus()) {
094            case EFFECTIVE:
095                String aclName = (String) ace.getContextData(CHANGED_ACL_NAME);
096                if (aclName == null) {
097                    continue;
098                }
099                Framework.doPrivileged(() -> {
100                    try (Session dirSession = directoryService.open(ACE_INFO_DIRECTORY)) {
101                        String id = computeDirectoryId(doc, aclName, ace.getId());
102                        DocumentModel entry = dirSession.getEntry(id);
103                        if (entry != null) {
104                            Boolean notify = (Boolean) entry.getPropertyValue(ACE_INFO_NOTIFY);
105                            String comment = (String) entry.getPropertyValue(ACE_INFO_COMMENT);
106                            if (Boolean.TRUE.equals(notify)) {
107                                // send the event for the notification
108                                ace.putContextData(COMMENT_KEY, comment);
109                                PermissionHelper.firePermissionNotificationEvent(session, doc, aclName, ace);
110                            }
111                        }
112                    }
113                });
114                break;
115            case ARCHIVED:
116                TransientUserPermissionHelper.revokeToken(ace.getUsername(), doc);
117                break;
118            }
119        }
120    }
121
122    @Override
123    public boolean acceptEvent(Event event) {
124        return ACE_STATUS_UPDATED.equals(event.getName());
125    }
126}