/// <inheritdoc/> protected override void BuildRenderTree(RenderTreeBuilder builder) { if (Source == null) { WhenNull?.Invoke(builder); return; } var componentType = MatchingTypeFinder.TryFind(Source.GetType(), Scope); if (componentType == null) { if (WhenNoMatchFound == null) { throw Errors.NoMatchingComponentFound(Source.GetType(), Scope); } WhenNoMatchFound(Source)(builder); return; } var i = 0; builder.OpenComponent(i++, componentType); if (!string.IsNullOrEmpty(SourceParameterName)) { builder.AddAttribute(i++, SourceParameterName, Source); } if (Attributes != null) { foreach (var(key, value) in Attributes) { builder.AddAttribute(i++, key, value); } } builder.CloseComponent(); }
/// <summary> /// Secures an entity by an enumerable property. /// </summary> /// <typeparam name="TK">The type to secure by.</typeparam> /// <typeparam name="TId">The type of the IDs to secure by.</typeparam> /// <param name="pathToCollection">An expression representing the path from an instance of T to the property of Type TK that T should be by.</param> /// <param name="whenNull">The behaviour to use when entitlements are null.</param> /// <returns>A Relationship representing how T should be by TK.</returns> public SecuredRelationship ByCollection <TK, TId>(Expression <Func <T, IEnumerable <TK> > > pathToCollection, WhenNull whenNull) where TK : IIdentifiable <TId> where TId : IEquatable <TId> { var closuredLambdaBuilder = PredicateBuilder.BuildPredicateFactoryForCollectionProperty <T, TK, TId>(pathToCollection, whenNull); return(new SecuredRelationship(typeof(T), typeof(IEnumerable <TK>), closuredLambdaBuilder.Compile())); }
public static LambdaExpression BuildPredicateFactoryForCollectionProperty(Type rootType, Type typeInCollection, Type keyType, LambdaExpression pathToRelationship, WhenNull whenNull) { // We are building one of the below expression factories // ids => return x => !x.CollectionProperty.Any() || x.CollectionProperty.Any(c => ids.Contains(c.Id)) // ids => return x => x.CollectionProperty.Any() && x.CollectionProperty.Any(c => ids.Contains(c.Id)) // Create the constructed generic type of Func<T, bool> without having access to a generic parameter of T ( the parameter rootType is the result of typeof(T) ) var typeForRootPredicate = MakeFuncOfTBool(rootType); // Create a parameter that represents the object from the collection, this will later become the {parameter of typeInCollection} => part of a lambda var parameterForSingleId = Expression.Parameter(typeInCollection); // Create an Expression to turn the parameter into an accessor like {parameter of typeInCollection}.Id, when combined in a lambda this will become {parameter of typeInCollection} => {parameter of typeInCollection}.Id var accessorForSingleId = Expression.PropertyOrField(parameterForSingleId, "Id"); // Reflect the generic method information from the keyType for checking later. This will be IEnumberable<keyType>.Contains({parameter of keyType}) var containsMethod = MakeContainsMethod(keyType, 2); // Create a parameter that represents the type of list of Ids that your user is entitled to access, this is IEnumerable<keyType> var typeForEntitledIdsParameter = MakeEnumerableOfT(keyType); // Create a parameter that represents the list of Ids that your user is entitled to access, created from the above type var parameterForEntitledIds = MakeIdsParameter(typeForEntitledIdsParameter); // Reflect LINQ methods to get the .Any() and .Any(x => [lambda predicate here]) methods var reflectedMethodInfoForLinqAnyThatDoesNotTakeParameters = MakeAnyMethod(typeInCollection, 1); var reflectedMethodInfoForLinqAnyThatTakesAPredicate = MakeAnyMethod(typeInCollection, 2); // Create an expression that given a collection of Ids calls contains, and passes the Id accessor from above. Outputs - ids.Contains({parameter of typeInCollection}.Id) var containsMethodCall = Expression.Call(null, containsMethod, parameterForEntitledIds, accessorForSingleId); // Create a lambda from the above method call that closures in a reference to the type in collection. Outputs - {parameter of typeInCollection} => ids.Contains({parameter of typeInCollection}.Id) var containsPredicate = Expression.Lambda(containsMethodCall, parameterForSingleId); // Create a placeholder for variations of which where predicate we are going to need based on null behavior. Expression wherePredicate; switch (whenNull) { // When we find a null object, the security expression should treat this the same as finding an object the user has permissions to case WhenNull.Allow: { // Build the follow predicate x => !x.CollectionProperty.Any() || x.CollectionProperty.Any(c => ids.Contains(c.Id)) wherePredicate = BuildAllowWhenNullWherePredicate(pathToRelationship, typeForRootPredicate, reflectedMethodInfoForLinqAnyThatTakesAPredicate, reflectedMethodInfoForLinqAnyThatDoesNotTakeParameters, containsPredicate); } break; // When we find a null object, the security expression should treat this the same as finding an object that the user doesn't have permission to case WhenNull.Deny: { // Build the following predicate x => x.CollectionProperty.Any() && x.CollectionProperty.Any(c => ids.Contains(c.Id)) wherePredicate = BuildDenyWhenNullWherePredicate(pathToRelationship, typeForRootPredicate, reflectedMethodInfoForLinqAnyThatDoesNotTakeParameters, reflectedMethodInfoForLinqAnyThatTakesAPredicate, containsPredicate); } break; default: { throw new NotImplementedException("Allow and Deny are the only supported behaviors. Stop modifying the enum without fixing the expression generator too!!"); } } // Return a expression that when compiled will build a where predicate on execution. ids => { predicate that takes ids from above } return(Expression.Lambda(Expression.Quote(wherePredicate), parameterForEntitledIds)); }
public static LambdaExpression BuildPredicateFactoryForCollectionProperty <T, TK, TId>(Expression <Func <T, IEnumerable <TK> > > pathToRelationship, WhenNull whenNull) where TK : IIdentifiable <TId> where TId : IEquatable <TId> { return(BuildPredicateFactoryForCollectionProperty(typeof(T), typeof(TK), typeof(TId), pathToRelationship, whenNull)); }
public static LambdaExpression BuildPredicateFactoryForSingleProperty(Type rootType, Type typeInProperty, Type keyType, LambdaExpression pathToRelationship, WhenNull whenNull) { // We are building one of the below expression factories // ids => return x => x.SingleProperty == null || ids.Contains(x.SingleProperty.Id) // ids => return x => ids.Contains(x.SingleProperty.Id) // Create the constructed generic type of Func<T, bool> without having access to a generic parameter of T ( the parameter rootType is the result of typeof(T) ) var typeForRootPredicate = MakeFuncOfTBool(rootType); // Create an Expression to turn the parameter into an accessor like {parameter of rootType}.SingleProperty.Id, when combined in a lambda this will become {parameter of rootType} => {parameter of rootType}.SingleProperty.Id var accessorForSingleId = Expression.PropertyOrField(pathToRelationship.Body, "Id"); // Create a parameter that represents the type of list of Ids that your user is entitled to access, this is IEnumerable<keyType> var typeForEntitledIdsParameter = MakeEnumerableOfT(keyType); // Create a parameter that represents the of list of Ids that your user is entitled to access, created from the above type var parameterForEntitledIds = MakeIdsParameter(typeForEntitledIdsParameter); // Reflect the generic method infromation from the keyType for checking later. This will be IEnumberable<keyType>.Contains({parameter of keyType}) var containsMethod = MakeContainsMethod(keyType, 2); // Create an expression that given a collection of Ids calls contains, and passes the Id accessor from above. Outputs - ids.Contains({parameter of rootType}.Id) var containsMethodCall = Expression.Call(null, containsMethod, parameterForEntitledIds, accessorForSingleId); // Create a placeholder for variations of which where predicate we are going to need based on null behavior. Expression wherePredicate; switch (whenNull) { // When we find a null object, the security expression should treat this the same as finding an object the user has permissions to case WhenNull.Allow: { // Create a check for property = null and add it in an or to the lambda var nullCheckOnProp = Expression.Equal(pathToRelationship.Body, Expression.Constant(null, typeInProperty)); // Create the lambda ( x => x.SingleProperty == null || ids.Contains(x.SingleProperty.Id) ) wherePredicate = Expression.Lambda(typeForRootPredicate, Expression.OrElse(nullCheckOnProp, containsMethodCall), pathToRelationship.Parameters); } break; // When we find a null object, the security expression should treat this the same as finding an object that the user doesn't have permission to case WhenNull.Deny: { // Create the lambda ( x => ids.Contains(x.SingleProperty.Id) ) wherePredicate = Expression.Lambda(typeForRootPredicate, containsMethodCall, pathToRelationship.Parameters); } break; default: { throw new NotImplementedException("Allow and Deny are the only supported behaviors, stop modifying the enum without fixing the expression generator too!!"); } } // Return a expression that when compiled will build a where predicate on execution. ids => { predicate that takes ids from above } return(Expression.Lambda(Expression.Quote(wherePredicate), parameterForEntitledIds)); }