Example #1
0
        public BindContext(RestRequest request, Snippet <MethodInfo> snippet)
        {
            Request = request.AssertNotNull();
            Snippet = snippet.AssertNotNull();
            Debug.EnsureBlankLine();
            Debug.WriteLine("Binding request to {0}...", snippet.Member.GetCSharpRef(ToCSharpOptions.InformativeWithNamespaces));

            var asmUri      = AsmResource.Uri ?? String.Empty;
            var typeUri     = TypeResource.Uri ?? String.Empty;
            var methodUri   = MethodResource.Uri ?? String.Empty;
            var uriTemplate = asmUri + typeUri + methodUri;

            ResourceTemplate = uriTemplate;
            Debug.WriteLine("    * Resource template: {0}", ResourceTemplate);

            var parsed1 = new Url(request.Resource).Parse(uriTemplate);
            var parsed2 = new Url(request.Resource + "/").Parse(uriTemplate);
            var parsed  = parsed1 ?? parsed2;

            Resource       = request.Resource;
            ParsedResource = parsed;
            Debug.WriteLine("    * Resource: {0}", Resource);

            if (parsed != null)
            {
                Debug.WriteLine("    + Resource matches the template");

                var code = snippet.Member;
                HandlerCode    = code;
                HandlerContext = new HandlerContext(request, parsed, code);
                Handler        = () =>
                {
                    HandlerContext.Validate().AssertTrue();
                    var result = HandlerContext.Invoke();

                    var response = request.Response;
                    var is_json  = request["Content-Type"] == "application/json";
                    if (is_json)
                    {
                        response.Succeed(new Json(result));
                    }
                    else
                    {
                        response.Succeed(result);
                    }
                };

                if (HandlerContext.Validate())
                {
                    Debug.WriteLine("    + Handler arguments are bound successfully");

                    var asmMethods     = AsmResource.Allow;
                    var typeMethods    = TypeResource.Allow;
                    var methodMethods  = MethodResource.Allow;
                    var allowedMethods = asmMethods & typeMethods & methodMethods;
                    AllowedMethods = allowedMethods;
                    Debug.WriteLine("    * Allowed HTTP methods: " + AllowedMethods);

                    var allowMethod = false;
                    allowMethod |= (request.Method == "GET" && ((allowedMethods & RestMethods.Get) != 0));
                    allowMethod |= (request.Method == "PUT" && ((allowedMethods & RestMethods.Put) != 0));
                    allowMethod |= (request.Method == "POST" && ((allowedMethods & RestMethods.Post) != 0));
                    allowMethod |= (request.Method == "MERGE" && ((allowedMethods & RestMethods.Merge) != 0));
                    allowMethod |= (request.Method == "DELETE" && ((allowedMethods & RestMethods.Delete) != 0));
                    Method       = request.Method;
                    Debug.WriteLine("    * HTTP method: " + Method);

                    if (allowMethod)
                    {
                        Debug.WriteLine("    + HTTP method is allowed");

                        var authenticators      = SnippetRegistry.Methods <AuthenticationFilterAttribute>();
                        var skipAuthentication  = AsmResource.SkipAuthentication || TypeResource.SkipAuthentication || MethodResource.SkipAuthentication;
                        var boundAuthenticators = authenticators.Select(authenticator =>
                        {
                            Debug.WriteLine("    * Authorizing against {0}...", authenticator.Member.GetCSharpRef(ToCSharpOptions.InformativeWithNamespaces));
                            return(new HandlerContext(request, parsed, authenticator.Member));
                        }).ToReadOnly();
                        var authenticated = skipAuthentication || boundAuthenticators.All(authenticator => authenticator.Validate() && Equals(authenticator.Invoke(), true));

                        if (authenticated)
                        {
                            Debug.WriteLine("    + Authentication successful");

                            var authorizers       = SnippetRegistry.Methods <AuthorizationFilterAttribute>();
                            var skipAuthorization = AsmResource.SkipAuthorization || TypeResource.SkipAuthorization || MethodResource.SkipAuthorization;
                            var boundAuthorizers  = authorizers.Select(authorizer =>
                            {
                                Debug.WriteLine("    * Authorizing against {0}...", authorizer.Member.GetCSharpRef(ToCSharpOptions.InformativeWithNamespaces));
                                return(new HandlerContext(request, parsed, authorizer.Member));
                            }).ToReadOnly();
                            var authorized = skipAuthorization || boundAuthorizers.All(authorizer => authorizer.Validate() && Equals(authorizer.Invoke(), true));

                            if (authorized)
                            {
                                Debug.WriteLine("    + Authorization successful");
                                Debug.WriteLine("    * Bind successful");
                                Result = BindResult.Success;
                            }
                            else
                            {
                                Debug.WriteLine("    FAIL => Failed to authorize");
                                Result = BindResult.NotAuthorized;
                            }
                        }
                        else
                        {
                            Debug.WriteLine("    FAIL => Failed to authenticate");
                            Result = BindResult.NotAuthenticated;
                        }
                    }
                    else
                    {
                        Debug.WriteLine("    FAIL => HTTP method is not allowed");
                        Result = BindResult.MethodMismatch;
                    }
                }
                else
                {
                    Debug.WriteLine("    FAIL => Failed to bind handler arguments");
                    Result = BindResult.ArgsMismatch;
                }
            }
            else
            {
                Debug.WriteLine("FAIL => Resource doesn't match the template");
                Result = BindResult.UrlMismatch;
            }
        }
