コード例 #1
0
        public static Func <T, bool> ToPredicate <T>(this PathFilterExpression filterExpression)
        {
            if (filterExpression == null)
            {
                return(user => true);
            }

            if (string.IsNullOrWhiteSpace(filterExpression.Filter))
            {
                throw new Exception("Invalid scim filter"); // TODO: (DG) exception handling
            }
            // parse our filter into an expression tree
            var declaryingType = filterExpression.Path == null
                ? typeof(T)
                : typeof(T)
                                 .GetProperty(filterExpression.Path, BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase)
                                 .PropertyType;

            // if enumerable, get generic argument
            if (declaryingType.IsNonStringEnumerable())
            {
                declaryingType = declaryingType.GetGenericArguments()[0];
            }

            var lexer  = new ScimFilterLexer(new AntlrInputStream(filterExpression.Filter));
            var parser = new ScimFilterParser(new CommonTokenStream(lexer));

            // create a visitor for the type of enumerable generic argument
            var filterVisitorType        = typeof(ScimFilterVisitor <>).MakeGenericType(declaryingType);
            var filterVisitor            = (IScimFilterVisitor)filterVisitorType.CreateInstance();
            var filterDelegateExpression = filterVisitor.VisitExpression(parser.parse());

            if (filterExpression.Path == null)
            {
                return(filterDelegateExpression.Compile().AsFunc <T, bool>());
            }

            // TODO: (DG) add more comments
            var resourceArg            = Expression.Parameter(typeof(T));
            var resourceComplexAttr    = Expression.Property(resourceArg, filterExpression.Path);
            var invokeFilterExpression = Expression.Invoke(filterDelegateExpression, resourceComplexAttr);
            var predicate = Expression.Lambda <Func <T, bool> >(invokeFilterExpression, resourceArg);

            return(predicate.Compile().AsFunc <T, bool>());
        }
