View Javadoc

1   /*
2    * Copyright 2015 Data Archiving and Networked Services (an institute of
3    * Koninklijke Nederlandse Akademie van Wetenschappen), King's College London,
4    * Georg-August-Universitaet Goettingen Stiftung Oeffentlichen Rechts
5    *
6    * Licensed under the EUPL, Version 1.1 or – as soon they will be approved by
7    * the European Commission - subsequent versions of the EUPL (the "Licence");
8    * You may not use this work except in compliance with the Licence.
9    * You may obtain a copy of the Licence at:
10   *
11   * https://joinup.ec.europa.eu/software/page/eupl
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the Licence is distributed on an "AS IS" basis,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the Licence for the specific language governing
17   * permissions and limitations under the Licence.
18   */
19  
20  package eu.ehri.project.acl;
21  
22  import com.google.common.base.Preconditions;
23  import com.google.common.cache.CacheBuilder;
24  import com.google.common.cache.CacheLoader;
25  import com.google.common.cache.LoadingCache;
26  import com.google.common.collect.ImmutableList;
27  import com.google.common.collect.Iterables;
28  import com.google.common.collect.Lists;
29  import com.google.common.collect.Maps;
30  import com.google.common.collect.Sets;
31  import com.tinkerpop.blueprints.Direction;
32  import com.tinkerpop.blueprints.Vertex;
33  import com.tinkerpop.frames.FramedGraph;
34  import com.tinkerpop.pipes.PipeFunction;
35  import eu.ehri.project.core.GraphManager;
36  import eu.ehri.project.core.GraphManagerFactory;
37  import eu.ehri.project.definitions.Ontology;
38  import eu.ehri.project.exceptions.IntegrityError;
39  import eu.ehri.project.exceptions.PermissionDenied;
40  import eu.ehri.project.models.ContentType;
41  import eu.ehri.project.models.EntityClass;
42  import eu.ehri.project.models.Group;
43  import eu.ehri.project.models.Permission;
44  import eu.ehri.project.models.PermissionGrant;
45  import eu.ehri.project.models.base.Accessible;
46  import eu.ehri.project.models.base.Accessor;
47  import eu.ehri.project.models.base.PermissionGrantTarget;
48  import eu.ehri.project.models.base.PermissionScope;
49  
50  import java.util.Collection;
51  import java.util.HashSet;
52  import java.util.List;
53  import java.util.Map;
54  import java.util.NoSuchElementException;
55  import java.util.Optional;
56  import java.util.Set;
57  
58  /**
59   * Helper class for checking and asserting access and write permissions.
60   */
61  public final class AclManager {
62  
63      private final FramedGraph<?> graph;
64      private final GraphManager manager;
65      private final PermissionScope scope;
66      private final Set<PermissionScope> scopes;
67  
68      // Lookups to convert between the enum and node representations
69      // of content and permission types.
70      private final LoadingCache<PermissionType, Permission> enumPermissionMap = CacheBuilder.newBuilder()
71              .build(new CacheLoader<PermissionType, Permission>() {
72                  @Override
73                  public Permission load(PermissionType permissionType) throws Exception {
74                      return manager.getEntity(permissionType.getName(), Permission.class);
75                  }
76              });
77      private final LoadingCache<ContentTypes, ContentType> enumContentTypeMap = CacheBuilder.newBuilder()
78              .build(new CacheLoader<ContentTypes, ContentType>() {
79                  @Override
80                  public ContentType load(ContentTypes contentTypes) throws Exception {
81                      return manager.getEntity(contentTypes.getName(), ContentType.class);
82                  }
83              });
84      private final LoadingCache<Permission, PermissionType> permissionEnumMap = CacheBuilder.newBuilder()
85              .build(new CacheLoader<Permission, PermissionType>() {
86                  @Override
87                  public PermissionType load(Permission permission) throws Exception {
88                      return PermissionType.withName(permission.getId());
89                  }
90              });
91      private final LoadingCache<ContentType, ContentTypes> contentTypeEnumMap = CacheBuilder.newBuilder()
92              .build(new CacheLoader<ContentType, ContentTypes>() {
93                  @Override
94                  public ContentTypes load(ContentType contentType) throws Exception {
95                      return ContentTypes.withName(contentType.getId());
96                  }
97              });
98  
99      /**
100      * Scoped constructor.
101      *
102      * @param graph The framed graph
103      * @param scope The ACL scope
104      */
105     public AclManager(FramedGraph<?> graph, PermissionScope scope) {
106         this.graph = graph;
107         this.manager = GraphManagerFactory.getInstance(graph);
108         this.scope = Optional.ofNullable(scope).orElse(SystemScope.getInstance());
109         this.scopes = getAllScopes();
110     }
111 
112     /**
113      * Constructor.
114      *
115      * @param graph The framed graph
116      */
117     public AclManager(FramedGraph<?> graph) {
118         this(graph, SystemScope.getInstance());
119     }
120 
121     /**
122      * Check if an accessor is admin or a member of Admin.
123      *
124      * @param accessor The user/group
125      * @return User belongs to the admin group
126      */
127     public static boolean belongsToAdmin(Accessor accessor) {
128         if (accessor.isAdmin()) {
129             return true;
130         }
131         for (Accessor parent : accessor.getParents()) {
132             if (belongsToAdmin(parent)) {
133                 return true;
134             }
135         }
136         return false;
137     }
138 
139     /**
140      * Check if an accessor is admin or a member of Admin.
141      *
142      * @param accessor The user/group
143      * @return User is an anonymous accessor
144      */
145     public static boolean isAnonymous(Accessor accessor) {
146         Preconditions.checkNotNull(accessor, "NULL accessor given.");
147         return accessor instanceof AnonymousAccessor
148                 || accessor.getId().equals(
149                 Group.ANONYMOUS_GROUP_IDENTIFIER);
150     }
151 
152     /**
153      * Determine if a user can access an entity.
154      *
155      * @param entity   The item
156      * @param accessor The user/group
157      * @return Whether or not the given accessor can access the entity
158      */
159     public boolean canAccess(Accessible entity, Accessor accessor) {
160         Preconditions.checkNotNull(entity, "Entity is null");
161         Preconditions.checkNotNull(accessor, "Accessor is null");
162         return getAclFilterFunction(accessor).compute(entity.asVertex());
163     }
164 
165     /**
166      * Revoke an accessor's access to an entity.
167      *
168      * @param entity   The item
169      * @param accessor A user/group from whom to revoke access
170      */
171     public void removeAccessControl(Accessible entity, Accessor accessor) {
172         entity.removeAccessor(accessor);
173     }
174 
175     /**
176      * Set access control on an entity to several accessors.
177      *
178      * @param entity    The item
179      * @param accessors A set of users/groups who can access the item
180      */
181     public void setAccessors(Accessible entity,
182             Collection<Accessor> accessors) {
183         Set<Accessor> accessorVertices = Sets.newHashSet(accessors);
184         Set<Accessor> remove = Sets.newHashSet();
185         for (Accessor accessor : entity.getAccessors()) {
186             if (!accessorVertices.contains(accessor)) {
187                 remove.add(accessor);
188             }
189         }
190         for (Accessor accessor : remove) {
191             entity.removeAccessor(accessor);
192         }
193         for (Accessor accessor : accessors) {
194             entity.addAccessor(accessor);
195         }
196     }
197 
198     /**
199      * Get a list of permissions for a given accessor on a given entity, including
200      * inherited permissions. Returns a map of accessor IDs against grant permissions.
201      *
202      * @param accessor The accessor
203      * @return An inherited item permission set.
204      */
205     public InheritedItemPermissionSet getInheritedItemPermissions(
206             Accessible entity, Accessor accessor) {
207         InheritedItemPermissionSet.Builder builder
208                 = new InheritedItemPermissionSet
209                 .Builder(accessor.getId(), getItemPermissions(accessor, entity));
210         for (Accessor parent : accessor.getAllParents()) {
211             builder.withInheritedPermissions(parent.getId(), getItemPermissions(parent, entity));
212         }
213         return builder.build();
214     }
215 
216     /**
217      * Set the permissions for a particular user on the given item.
218      *
219      * @param item          The item
220      * @param accessor      The user/group
221      * @param permissionSet A set of permissions
222      */
223     public void setItemPermissions(Accessible item, Accessor accessor,
224             Set<PermissionType> permissionSet) throws PermissionDenied {
225         checkNoGrantOnAdminOrAnon(accessor);
226         for (PermissionType t : PermissionType.values()) {
227             if (permissionSet.contains(t)) {
228                 grantPermission(item, t, accessor);
229             } else {
230                 revokePermission(item, t, accessor);
231             }
232         }
233     }
234 
235     /**
236      * Return a permission list for the given accessor and her inherited groups.
237      *
238      * @param accessor The user/group
239      * @return List of permission maps for the given accessor and his group
240      * parents.
241      */
242     public InheritedGlobalPermissionSet getInheritedGlobalPermissions(
243             Accessor accessor) {
244         InheritedGlobalPermissionSet.Builder builder
245                 = new InheritedGlobalPermissionSet
246                 .Builder(accessor.getId(), getGlobalPermissions(accessor));
247         for (Accessor parent : accessor.getParents()) {
248             builder.withInheritedPermissions(parent.getId(), getGlobalPermissions(parent));
249         }
250         return builder.build();
251     }
252 
253     /**
254      * Recursive helper function to ascend an accessor's groups and populate
255      * their global permissions.
256      *
257      * @param accessor The user/group
258      * @return Permission map for the given accessor
259      */
260     public GlobalPermissionSet getGlobalPermissions(Accessor accessor) {
261         return belongsToAdmin(accessor)
262                 ? getAdminPermissions()
263                 : getAccessorPermissions(accessor);
264     }
265 
266     /**
267      * Set a matrix of global permissions for a given accessor.
268      *
269      * @param accessor The user/group
270      * @param globals  global permission map
271      */
272     public void setPermissionMatrix(Accessor accessor, GlobalPermissionSet globals)
273             throws PermissionDenied {
274         checkNoGrantOnAdminOrAnon(accessor);
275         Map<ContentTypes, Collection<PermissionType>> globalsMap = globals.asMap();
276 
277         //for (Entry<ContentTypes, ContentType> centry : enumContentTypeMap.entrySet()) {
278         for (ContentTypes ct : ContentTypes.values()) {
279             ContentType target = enumContentTypeMap.getUnchecked(ct);
280             Collection<PermissionType> pset = globalsMap.containsKey(ct)
281                     ? globalsMap.get(ct)
282                     : Sets.<PermissionType>newHashSet();
283             for (PermissionType perm : PermissionType.values()) {
284                 if (pset.contains(perm)) {
285                     grantPermission(target, perm, accessor);
286                 } else {
287                     revokePermission(target, perm, accessor);
288                 }
289             }
290         }
291     }
292 
293     /**
294      * Grant a user permissions to a content type.
295      *
296      * @param target   The grant target (content type or item)
297      * @param permType The permission type
298      * @param accessor The user/group
299      * @return The permission grant given for this accessor and target
300      */
301     public PermissionGrant grantPermission(PermissionGrantTarget target,
302             PermissionType permType, Accessor accessor) {
303         assertNoGrantOnAdminOrAnon(accessor);
304         // If we can find an existing grant, use that, otherwise create a new one.
305         Optional<PermissionGrant> maybeGrant = findPermission(target, permType, accessor);
306         if (maybeGrant.isPresent()) {
307             return maybeGrant.get();
308         } else {
309             PermissionGrant grant = createPermissionGrant();
310             accessor.addPermissionGrant(grant);
311             grant.setPermission(vertexForPermission(permType));
312             grant.addTarget(target);
313             if (!isSystemScope()) {
314                 grant.setScope(scope);
315             }
316             return grant;
317         }
318     }
319 
320     /**
321      * Revoke a particular permission on the given entity.
322      *
323      * @param entity   The item
324      * @param permType The permission type
325      * @param accessor The user/group
326      */
327     public void revokePermission(Accessible entity, PermissionType permType,
328             Accessor accessor) {
329         Optional<PermissionGrant> maybeGrant = findPermission(entity, permType, accessor);
330         if (maybeGrant.isPresent()) {
331             manager.deleteVertex(maybeGrant.get().asVertex());
332         }
333     }
334 
335     /**
336      * Revoke a particular permission grant.
337      *
338      * @param grant The grant to revoke
339      */
340     public void revokePermissionGrant(PermissionGrant grant) {
341         manager.deleteVertex(grant.asVertex());
342     }
343 
344     /**
345      * Build a gremlin filter function that passes through items that are
346      * bona fide content types.
347      *
348      * @return A PipeFunction for filtering vertices that are content types.
349      */
350     public PipeFunction<Vertex, Boolean> getContentTypeFilterFunction() {
351         final Set<String> typeStrings = Sets.newHashSet();
352         for (ContentTypes ct : ContentTypes.values()) {
353             typeStrings.add(ct.getName());
354         }
355         return v -> v != null && typeStrings.contains(manager.getType(v));
356     }
357 
358     /**
359      * Build a gremlin filter function that passes through items readable by a
360      * given accessor.
361      *
362      * @param accessor The user/group
363      * @return A PipeFunction for filtering a set of vertices as the given user
364      */
365     public static PipeFunction<Vertex, Boolean> getAclFilterFunction(Accessor accessor) {
366         Preconditions.checkNotNull(accessor, "Accessor is null");
367         if (belongsToAdmin(accessor)) {
368             return noopFilterFunction();
369         }
370 
371         final HashSet<Vertex> all = getAllAccessors(accessor);
372         return v -> {
373             Iterable<Vertex> verts = v.getVertices(Direction.OUT,
374                     Ontology.IS_ACCESSIBLE_TO);
375             // If there's no Access conditions, it's
376             // read-only...
377             if (!verts.iterator().hasNext()) {
378                 return true;
379             }
380             // If it's promoted it's publicly accessible
381             if (isPromoted(v)) {
382                 return true;
383             }
384             // Otherwise, check relevant accessors...
385             for (Vertex other : verts) {
386                 if (all.contains(other)) {
387                     return true;
388                 }
389             }
390             return false;
391         };
392     }
393 
394     /**
395      * Check if a user has permission to perform an action on the given content type.
396      *
397      * @param contentType    The content type
398      * @param permissionType The requested permission
399      * @param accessor       The user
400      * @return If the user has permission on the given content type within the current scope
401      */
402     public boolean hasPermission(ContentTypes contentType, PermissionType permissionType, Accessor accessor) {
403         return hasPermission(contentType, permissionType, accessor, scopes);
404     }
405 
406     /**
407      * Check if a user has permission to perform an action on the given item.
408      *
409      * @param entity         The item
410      * @param permissionType The requested permission
411      * @param accessor       The user
412      * @return If the user has the permission on the given item
413      */
414     public boolean hasPermission(Accessible entity, PermissionType permissionType, Accessor accessor) {
415         // Get a list of our current context scopes, plus
416         // the parent scopes of the item.
417         Set<PermissionScope> allScopes = Sets.newHashSet(scopes);
418         for (PermissionScope scope : entity.getPermissionScopes()) {
419             allScopes.add(scope);
420         }
421 
422         // Check if the user has content type permissions on this item, using
423         // the parent scope of the item...
424         ContentTypes contentType = getContentType(manager.getEntityClass(entity));
425         if (hasPermission(contentType, permissionType, accessor, allScopes)) {
426             return true;
427         }
428 
429         // Otherwise, we have to check the item's permissions...
430         return hasScopedPermission(entity, permissionType, accessor, allScopes);
431     }
432 
433     // Helpers...
434 
435     /**
436      * Set scope.
437      *
438      * @param scope The new permission scope
439      * @return A new ACL Manager
440      */
441     public AclManager withScope(PermissionScope scope) {
442         return new AclManager(graph, scope);
443     }
444 
445     /**
446      * Get scope.
447      *
448      * @return The permission scope.
449      */
450     public PermissionScope getScope() {
451         return scope;
452     }
453 
454     /**
455      * Check for a content permission with a given set of scopes.
456      *
457      * @param contentType    The content type
458      * @param permissionType The permission type
459      * @param accessor       The user/group
460      * @param scopes         The item scopes
461      * @return Whether or not the user has permission
462      */
463     private boolean hasPermission(ContentTypes contentType, PermissionType permissionType, Accessor accessor,
464             Collection<PermissionScope> scopes) {
465 
466         ContentType contentTypeNode = enumContentTypeMap.getUnchecked(contentType);
467         // Check the user themselves...
468         return belongsToAdmin(accessor)
469                 || hasScopedPermission(contentTypeNode, permissionType, accessor, scopes);
470     }
471 
472     /**
473      * Attempt to find a permission, searching the accessor's parent hierarchy.
474      *
475      * @param target         A target permission grant
476      * @param permissionType The permission type
477      * @param accessor       The user/group
478      * @param scopes         A set of parent scopes
479      * @return Whether or not the grant was found
480      */
481     private boolean hasScopedPermission(PermissionGrantTarget target,
482             PermissionType permissionType, Accessor accessor,
483             Collection<PermissionScope> scopes) {
484 
485         for (PermissionGrant grant : accessor.getPermissionGrants()) {
486             PermissionType grantPermissionType
487                     = enumForPermission(grant.getPermission());
488 
489             // If it's not the permission type we want, skip it...
490             if (!grantPermissionType.contains(permissionType)) {
491                 continue;
492             }
493 
494             // Get the content type node and check it matches the grant target
495             for (PermissionGrantTarget tg : grant.getTargets()) {
496                 if (!target.equals(tg)) {
497                     continue;
498                 }
499 
500                 // Now check the scope - if there is none we're good.
501                 if (grant.getScope() == null || scopes.contains(grant.getScope())) {
502                     return true;
503                 }
504             }
505         }
506 
507         // If no joy, check if they inherit the permission...
508         for (Accessor ancestor : accessor.getParents()) {
509             if (hasScopedPermission(target, permissionType, ancestor, scopes)) {
510                 return true;
511             }
512         }
513 
514         // Default case, return false....
515         return false;
516     }
517 
518     /**
519      * Get the permission type enum for a given node.
520      */
521     private Permission vertexForPermission(PermissionType perm) {
522         return enumPermissionMap.getUnchecked(perm);
523     }
524 
525     /**
526      * Get the permission type enum for a given node.
527      */
528     private PermissionType enumForPermission(Permission perm) {
529         return permissionEnumMap.getUnchecked(perm);
530     }
531 
532     /**
533      * Get a list of global permissions for a given accessor. Returns a map of
534      * content types against the grant permissions.
535      *
536      * @param accessor The user/group
537      * @return List of permission names for the given accessor on the given
538      * target
539      */
540     private List<PermissionType> getItemPermissions(Accessor accessor,
541             Accessible entity) {
542         // If we're admin, add it regardless.
543         if (belongsToAdmin(accessor)) {
544             return ImmutableList.copyOf(PermissionType.values());
545         } else {
546             List<PermissionType> list = Lists.newArrayList();
547             // Cache a set of permission scopes. This is the hierarchy on which
548             // permissions are granted. For most items it will contain zero
549             // entries and thus be pretty fast, but for deeply nested
550             // documentary units there might be quite a few.
551             HashSet<PermissionScope> scopes = Sets.newHashSet(entity.getPermissionScopes());
552             PermissionGrantTarget target = entity.as(PermissionGrantTarget.class);
553 
554             for (PermissionGrant grant : accessor.getPermissionGrants()) {
555                 if (Iterables.contains(grant.getTargets(), target)) {
556                     list.add(enumForPermission(grant.getPermission()));
557                 } else if (grant.getScope() != null && hasContentTypeTargets(grant)) {
558                     // If there isn't a direct grant to the entity, search its
559                     // parent scopes for an appropriate scoped permission
560                     if (scopes.contains(grant.getScope())) {
561                         list.add(enumForPermission(grant.getPermission()));
562                     }
563                 }
564             }
565             return list;
566         }
567     }
568 
569     /**
570      * Attempt to locate an existing grant with the same accessor, entity, and
571      * permission, within the given scope.
572      */
573     private Optional<PermissionGrant> findPermission(PermissionGrantTarget entity,
574             PermissionType permType, Accessor accessor) {
575 
576         PermissionGrantTarget target = entity.as(PermissionGrantTarget.class);
577         Permission perm = enumPermissionMap.getUnchecked(permType);
578         for (PermissionGrant grant : accessor.getPermissionGrants()) {
579             if (isInScope(grant)
580                     && Iterables.contains(grant.getTargets(), target)
581                     && grant.getPermission().equals(perm)) {
582                 return Optional.of(grant);
583             }
584         }
585         return Optional.empty();
586     }
587 
588     private PermissionGrant createPermissionGrant() {
589         try {
590             Vertex vertex = manager.createVertex(
591                     EntityClass.PERMISSION_GRANT.getIdGen()
592                             .generateId(Lists.<String>newArrayList(), null),
593                     EntityClass.PERMISSION_GRANT,
594                     Maps.newHashMap());
595             return graph.frame(vertex, PermissionGrant.class);
596         } catch (IntegrityError e) {
597             e.printStackTrace();
598             throw new RuntimeException("Something very unlikely has occured because two" +
599                     " supposedly-random numbers have collided. Trying again should fix this.");
600         }
601     }
602 
603     private void checkNoGrantOnAdminOrAnon(Accessor accessor)
604             throws PermissionDenied {
605         // Quick sanity check to make sure we're not trying to add/remove
606         // permissions from the admin or the anonymous accounts.
607         if (accessor.isAdmin() || accessor.isAnonymous()) {
608             throw new PermissionDenied(
609                     "Unable to grant or revoke permissions to system accounts.");
610         }
611     }
612 
613     private void assertNoGrantOnAdminOrAnon(Accessor accessor) {
614         // Quick sanity check to make sure we're not trying to add/remove
615         // permissions from the admin or the anonymous accounts.
616         // This time throw  a runtime error.
617         if (accessor.isAdmin() || accessor.isAnonymous()) {
618             throw new RuntimeException(
619                     "Unable to grant or revoke permissions to system accounts.");
620         }
621     }
622 
623     /**
624      * For a given user, fetch a lookup of all the inherited accessors it
625      * belongs to. NB: This returns a lookup of raw Vertices because it's
626      * used by the a Gremlin filter function, which likewise operates
627      * directly on vertices.
628      *
629      * @param accessor The user/group
630      * @return A lookup of accessor vertices
631      */
632     private static HashSet<Vertex> getAllAccessors(Accessor accessor) {
633 
634         HashSet<Vertex> all = Sets.newHashSet();
635         if (!isAnonymous(accessor)) {
636             Iterable<Accessor> parents = accessor.getAllParents();
637             for (Accessor a : parents) {
638                 all.add(a.asVertex());
639             }
640             all.add(accessor.asVertex());
641         }
642         return all;
643     }
644 
645     /**
646      * Fetch a user's global permission set.
647      *
648      * @param accessor The user/group
649      * @return A global permission set for the given user.
650      */
651     private GlobalPermissionSet getAccessorPermissions(Accessor accessor) {
652         GlobalPermissionSet.Builder builder = GlobalPermissionSet.newBuilder();
653         for (PermissionGrant grant : accessor.getPermissionGrants()) {
654             PermissionScope scope = grant.getScope();
655             if (scope == null || scopes.contains(scope)) {
656                 for (PermissionGrantTarget target : grant.getTargets()) {
657                     if (manager.getEntityClass(target).equals(EntityClass.CONTENT_TYPE)) {
658                         ContentType contentType = target.as(ContentType.class);
659                         Permission permission = grant.getPermission();
660                         if (permission != null) {
661                             builder.set(
662                                     contentTypeEnumMap.getUnchecked(contentType),
663                                     permissionEnumMap.getUnchecked(permission));
664                         }
665                     }
666                 }
667             }
668         }
669         return builder.build();
670     }
671 
672     /**
673      * Get the data structure for admin permissions, which is basically all
674      * available permissions turned on.
675      *
676      * @return A global permission set for the admin user.
677      */
678     private GlobalPermissionSet getAdminPermissions() {
679         GlobalPermissionSet.Builder builder = GlobalPermissionSet.newBuilder();
680         for (ContentTypes ct : ContentTypes.values()) {
681             builder.set(ct, PermissionType.values());
682         }
683         return builder.build();
684     }
685 
686     /**
687      * Pipe filter function that passes through all items.
688      *
689      * @return A no-op PipeFunction for filtering a list of vertices as an admin
690      * user
691      */
692     private static PipeFunction<Vertex, Boolean> noopFilterFunction() {
693         return v -> true;
694     }
695 
696     // Get a list of the current scope and its parents
697     private HashSet<PermissionScope> getAllScopes() {
698         HashSet<PermissionScope> all = Sets.newHashSet(scope.getPermissionScopes());
699         if (!isSystemScope()) {
700             all.add(scope);
701         }
702         return all;
703     }
704 
705     private boolean isSystemScope() {
706         return scope.equals(SystemScope.INSTANCE);
707     }
708 
709     private boolean isInScope(PermissionGrant grant) {
710         // If we're on the system scope, only remove unscoped
711         // permissions. Otherwise, check the scope matches the grant.
712         return isSystemScope()
713                 && grant.getScope() == null
714                 || (grant.getScope() != null && Iterables.contains(scopes,
715                 grant.getScope()));
716     }
717 
718     /**
719      * Get the content type with the given id.
720      */
721     private ContentTypes getContentType(EntityClass type) {
722         try {
723             return ContentTypes.withName(type.getName());
724         } catch (NoSuchElementException e) {
725             throw new RuntimeException(
726                     String.format("No content type found for type: '%s'", type.getName()), e);
727         }
728     }
729 
730     private boolean hasContentTypeTargets(PermissionGrant grant) {
731         for (PermissionGrantTarget tg : grant.getTargets()) {
732             if (!manager.getEntityClass(tg).equals(EntityClass.CONTENT_TYPE)) {
733                 return false;
734             }
735         }
736         return true;
737     }
738 
739     private static boolean isPromoted(Vertex v) {
740         int promotions = Iterables.size(v.getEdges(Direction.OUT, Ontology.PROMOTED_BY));
741         return promotions > 0
742                 && promotions > Iterables.size(v.getEdges(Direction.OUT, Ontology.DEMOTED_BY));
743     }
744 }