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