コード例 #2
0
        private IEnumerable<Node> GetAffectedMembers(
            IList<PathFilterExpression> pathTree, 
            ref int lastPosition,
            Node node)
        {
            for (int i = lastPosition; i < pathTree.Count; i++)
            {
                // seems absurd, but this MAY be called recursively, OR simply
                // iterated via the for loop
                lastPosition = i; 
                
                var jsonContract = (JsonObjectContract)_ContractResolver.ResolveContract(node.Target.GetType());
                var attemptedProperty = jsonContract.Properties.GetClosestMatchProperty(pathTree[i].Path);
                if (attemptedProperty == null)
                {
                    if (!_ServerConfiguration.ResourceExtensionExists(pathTree[i].Path))
                    {
                        // property cannot be found, and we're not working with an extension.
                        ErrorType = ScimErrorType.InvalidPath;
                        break;
                    }

                    // this is a resource extension
                    // TODO: (DG) the code below works as well and will remove once it's determined how 
                    // repositories will access and persist extension data.  Currently, Extensions property is public.
//                    var memberInfo = node.Target.GetType().GetProperty("Extensions", BindingFlags.NonPublic | BindingFlags.Instance);
//                    var property = new JsonProperty
//                    {
//                        PropertyType = memberInfo.PropertyType,
//                        DeclaringType = memberInfo.DeclaringType,
//                        ValueProvider = new ReflectionValueProvider(memberInfo),
//                        AttributeProvider = new ReflectionAttributeProvider(memberInfo),
//                        Readable = true,
//                        Writable = true,
//                        ShouldSerialize = member => false
//                    };

                    attemptedProperty = jsonContract.Properties.GetProperty("extensions", StringComparison.Ordinal);
                }

                // if there's nothing to filter and we're not yet at the last path entry, continue
                if (pathTree[i].Filter == null && i != pathTree.Count - 1)
                {
                    // if they enter an invalid target 
                    if (attemptedProperty.PropertyType.IsTerminalObject())
                    {
                        ErrorType = ScimErrorType.InvalidPath;
                        break;
                    }

                    object targetValue;
                    var propertyType = attemptedProperty.PropertyType;

                    // support for resource extensions
                    if (propertyType == typeof (ResourceExtensions))
                    {
                        var resourceExtensions = (ResourceExtensions) attemptedProperty.ValueProvider.GetValue(node.Target);
                        var extensionType = _ServerConfiguration.GetResourceExtensionType(node.Target.GetType(), pathTree[i].Path);
                        if (_Operation.OperationType == OperationType.Remove && !resourceExtensions.Contains(extensionType))
                            break;

                        targetValue = resourceExtensions.GetOrCreate(extensionType);
                    }
                    else
                    {
                        // if targetValue is null, then we need to initialize it, UNLESS we're in a remove operation
                        // e.g. user.name.givenName, when name == null
                        targetValue = attemptedProperty.ValueProvider.GetValue(node.Target);
                        if (targetValue == null)
                        {
                            if (_Operation.OperationType == OperationType.Remove)
                                break;

                            if (!propertyType.IsNonStringEnumerable())
                            {
                                // if just a normal complex type, just create a new instance
                                targetValue = propertyType.CreateInstance();
                            }
                            else
                            {
                                var enumerableInterface = propertyType.GetEnumerableType();
                                var listArgumentType = enumerableInterface.GetGenericArguments()[0];
                                var listType = typeof (List<>).MakeGenericType(listArgumentType);
                                targetValue = listType.CreateInstance();
                            }

                            attemptedProperty.ValueProvider.SetValue(node.Target, targetValue);
                        }
                    }

                    // the Target becomes the Target's child property value
                    // the Parent becomes the current Target
                    node = new Node(targetValue, node.Target);
                    continue; // keep traversing the path tree
                }
                    
                if (pathTree[i].Filter != null)
                {
                    // we can only filter enumerable types
                    if (!attemptedProperty.PropertyType.IsNonStringEnumerable())
                    {
                        ErrorType = ScimErrorType.InvalidFilter;
                        break;
                    }

                    var enumerable = attemptedProperty.ValueProvider.GetValue(node.Target) as IEnumerable;
                    if (enumerable == null)
                    {
                        // if the value of the attribute is null then there's nothing to filter
                        // it should never get here beause ScimObjectAdapter should apply a 
                        // different ruleset for null values; replacing or setting the attribute value
                        ErrorType = ScimErrorType.NoTarget;
                        break;
                    }
                    
                    dynamic predicate;
                    try
                    {
                        // parse our filter into an expression tree
                        var lexer = new ScimFilterLexer(new AntlrInputStream(pathTree[i].Filter));
                        var parser = new ScimFilterParser(new CommonTokenStream(lexer));

                        // create a visitor for the type of enumerable generic argument
                        var enumerableType = attemptedProperty.PropertyType.GetGenericArguments()[0];
                        var filterVisitorType = typeof (ScimFilterVisitor<>).MakeGenericType(enumerableType);
                        var filterVisitor = (IScimFilterVisitor) filterVisitorType.CreateInstance(_ServerConfiguration);
                        predicate = filterVisitor.VisitExpression(parser.parse()).Compile();
                    }
                    catch (Exception)
                    {
                        ErrorType = ScimErrorType.InvalidFilter;
                        break;
                    }

                    // we have an enumerable and a filter predicate
                    // for each element in the enumerable that satisfies the predicate, 
                    // visit that element as part of the path tree
                    var originalHasElements = false;
                    var filteredNodes = new List<Node>();
                    var enumerator = enumerable.GetEnumerator();
                    lastPosition = i + 1; // increase the position in the tree
                    while (enumerator.MoveNext())
                    {
                        originalHasElements = true;
                        dynamic currentElement = enumerator.Current;
                        if ((bool) predicate(currentElement))
                        {
                            filteredNodes.AddRange(
                                GetAffectedMembers(
                                    pathTree,
                                    ref lastPosition,
                                    new Node(enumerator.Current, node.Target)));
                        }
                    }

                    /* SCIM PATCH 'replace' RULE:
                        o  If the target location is a multi-valued attribute for which a
                            value selection filter ("valuePath") has been supplied and no
                            record match was made, the service provider SHALL indicate failure
                            by returning HTTP status code 400 and a "scimType" error code of
                            "noTarget".
                    */
                    if (originalHasElements &&
                        filteredNodes.Count == 0 &&
                        _Operation != null &&
                        _Operation.OperationType == OperationType.Replace)
                    {
                        throw new ScimPatchException(
                            ScimErrorType.NoTarget, _Operation);
                    }

                    return filteredNodes;
                }
            }

            return new List<Node> { node };
        }
