001/*
002 * (C) Copyright 2006-2018 Nuxeo (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 *     bstefanescu
018 */
019package org.nuxeo.ecm.automation.features;
020
021import java.util.HashSet;
022import java.util.Set;
023
024import org.apache.commons.lang3.StringUtils;
025import org.nuxeo.ecm.core.api.DocumentModel;
026import org.nuxeo.ecm.core.api.NuxeoGroup;
027import org.nuxeo.ecm.core.api.NuxeoPrincipal;
028import org.nuxeo.ecm.core.api.security.ACE;
029import org.nuxeo.ecm.core.api.security.ACL;
030import org.nuxeo.ecm.core.api.security.ACP;
031import org.nuxeo.ecm.core.api.security.PermissionProvider;
032import org.nuxeo.ecm.core.api.security.SecurityConstants;
033import org.nuxeo.ecm.platform.usermanager.UserManager;
034
035/**
036 * Provides helper methods to find extract permissions/principals info from documents.
037 *
038 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
039 * @author Anahide Tchertchian
040 */
041public class PrincipalHelper {
042
043    protected UserManager userManager;
044
045    protected PermissionProvider permissionProvider;
046
047    public PrincipalHelper(UserManager userManager, PermissionProvider permissionProvider) {
048        this.userManager = userManager;
049        this.permissionProvider = permissionProvider;
050    }
051
052    @SuppressWarnings("unchecked")
053    public Set<String> getEmailsForPermission(DocumentModel input, String permission, boolean ignoreGroups) {
054        return (Set<String>) collectObjectsMatchingPermission(input, permission, ignoreGroups, true,
055                new EmailCollector(userManager.getUserSchemaName(), userManager.getUserEmailField()));
056    }
057
058    /**
059     * Resolves the list of identifiers for users and groups who have the given permission on given document.
060     *
061     * @param input document model to resolve users and groups on.
062     * @param permission the permission to check
063     * @param ignoreGroups if true, will ignore groups in resolution of ids
064     * @param resolveGroups if true, will resolve user members, iterating in the hierarchy of groups
065     * @param prefixIds if true, will prefix identifiers with {@link NuxeoPrincipal#PREFIX} and
066     *            {@link NuxeoGroup#PREFIX}
067     */
068    @SuppressWarnings("unchecked")
069    public Set<String> getUserAndGroupIdsForPermission(DocumentModel input, String permission, boolean ignoreGroups,
070            boolean resolveGroups, boolean prefixIds) {
071        return (Set<String>) collectObjectsMatchingPermission(input, permission, ignoreGroups, resolveGroups,
072                new IdCollector(prefixIds));
073    }
074
075    @SuppressWarnings("unchecked")
076    public Set<NuxeoPrincipal> getPrincipalsForPermission(DocumentModel input, String permission, boolean ignoreGroups,
077            boolean resolveGroups) {
078        return (Set<NuxeoPrincipal>) collectObjectsMatchingPermission(input, permission, ignoreGroups, resolveGroups,
079                new PrincipalCollector());
080    }
081
082    public Set<String> getEmailsFromGroup(String groupId, boolean resolveGroups) {
083        EmailCollector collector = new EmailCollector(userManager.getUserSchemaName(), userManager.getUserEmailField());
084        collectObjectsFromGroup(groupId, resolveGroups, collector);
085        return collector.getResult();
086    }
087
088    public Set<NuxeoPrincipal> getPrincipalsFromGroup(String groupId, boolean resolveGroups) {
089        PrincipalCollector collector = new PrincipalCollector();
090        collectObjectsFromGroup(groupId, resolveGroups, collector);
091        return collector.getResult();
092    }
093
094    public Set<String> getUserNamesFromGroup(String groupId, boolean resolveGroups, boolean prefixIds) {
095        IdCollector collector = new IdCollector(prefixIds);
096        collectObjectsFromGroup(groupId, resolveGroups, collector);
097        return collector.getResult();
098    }
099
100    public void collectObjectsFromGroup(String groupId, boolean resolveGroups, Collector<?> collector) {
101        NuxeoGroup group = userManager.getGroup(groupId);
102        if (group == null) {
103            userManager.getPrincipal(groupId);
104        } else {
105            for (String u : group.getMemberUsers()) {
106                NuxeoPrincipal principal = userManager.getPrincipal(u);
107                if (principal != null) {
108                    collector.collect(principal);
109                }
110            }
111            if (resolveGroups) {
112                for (String g : group.getMemberGroups()) {
113                    collectObjectsFromGroup(g, resolveGroups, collector);
114                }
115            }
116        }
117    }
118
119    public HashSet<?> collectObjectsMatchingPermission(DocumentModel input, String permission, boolean ignoreGroups,
120            boolean resolveGroups, Collector<?> collector) {
121        String[] perms = getPermissionsToCheck(permission);
122        ACP acp = input.getACP();
123        for (ACL acl : acp.getACLs()) {
124            for (ACE ace : acl.getACEs()) {
125                if (ace.isGranted() && permissionMatch(perms, ace.getPermission())) {
126                    NuxeoGroup group = userManager.getGroup(ace.getUsername());
127                    if (group == null) {
128                        // this may be a user
129                        collector.collect(userManager.getPrincipal(ace.getUsername()));
130                    } else if (!ignoreGroups) {
131                        if (resolveGroups) {
132                            resolveGroups(group, collector);
133                        } else {
134                            collector.collect(group);
135                        }
136                    }
137                }
138            }
139        }
140        return collector.getResult();
141    }
142
143    public void resolveGroups(NuxeoGroup group, Collector<?> collector) {
144        if (group != null) {
145            for (String memberUser : group.getMemberUsers()) {
146                collector.collect(userManager.getPrincipal(memberUser));
147            }
148            for (String subGroup : group.getMemberGroups()) {
149                resolveGroups(userManager.getGroup(subGroup), collector);
150            }
151        }
152    }
153
154    public String[] getPermissionsToCheck(String permission) {
155        String[] groups = permissionProvider.getPermissionGroups(permission);
156        if (groups == null) {
157            return new String[] { permission, SecurityConstants.EVERYTHING };
158        } else {
159            String[] perms = new String[groups.length + 2];
160            perms[0] = permission;
161            System.arraycopy(groups, 0, perms, 1, groups.length);
162            perms[groups.length + 1] = SecurityConstants.EVERYTHING;
163            return perms;
164        }
165    }
166
167    public boolean permissionMatch(String[] perms, String perm) {
168        for (String p : perms) {
169            if (p.equals(perm)) {
170                return true;
171            }
172        }
173        return false;
174    }
175
176    interface Collector<T> {
177
178        void collect(NuxeoPrincipal principal);
179
180        void collect(NuxeoGroup group);
181
182        HashSet<T> getResult();
183    }
184
185    public static class EmailCollector implements Collector<String> {
186
187        protected final String userSchemaName;
188
189        protected final String userEmailFieldName;
190
191        protected HashSet<String> result = new HashSet<>();
192
193        EmailCollector(String userSchemaName, String userEmailFieldName) {
194            this.userSchemaName = userSchemaName;
195            this.userEmailFieldName = userEmailFieldName;
196        }
197
198        @Override
199        public void collect(NuxeoPrincipal principal) {
200            if (principal == null) {
201                return;
202            }
203            DocumentModel userEntry = principal.getModel();
204            String email = (String) userEntry.getProperty(userSchemaName, userEmailFieldName);
205            if (!StringUtils.isEmpty(email)) {
206                result.add(email);
207            }
208        }
209
210        @Override
211        public void collect(NuxeoGroup group) {
212            // do nothing
213        }
214
215        @Override
216        public HashSet<String> getResult() {
217            return result;
218        }
219    }
220
221    static class PrincipalCollector implements Collector<NuxeoPrincipal> {
222
223        protected HashSet<NuxeoPrincipal> result = new HashSet<>();
224
225        @Override
226        public void collect(NuxeoPrincipal principal) {
227            if (principal == null) {
228                return;
229            }
230            result.add(principal);
231        }
232
233        @Override
234        public void collect(NuxeoGroup group) {
235            // do nothing
236        }
237
238        @Override
239        public HashSet<NuxeoPrincipal> getResult() {
240            return result;
241        }
242    }
243
244    static class IdCollector implements Collector<String> {
245
246        protected final boolean prefixIds;
247
248        protected HashSet<String> result = new HashSet<>();
249
250        IdCollector(boolean prefixIds) {
251            this.prefixIds = prefixIds;
252        }
253
254        @Override
255        public void collect(NuxeoPrincipal principal) {
256            if (principal != null) {
257                String name = principal.getName();
258                if (name != null) {
259                    if (prefixIds) {
260                        result.add(NuxeoPrincipal.PREFIX + name);
261                    } else {
262                        result.add(name);
263                    }
264                }
265            }
266        }
267
268        @Override
269        public void collect(NuxeoGroup group) {
270            if (group != null) {
271                String name = group.getName();
272                if (name != null) {
273                    if (prefixIds) {
274                        result.add(NuxeoGroup.PREFIX + name);
275                    } else {
276                        result.add(name);
277                    }
278                }
279            }
280        }
281
282        @Override
283        public HashSet<String> getResult() {
284            return result;
285        }
286
287    }
288
289}