Ejemplo n.º 1
0
        public async Task <AuthenticateResult> AuthenticateAsync(HttpRequestBase request, HttpResponseBase response)
        {
            request.ThrowIfNull("request");
            response.ThrowIfNull("response");

            if (_authenticationProvider == null)
            {
                return(AuthenticateResult.NoAuthenticationPerformed());
            }

            AuthenticationResult result = await _authenticationProvider.AuthenticateAsync(request, response, this);

            return(result == AuthenticationResult.AuthenticationSucceeded
                                ? AuthenticateResult.AuthenticationSucceeded()
                                : AuthenticateResult.AuthenticationFailed(await _authenticationProvider.GetFailedAuthenticationResponseAsync(request)));
        }
Ejemplo n.º 2
0
        public async Task <ClaimsIdentity> AuthenticateAsync(HttpRequest httpRequest, HttpResponse httpResponse)
        {
            if (httpRequest == null)
            {
                throw new ArgumentNullException(nameof(httpRequest));
            }

            if (httpResponse == null)
            {
                throw new ArgumentNullException(nameof(httpResponse));
            }

            var authorizationHeader = httpRequest.Headers["Authorization"];

            if (string.IsNullOrWhiteSpace(authorizationHeader))
            {
                httpResponse.StatusCode = (int)HttpStatusCode.Unauthorized;
                return(null);
            }

            var claimsIdentity = await _authenticationProvider.AuthenticateAsync(authorizationHeader).ConfigureAwait(false);

            if (claimsIdentity == null)
            {
                httpResponse.StatusCode = (int)HttpStatusCode.Unauthorized;
                return(null);
            }

            var appIdClaimName = AuthHelpers.GetAppIdClaimName(claimsIdentity);
            var appId          = claimsIdentity.Claims.FirstOrDefault(c => c.Type == appIdClaimName)?.Value;

            if (_whitelistAuthenticationProvider.AppsWhitelist != null &&
                _whitelistAuthenticationProvider.AppsWhitelist.Count > 0 &&
                !_whitelistAuthenticationProvider.AppsWhitelist.Contains(appId))
            {
                httpResponse.StatusCode = (int)HttpStatusCode.Unauthorized;
                await httpResponse.WriteAsync("Skill could not allow access from calling bot.").ConfigureAwait(false);
            }

            return(claimsIdentity);
        }
        public async Task <HttpResponseMessage> InvokeAsync(HttpMethod verb, string resource, Dictionary <string, string> queryParams = null,
                                                            Dictionary <string, object> headers = null, Func <CancellationToken, Task <HttpContent> > contentFactory = null,
                                                            bool bufferResponseContent          = true, bool skipAuthentication = false, CancellationToken cancellationToken = default)
        {
            if (verb == null)
            {
                throw new ArgumentNullException(nameof(verb));
            }
            if (resource == null)
            {
                throw new ArgumentNullException(nameof(resource));
            }

            // Performs a maximum of as many attempts as the number of configured base API endpoints, keeping track
            // of the last used endpoint after each call, in order to try to distribute the load evenly across the
            // available endpoints.

            var errors = new Dictionary <Url, Exception>();

            for (var idxAttempt = 0; idxAttempt < _baseUrls.Length; idxAttempt++, _currentBaseUrlIdx++)
            {
                cancellationToken.ThrowIfCancellationRequested();

                // Authenticates the underlying flurl client, if needed

                if (!skipAuthentication)
                {
                    await _authenticator.AuthenticateAsync(this, cancellationToken)
                    .ConfigureAwait(false);
                }

                // Build the final url by combining the base url and the specified path and query

                var baseUrl  = _baseUrls[_currentBaseUrlIdx % _baseUrls.Length];
                var finalUrl = new Url(baseUrl)
                               .AppendPathSegment(resource);

                // Add the eventual querystring parameters

                if (queryParams != null)
                {
                    foreach (var queryParam in queryParams)
                    {
                        finalUrl = finalUrl.SetQueryParam(queryParam.Key, queryParam.Value);
                    }
                }

                var request = UnderlyingClient.Request(finalUrl);

                // Add the eventual custom headers

                if (headers != null)
                {
                    foreach (var queryParam in headers)
                    {
                        request = request.WithHeader(queryParam.Key, queryParam.Value);
                    }
                }

                try
                {
                    // HttpClient.SendAsync() disposed the passed content automatically: https://github.com/dotnet/runtime/issues/14612
                    // A fix was made (https://github.com/dotnet/corefx/pull/19082) but Flurl still targets .NET Standard and not .NET Core,
                    // so we can't take advantage of it: because of that, we are using a factory to build the actual content.

                    HttpContent         content = null;
                    HttpResponseMessage response;

                    try
                    {
                        if (contentFactory != null)
                        {
                            content = await contentFactory(cancellationToken)
                                      .ConfigureAwait(false);
                        }

                        response = await request
                                   .SendAsync(verb,
                                              content,
                                              cancellationToken,
                                              bufferResponseContent
                                              ?HttpCompletionOption.ResponseContentRead
                                              : HttpCompletionOption.ResponseHeadersRead)
                                   .ConfigureAwait(false);
                    }
                    finally
                    {
                        // Dispose the eventual content - that should make sense whenever .NET Standard will receive the fix mentioned above
                        // or whenever Flurl will target .NET Core.

                        content?.Dispose();
                    }

                    // Automatically retry with another host on HTTP 5xx status codes

                    if ((int)response.StatusCode >= 500 && (int)response.StatusCode <= 599)
                    {
                        errors.Add(finalUrl, new EndpointServerErrorException($"The API endpoint {baseUrl} returned a server error HTTP status code {response.StatusCode}."));
                        continue;
                    }

                    // If the request is unauthorized, give the authentication provider a chance to remediate (on a subsequent attempt)

                    if (response.StatusCode == HttpStatusCode.Unauthorized)
                    {
                        await _authenticator.HandleUnauthorizedRequestAsync(this, cancellationToken)
                        .ConfigureAwait(false);

                        errors.Add(finalUrl, new AuthorizationException("Can't authenticate to Verifalia using the provided credentials (will retry in the next attempt)."));
                        continue;
                    }

                    // Fails on the first occurrence of an HTTP 403 status code

                    if (response.StatusCode == HttpStatusCode.Forbidden)
                    {
                        throw new AuthorizationException(response.ReasonPhrase);
                    }

                    // Returns the original response only if it has been completed with a non-500 HTTP status code

                    return(response);
                }
                catch (AggregateException aggregateException)
                {
                    var flattenException = aggregateException.Flatten();

                    foreach (var innerException in flattenException.InnerExceptions)
                    {
                        if (innerException is OperationCanceledException)
                        {
                            throw innerException;
                        }

                        errors.Add(finalUrl, innerException);
                    }
                }
                catch (FlurlHttpTimeoutException httpException)
                {
                    // A single Flurl request timed out - we take note of it and proceed with the next endpoint, if any

                    errors.Add(finalUrl, httpException);
                }
                catch (FlurlHttpException httpException)
                {
                    if (httpException.InnerException is OperationCanceledException && cancellationToken.IsCancellationRequested)
                    {
                        throw httpException.InnerException;
                    }

                    errors.Add(finalUrl, httpException);
                }
            }

            throw new ServiceUnreachableException("All the base URIs are unreachable: " + String.Join(", ", errors.Select(error => $"{error.Key} => {error.Value.Message}")),
                                                  new AggregateException(errors.Select(error => error.Value)));
        }