Example #2
0
        public DispatchContext(RestRequest req)
        {
            Info.EnsureBlankLine();
            Info.Write("Dispatching request ");
            var snippets = SnippetRegistry.Methods <RestResourceAttribute>();

            Info.WriteLine("(across {0} handlers)...", snippets.Count());
            BindResults = snippets.Select(snippet => new BindContext(req, snippet)).ToReadOnly();

            // todo. maybe invent some novel way to do dispatch
            // so that "http://localhost/daas/ldab/Users?$filter=substringof('Bu', Name)"
            // doesn't get dispatched to "/{relativeFilePath}" and cause a crash with 400
            // but rather becomes a 404 because it's just "/daas/ldap/{s_dataQuery}" with a typo
            var bestMatch = PickBestMatch(BindResults);

            if (bestMatch == null)
            {
                if (BindResults.Any(r => r.Result == BindResult.UrlMismatch))
                {
                    Result = DispatchResult.UrlMismatch;
                }
                if (BindResults.Any(r => r.Result == BindResult.ArgsMismatch))
                {
                    Result = DispatchResult.ArgsMismatch;
                }
                if (BindResults.Any(r => r.Result == BindResult.MethodMismatch))
                {
                    Result = DispatchResult.MethodNotAllowed;
                }
                if (BindResults.Any(r => r.Result == BindResult.NotAuthenticated))
                {
                    Result = DispatchResult.NotAuthenticated;
                }
                if (BindResults.Any(r => r.Result == BindResult.NotAuthorized))
                {
                    Result = DispatchResult.NotAuthorized;
                }
                if (BindResults.Count(r => r.Result == BindResult.Success) > 1)
                {
                    Result = DispatchResult.Ambiguous;
                }
            }
            else
            {
                BestMatch = bestMatch;
                Result    = (DispatchResult)Enum.Parse(typeof(DispatchResult), bestMatch.Result.ToString());
            }

            Info.EnsureBlankLine();
            Info.WriteLine("Dispatch summary: {0}", Result);
            Info.WriteLine(BindResults.Select(r => "    * " + r.ToString()).StringJoin(Environment.NewLine));

            Info.EnsureBlankLine();
            if (Result != DispatchResult.Success)
            {
                Error.WriteLine("Dispatch has failed with status code {0}", Result.ToHttpStatusCode());
            }
            else
            {
                Info.WriteLine("Bound handler: {0}", BestMatch.HandlerContext);
            }
        }