001/*
002 * (C) Copyright 2013 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 *     Martin Pernollet
018 */
019
020package org.nuxeo.ecm.platform.groups.audit.service.acl.data;
021
022import java.util.HashSet;
023
024import org.apache.commons.logging.Log;
025import org.apache.commons.logging.LogFactory;
026import org.nuxeo.ecm.core.api.DocumentModel;
027import org.nuxeo.ecm.core.api.security.ACE;
028import org.nuxeo.ecm.core.api.security.ACL;
029import org.nuxeo.ecm.core.api.security.ACP;
030import org.nuxeo.ecm.core.api.security.SecurityConstants;
031import org.nuxeo.ecm.platform.groups.audit.service.acl.Pair;
032import org.nuxeo.ecm.platform.groups.audit.service.acl.filter.IContentFilter;
033
034import com.google.common.collect.HashMultimap;
035import com.google.common.collect.Multimap;
036
037import static org.nuxeo.ecm.core.api.security.ACL.LOCAL_ACL;
038
039public class AclSummaryExtractor {
040    private static final Log log = LogFactory.getLog(AclSummaryExtractor.class);
041
042    protected IContentFilter filter;
043
044    public AclSummaryExtractor(IContentFilter filter) {
045        this.filter = filter;
046    }
047
048    /**
049     * Return a compact version of a document ACLs, e.g.:
050     * <ul>
051     * <li>user1 -> [(READ,true), (WRITE,false), (ADD_CHILDREN,false), ...]
052     * <li>user2 -> [(READ,true), (WRITE,true), (ADD_CHILDREN,true), ...]
053     * <li>
054     * </ul>
055     * Remark: content might be ignored according to the policy implemented by {@link IContentFilter}.
056     *
057     * @param doc
058     * @return
059     */
060    public Multimap<String, Pair<String, Boolean>> getAllAclByUser(DocumentModel doc) {
061        ACP acp = doc.getACP();
062        ACL[] acls = acp.getACLs();
063        return getAclByUser(acls);
064    }
065
066    public Multimap<String, Pair<String, Boolean>> getAclLocalByUser(DocumentModel doc) {
067        ACP acp = doc.getACP();
068        ACL acl = acp.getACL(LOCAL_ACL);
069        return getAclByUser(acl);
070    }
071
072    public Multimap<String, Pair<String, Boolean>> getAclInheritedByUser(DocumentModel doc) {
073        ACP acp = doc.getACP();
074        ACL acl = acp.getACL(ACL.INHERITED_ACL);
075        return getAclByUser(acl);
076    }
077
078    public Multimap<String, Pair<String, Boolean>> getAclByUser(ACL[] acls) {
079        Multimap<String, Pair<String, Boolean>> aclByUser = HashMultimap.create();
080
081        for (ACL acl : acls) {
082            fillAceByUser(aclByUser, acl);
083        }
084        return aclByUser;
085    }
086
087    public Multimap<String, Pair<String, Boolean>> getAclByUser(ACL acl) {
088        Multimap<String, Pair<String, Boolean>> aclByUser = HashMultimap.create();
089        fillAceByUser(aclByUser, acl);
090        return aclByUser;
091    }
092
093    protected void fillAceByUser(Multimap<String, Pair<String, Boolean>> aclByUser, ACL acl) {
094        if (acl == null)
095            return;
096        for (ACE ace : acl.getACEs()) {
097            if (filter.acceptsUserOrGroup(ace.getUsername())) {
098                String userOrGroup = ace.getUsername();
099                String permission = ace.getPermission();
100                boolean allow = ace.isGranted();
101                Pair<String, Boolean> pair = Pair.of(permission, allow);
102                aclByUser.put(userOrGroup, pair);
103
104                if (ace.isGranted() && ace.isDenied())
105                    log.warn("stupid state: ace granted and denied at the same time. Considered granted");
106            }
107        }
108    }
109
110    /**
111     * Returns true if this document owns an ACE locking inheritance Remark: content might be ignored according to the
112     * policy implemented by {@link IContentFilter}.
113     *
114     * @see isLockInheritance(ACE)
115     * @param doc
116     * @return
117     */
118    public boolean hasLockInheritanceACE(DocumentModel doc) {
119        // Fetch only local ACL to prevent from having blocking inheritance on
120        // all child.
121        ACL acl = doc.getACP().getOrCreateACL(LOCAL_ACL);
122
123        for (ACE ace : acl.getACEs()) {
124            if (filter.acceptsUserOrGroup(ace.getUsername())) {
125                if (isLockInheritance(ace))
126                    return true;
127            }
128        }
129        return false;
130    }
131
132    public boolean hasLockInheritanceACE(Multimap<String, Pair<String, Boolean>> acls) {
133        for (String user : acls.keySet()) {
134            for (Pair<String, Boolean> ace : acls.get(user)) {
135                if (SecurityConstants.EVERYONE.equals(user)) {
136                    if (filter.acceptsUserOrGroup(user)) {
137                        if (SecurityConstants.EVERYTHING.equals(ace.a) && !ace.b)
138                            return true;
139                    }
140                }
141            }
142        }
143        return false;
144    }
145
146    /**
147     * Return true if this ACE locks inheritance, in other word:
148     * <ul>
149     * <li>EVERYONE
150     * <li>EVERYTHING
151     * <li>deny
152     * </ul>
153     */
154    public boolean isLockInheritance(ACE ace) {
155        return (SecurityConstants.EVERYONE.equals(ace.getUsername())
156                && SecurityConstants.EVERYTHING.equals(ace.getPermission()) && ace.isDenied());
157    }
158
159    public boolean isLockInheritance(String user, Pair<String, Boolean> ace) {
160        return (SecurityConstants.EVERYONE.equals(user) && SecurityConstants.EVERYTHING.equals(ace.a) && !ace.b);
161    }
162
163    /**
164     * Return the set of users and permissions mentionned in this document's ACLs. Remark: content might be ignored
165     * according to the policy implemented by {@link IContentFilter}.
166     *
167     * @param doc
168     * @return
169     */
170    public Pair<HashSet<String>, HashSet<String>> getAclSummary(DocumentModel doc) {
171        Pair<HashSet<String>, HashSet<String>> summary = newSummary();
172        ACP acp = doc.getACP();
173        ACL[] acls = acp.getACLs();
174
175        for (ACL acl : acls) {
176            for (ACE ace : acl.getACEs()) {
177                String userOrGroup = ace.getUsername();
178                if (filter.acceptsUserOrGroup(userOrGroup)) {
179                    String permission = ace.getPermission();
180                    summary.a.add(userOrGroup);
181                    summary.b.add(permission);
182                }
183            }
184        }
185        return summary;
186    }
187
188    protected Pair<HashSet<String>, HashSet<String>> newSummary() {
189        return Pair.of(new HashSet<String>(), new HashSet<String>());
190    }
191
192    public void printAce(DocumentModel doc) {
193        ACP acp = doc.getACP();
194        ACL[] acls = acp.getACLs();
195
196        for (ACL acl : acls) {
197            for (ACE ace : acl.getACEs()) {
198                System.out.println(ace);
199            }
200        }
201    }
202}