コード例 #3
0
        private IEnumerable <Node> GetAffectedMembers(
            IList <PathFilterExpression> pathTree,
            ref int lastPosition,
            Node node)
        {
            for (int i = lastPosition; i < pathTree.Count; i++)
            {
                // seems absurd, but this MAY be called recursively, OR simply
                // iterated via the for loop
                lastPosition = i;

                var jsonContract      = (JsonObjectContract)_ContractResolver.ResolveContract(node.Target.GetType());
                var attemptedProperty = jsonContract.Properties.GetClosestMatchProperty(pathTree[i].Path);
                if (attemptedProperty == null)
                {
                    if (!_ServerConfiguration.ResourceExtensionExists(pathTree[i].Path))
                    {
                        // property cannot be found, and we're not working with an extension.
                        ErrorType = ScimErrorType.InvalidPath;
                        break;
                    }

                    // this is a resource extension
                    // TODO: (DG) the code below works as well and will remove once it's determined how
                    // repositories will access and persist extension data.  Currently, Extensions property is public.
//                    var memberInfo = node.Target.GetType().GetProperty("Extensions", BindingFlags.NonPublic | BindingFlags.Instance);
//                    var property = new JsonProperty
//                    {
//                        PropertyType = memberInfo.PropertyType,
//                        DeclaringType = memberInfo.DeclaringType,
//                        ValueProvider = new ReflectionValueProvider(memberInfo),
//                        AttributeProvider = new ReflectionAttributeProvider(memberInfo),
//                        Readable = true,
//                        Writable = true,
//                        ShouldSerialize = member => false
//                    };

                    attemptedProperty = jsonContract.Properties.GetProperty("extensions", StringComparison.Ordinal);
                }

                // if there's nothing to filter and we're not yet at the last path entry, continue
                if (pathTree[i].Filter == null && i != pathTree.Count - 1)
                {
                    // if they enter an invalid target
                    if (attemptedProperty.PropertyType.IsTerminalObject())
                    {
                        ErrorType = ScimErrorType.InvalidPath;
                        break;
                    }

                    object targetValue;
                    var    propertyType = attemptedProperty.PropertyType;

                    // support for resource extensions
                    if (propertyType == typeof(ResourceExtensions))
                    {
                        var resourceExtensions = (ResourceExtensions)attemptedProperty.ValueProvider.GetValue(node.Target);
                        var extensionType      = _ServerConfiguration.GetResourceExtensionType(node.Target.GetType(), pathTree[i].Path);
                        if (_Operation.OperationType == OperationType.Remove && !resourceExtensions.Contains(extensionType))
                        {
                            break;
                        }

                        targetValue = resourceExtensions.GetOrCreate(extensionType);
                    }
                    else
                    {
                        // if targetValue is null, then we need to initialize it, UNLESS we're in a remove operation
                        // e.g. user.name.givenName, when name == null
                        targetValue = attemptedProperty.ValueProvider.GetValue(node.Target);
                        if (targetValue == null)
                        {
                            if (_Operation.OperationType == OperationType.Remove)
                            {
                                break;
                            }

                            if (!propertyType.IsNonStringEnumerable())
                            {
                                // if just a normal complex type, just create a new instance
                                targetValue = propertyType.CreateInstance();
                            }
                            else
                            {
                                var enumerableInterface = propertyType.GetEnumerableType();
                                var listArgumentType    = enumerableInterface.GetGenericArguments()[0];
                                var listType            = typeof(List <>).MakeGenericType(listArgumentType);
                                targetValue = listType.CreateInstance();
                            }

                            attemptedProperty.ValueProvider.SetValue(node.Target, targetValue);
                        }
                    }

                    // the Target becomes the Target's child property value
                    // the Parent becomes the current Target
                    node = new Node(targetValue, node.Target);
                    continue; // keep traversing the path tree
                }

                if (pathTree[i].Filter != null)
                {
                    // we can only filter enumerable types
                    if (!attemptedProperty.PropertyType.IsNonStringEnumerable())
                    {
                        ErrorType = ScimErrorType.InvalidFilter;
                        break;
                    }

                    var enumerable = attemptedProperty.ValueProvider.GetValue(node.Target) as IEnumerable;
                    if (enumerable == null)
                    {
                        // if the value of the attribute is null then there's nothing to filter
                        // it should never get here beause ScimObjectAdapter should apply a
                        // different ruleset for null values; replacing or setting the attribute value
                        ErrorType = ScimErrorType.NoTarget;
                        break;
                    }

                    dynamic predicate;
                    try
                    {
                        // parse our filter into an expression tree
                        var lexer  = new ScimFilterLexer(new AntlrInputStream(pathTree[i].Filter));
                        var parser = new ScimFilterParser(new CommonTokenStream(lexer));

                        // create a visitor for the type of enumerable generic argument
                        var enumerableType    = attemptedProperty.PropertyType.GetGenericArguments()[0];
                        var filterVisitorType = typeof(ScimFilterVisitor <>).MakeGenericType(enumerableType);
                        var filterVisitor     = (IScimFilterVisitor)filterVisitorType.CreateInstance(_ServerConfiguration);
                        predicate = filterVisitor.VisitExpression(parser.parse()).Compile();
                    }
                    catch (Exception)
                    {
                        ErrorType = ScimErrorType.InvalidFilter;
                        break;
                    }

                    // we have an enumerable and a filter predicate
                    // for each element in the enumerable that satisfies the predicate,
                    // visit that element as part of the path tree
                    var originalHasElements = false;
                    var filteredNodes       = new List <Node>();
                    var enumerator          = enumerable.GetEnumerator();
                    lastPosition = i + 1; // increase the position in the tree
                    while (enumerator.MoveNext())
                    {
                        originalHasElements = true;
                        dynamic currentElement = enumerator.Current;
                        if ((bool)predicate(currentElement))
                        {
                            filteredNodes.AddRange(
                                GetAffectedMembers(
                                    pathTree,
                                    ref lastPosition,
                                    new Node(enumerator.Current, node.Target)));
                        }
                    }

                    /* SCIM PATCH 'replace' RULE:
                     *  o  If the target location is a multi-valued attribute for which a
                     *      value selection filter ("valuePath") has been supplied and no
                     *      record match was made, the service provider SHALL indicate failure
                     *      by returning HTTP status code 400 and a "scimType" error code of
                     *      "noTarget".
                     */
                    if (originalHasElements &&
                        filteredNodes.Count == 0 &&
                        _Operation != null &&
                        _Operation.OperationType == OperationType.Replace)
                    {
                        throw new ScimPatchException(
                                  ScimErrorType.NoTarget, _Operation);
                    }

                    return(filteredNodes);
                }
            }

            return(new List <Node> {
                node
            });
        }