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.io.registry.reflect.Instantiations.SINGLETON;
021import static org.nuxeo.ecm.core.io.registry.reflect.Priorities.REFERENCE;
022import static org.nuxeo.ecm.permissions.Constants.ACE_INFO_COMMENT;
023import static org.nuxeo.ecm.permissions.Constants.ACE_INFO_DIRECTORY;
024import static org.nuxeo.ecm.permissions.Constants.ACE_INFO_NOTIFY;
025
026import java.io.IOException;
027import java.io.Serializable;
028import java.util.HashMap;
029import java.util.Map;
030
031import org.codehaus.jackson.JsonGenerator;
032import org.nuxeo.ecm.core.api.DocumentModel;
033import org.nuxeo.ecm.core.api.security.ACE;
034import org.nuxeo.ecm.core.api.security.ACL;
035import org.nuxeo.ecm.core.api.security.ACP;
036import org.nuxeo.ecm.core.io.marshallers.json.enrichers.AbstractJsonEnricher;
037import org.nuxeo.ecm.core.io.registry.reflect.Setup;
038import org.nuxeo.ecm.core.schema.utils.DateParser;
039import org.nuxeo.ecm.directory.Session;
040import org.nuxeo.ecm.directory.api.DirectoryService;
041import org.nuxeo.runtime.api.Framework;
042
043/**
044 * Enrich {@link DocumentModel} Json.
045 * <p>
046 * Add {@link DocumentModel}'s ACP as json attachment with notifications info for each ACE (such as whether a
047 * notification should be send and the notification comment).
048 * </p>
049 * <p>
050 * Enable if parameter enrichers.document=extendedAcls is present.
051 * </p>
052 * <p>
053 * Format is:
054 *
055 * <pre>
056 * {@code
057 * {
058 *   "entity-type":"document",
059 *   ...
060 *   "contextParameters": {
061 *     "acls": [
062 *       {
063 *         "name":" inherited",
064 *         "ace": [
065 *           {
066 *             "username": "administrators",
067 *             "permission": "Everything",
068 *             "granted": true,
069 *             "notify": false,
070 *             "comment": ""
071 *           },
072 *           ...
073 *         ]
074 *       },
075 *       ...
076 *     ]
077 *   }
078 * }
079 * </pre>
080 * </p>
081 *
082 * @since 7.4
083 */
084@Setup(mode = SINGLETON, priority = REFERENCE)
085public class ExtendedACLJsonEnricher extends AbstractJsonEnricher<DocumentModel> {
086
087    public static final String NAME = "extendedAcls";
088
089    public ExtendedACLJsonEnricher() {
090        super(NAME);
091    }
092
093    @Override
094    public void write(JsonGenerator jg, DocumentModel document) throws IOException {
095        ACP item = document.getACP();
096        jg.writeArrayFieldStart("acls");
097        for (ACL acl : item.getACLs()) {
098            jg.writeStartObject();
099            jg.writeStringField("name", acl.getName());
100            jg.writeArrayFieldStart("ace");
101            for (ACE ace : acl.getACEs()) {
102                jg.writeStartObject();
103                jg.writeStringField("id", ace.getId());
104                jg.writeStringField("username", ace.getUsername());
105                jg.writeStringField("permission", ace.getPermission());
106                jg.writeBooleanField("granted", ace.isGranted());
107                jg.writeStringField("creator", ace.getCreator());
108                jg.writeStringField("begin",
109                        ace.getBegin() != null ? DateParser.formatW3CDateTime(ace.getBegin().getTime()) : null);
110                jg.writeStringField("end",
111                        ace.getEnd() != null ? DateParser.formatW3CDateTime(ace.getEnd().getTime()) : null);
112                jg.writeStringField("status", ace.getStatus().toString().toLowerCase());
113                Map<String, Serializable> m = computeAdditionalFields(document, acl.getName(), ace.getId());
114                for (Map.Entry<String, Serializable> entry : m.entrySet()) {
115                    jg.writeObjectField(entry.getKey(), entry.getValue());
116                }
117                jg.writeEndObject();
118            }
119            jg.writeEndArray();
120            jg.writeEndObject();
121        }
122        jg.writeEndArray();
123    }
124
125    protected Map<String, Serializable> computeAdditionalFields(DocumentModel doc, String aclName, String aceId) {
126        Map<String, Serializable> m = new HashMap<>();
127
128        DirectoryService directoryService = Framework.getLocalService(DirectoryService.class);
129        Session session = null;
130        try {
131            session = directoryService.open(ACE_INFO_DIRECTORY);
132            String id = computeDirectoryId(doc, aclName, aceId);
133            DocumentModel entry = session.getEntry(id);
134            if (entry != null) {
135                m.put("notify", entry.getPropertyValue(ACE_INFO_NOTIFY));
136                m.put("comment", entry.getPropertyValue(ACE_INFO_COMMENT));
137            }
138        } finally {
139            if (session != null) {
140                session.close();
141            }
142        }
143
144        return m;
145    }
146
147    protected String computeDirectoryId(DocumentModel doc, String aclName, String aceId) {
148        return String.format("%s:%s:%s:%s", doc.getId(), doc.getRepositoryName(), aclName, aceId);
149    }
150
151}