/// <summary> /// Determine the complete list of types reachable as a consequence of a starting group of types. /// </summary> /// <remarks> /// Provide a single shared implementation that is used either at type-level checks, as well as in diagnostic checks to ensure fidelity of result. /// </remarks> /// <param name="initialTypes">The types to start searching from.</param> /// <param name="ancestorsOnlyOnFirst"></param> /// <param name="getTargets"> /// False to follow relationships in reverse to potential sources of grants. /// True to follow relationships in forward to target types that receive grants. /// </param> /// <returns></returns> public static IEnumerable <WalkStep <Tuple <Relationship, Direction, EntityType, EntityType> > > ReachableTypes(IEnumerable <EntityType> initialTypes) { var entityComparer = new EntityIdEqualityComparer <EntityType>( ); var tupleComparer = new CastingComparer <Tuple <Relationship, Direction, EntityType, EntityType>, EntityType>(t => t.Item3, entityComparer); return(Delegates.WalkGraphWithSteps( initialTypes.Select(et => new Tuple <Relationship, Direction, EntityType, EntityType>(null, Direction.Forward, et, null)), tuple => { EntityType et = tuple.Item3; var securingRelationships = GetSecuredRelationships(et, false, false); // hmm .. this should probably pass true return securingRelationships; // Item3 = the next entityType }, tupleComparer )); }
/// <summary> /// Get all securing relationships. /// </summary> /// <param name="entityType"> /// The <see cref="EntityType"/> to check. This cannot be null. /// </param> /// <param name="ancestorsOnly"> /// True if only ancestores are included, false if all possible types are checked. /// </param> /// <param name="isModifyPermission"> /// If true, only consider relationships that should be traversed in modify & delete scenarios. /// </param> /// <param name="getTargets"> /// False to follow relationships in reverse to potential sources of grants. /// True to follow relationships in forward to target types that receive grants. /// </param> /// <returns> /// The relationships with the secures to or from flags set. /// </returns> /// <exception cref="ArgumentNullException"> /// <paramref name="entityType"/> cannot be null. /// </exception> public static IList <Tuple <Relationship, Direction, EntityType, object> > GetSecuringRelationships( this EntityType entityType, bool ancestorsOnly, bool isModifyPermission, bool getTargets = false) { if (entityType == null) { throw new ArgumentNullException("entityType"); } IList <EntityType> entityTypes; IEqualityComparer <Relationship> relationshipEqualityComparer; IList <Tuple <Relationship, Direction, EntityType, object> > forwardRelationships; IList <Tuple <Relationship, Direction, EntityType, object> > reverseRelationships; relationshipEqualityComparer = new EntityIdEqualityComparer <Relationship>(); if (ancestorsOnly) { entityTypes = entityType.GetAncestorsAndSelf() .ToList(); } else { entityTypes = entityType.GetDescendantsAndSelf() .SelectMany(et => et.GetAncestorsAndSelf()) .Distinct(new EntityIdEqualityComparer <EntityType>()) .ToList(); } bool isOnlyReadPerm = !isModifyPermission; forwardRelationships = entityTypes.SelectMany(et2 => getTargets ? et2.ReverseRelationships : et2.Relationships) .Distinct(relationshipEqualityComparer) .Where(r => (r.SecuresFrom == true) && (isOnlyReadPerm || r.SecuresFromReadOnly != true) && (r.ToType != null)) .Select(r => new Tuple <Relationship, Direction, EntityType, object>(r, Direction.Forward, r.ToType, null)) .ToList(); reverseRelationships = entityTypes.SelectMany(et2 => getTargets ? et2.Relationships : et2.ReverseRelationships) .Distinct(relationshipEqualityComparer) .Where(r => (r.SecuresTo == true) && (isOnlyReadPerm || r.SecuresToReadOnly != true) && (r.FromType != null)) .Select(r => new Tuple <Relationship, Direction, EntityType, object>(r, Direction.Reverse, r.FromType, null)) .ToList(); return(forwardRelationships.Union(reverseRelationships).ToList()); }
/// <summary> /// Build an <see cref="EntityMemberRequest"/> used to look for entities /// to perform additional security checks on. /// </summary> /// <param name="entityType">The type of entity whose security is being checked.</param> /// <param name="permissions">The type of permissions required.</param> /// <returns>The <see cref="EntityMemberRequest"/></returns> public EntityMemberRequest BuildEntityMemberRequest(EntityType entityType, IList <EntityRef> permissions) { if (entityType == null) { throw new ArgumentNullException(nameof(entityType)); } // Should we be following all security relationships, or only ones that convey modify perms. bool isModify = false; if (permissions != null) { foreach (EntityRef perm in permissions) { if (perm.Id == Permissions.Modify.Id || perm.Id == Permissions.Delete.Id) { isModify = true; break; } } } // Create context FactoryContext context = new FactoryContext { IsModify = isModify, InitialType = entityType }; // Walk graph IEqualityComparer <EntityType> comparer = new EntityIdEqualityComparer <EntityType>( ); Delegates.WalkGraph(entityType, node => VisitNode(node, context), null, comparer).VisitAll( ); // Register cache invalidations using (CacheContext cacheContext = CacheContext.GetContext( )) { cacheContext.EntityTypes.Add(WellKnownAliases.CurrentTenant.Relationship); cacheContext.FieldTypes.Add(WellKnownAliases.CurrentTenant.SecuresFrom, WellKnownAliases.CurrentTenant.SecuresTo, WellKnownAliases.CurrentTenant.SecuresFromReadOnly, WellKnownAliases.CurrentTenant.SecuresToReadOnly); } return(context.Result); }
/// <summary> /// Get all securing relationships. /// </summary> /// <param name="entityType"> /// The <see cref="EntityType"/> to check. This cannot be null. /// </param> /// <param name="ancestorsOnly"> /// True if only ancestores are included, false if all possible types are checked. /// </param> /// <param name="isModifyPermission"> /// If true, only consider relationships that should be traversed in modify & delete scenarios. /// </param> /// <param name="getTargets"> /// False to follow relationships in reverse to potential sources of grants. /// True to follow relationships in forward to target types that receive grants. /// </param> /// <returns> /// The relationships with the secures to or from flags set. /// </returns> /// <exception cref="ArgumentNullException"> /// <paramref name="entityType"/> cannot be null. /// </exception> private static IList <Tuple <Relationship, Direction, EntityType, EntityType> > GetSecuredRelationships( EntityType entityType, bool ancestorsOnly, bool isModifyPermission) { if (entityType == null) { throw new ArgumentNullException("entityType"); } IList <EntityType> entityTypes; IEqualityComparer <Relationship> relationshipEqualityComparer; IList <Tuple <Relationship, Direction, EntityType, EntityType> > forwardRelationships; IList <Tuple <Relationship, Direction, EntityType, EntityType> > reverseRelationships; relationshipEqualityComparer = new EntityIdEqualityComparer <Relationship>( ); if (ancestorsOnly) { entityTypes = entityType.GetAncestorsAndSelf( ) .ToList( ); } else { entityTypes = entityType.GetAllMemberContributors( ) .ToList( ); } bool isOnlyReadPerm = !isModifyPermission; forwardRelationships = entityTypes.SelectMany(et2 => et2.ReverseRelationships) .Distinct(relationshipEqualityComparer) .Where(r => (r.SecuresFrom == true) && (isOnlyReadPerm || r.SecuresFromReadOnly != true) && (r.ToType != null)) .Select(r => new Tuple <Relationship, Direction, EntityType, EntityType>(r, Direction.Forward, r.FromType, entityType)) .ToList( ); reverseRelationships = entityTypes.SelectMany(et2 => et2.Relationships) .Distinct(relationshipEqualityComparer) .Where(r => (r.SecuresTo == true) && (isOnlyReadPerm || r.SecuresToReadOnly != true) && (r.FromType != null)) .Select(r => new Tuple <Relationship, Direction, EntityType, EntityType>(r, Direction.Reverse, r.ToType, entityType)) .ToList( ); return(forwardRelationships.Union(reverseRelationships).ToList( )); }