Пример #1
0
        private static HttpRequestMessage CreateProxyHttpRequest(HttpContext context, Uri uri)
        {
            var request = context.Request;

            var requestMessage = new HttpRequestMessage();
            var requestMethod  = request.Method;

            if (!HttpMethods.IsGet(requestMethod) &&
                !HttpMethods.IsHead(requestMethod) &&
                !HttpMethods.IsDelete(requestMethod) &&
                !HttpMethods.IsTrace(requestMethod))
            {
                var streamContent = new StreamContent(request.Body);
                requestMessage.Content = streamContent;
            }

            // Copy the request headers
            foreach (var header in request.Headers)
            {
                if (NotForwardedHttpHeaders.Contains(header.Key, StringComparer.OrdinalIgnoreCase))
                {
                    continue;
                }

                if (!requestMessage.Headers.TryAddWithoutValidation(header.Key, header.Value.ToArray()) && requestMessage.Content != null)
                {
                    requestMessage.Content?.Headers.TryAddWithoutValidation(header.Key, header.Value.ToArray());
                }
            }

            requestMessage.Headers.Host = uri.Authority;
            requestMessage.RequestUri   = uri;
            requestMessage.Method       = new HttpMethod(request.Method);

            return(requestMessage);
        }
            protected override bool ShouldValidate(AuthorizationFilterContext context)
            {
                if (!base.ShouldValidate(context))
                {
                    return(false);
                }

                //if (!_securitySettings.EnableXsrfProtectionForPublicStore)
                //    return false;

                ////ignore GET requests
                var request = context.HttpContext.Request;

                if (request == null)
                {
                    return(false);
                }

                if (HttpMethods.IsGet(request.Method))
                {
                    return(false);
                }

                //check whether this filter has been overridden for the Action
                var actionFilter = context.ActionDescriptor.FilterDescriptors
                                   .Where(f => f.Scope == FilterScope.Action)
                                   .Select(f => f.Filter).OfType <PublicAntiForgeryAttribute>().FirstOrDefault();

                //ignore this filter
                if (actionFilter?.IgnoreFilter ?? _ignoreFilter)
                {
                    return(false);
                }

                return(true);
            }
Пример #3
0
        public async Task ExecuteAsync(HttpContext context, HttpConnectionDispatcherOptions options, ConnectionDelegate connectionDelegate)
        {
            // Create the log scope and attempt to pass the Connection ID to it so as many logs as possible contain
            // the Connection ID metadata. If this is the negotiate request then the Connection ID for the scope will
            // be set a little later.
            var logScope = new ConnectionLogScope(GetConnectionId(context));

            using (_logger.BeginScope(logScope))
            {
                if (!await AuthorizeHelper.AuthorizeAsync(context, options.AuthorizationData))
                {
                    return;
                }

                if (HttpMethods.IsPost(context.Request.Method))
                {
                    // POST /{path}
                    await ProcessSend(context, options);
                }
                else if (HttpMethods.IsGet(context.Request.Method))
                {
                    // GET /{path}
                    await ExecuteAsync(context, connectionDelegate, options, logScope);
                }
                else if (HttpMethods.IsDelete(context.Request.Method))
                {
                    // DELETE /{path}
                    await ProcessDeleteAsync(context);
                }
                else
                {
                    context.Response.ContentType = "text/plain";
                    context.Response.StatusCode  = StatusCodes.Status405MethodNotAllowed;
                }
            }
        }
Пример #4
0
        protected virtual HttpVerbs GetVerb(HttpRequest request)
        {
            if (HttpMethods.IsGet(request.Method))
            {
                return(HttpVerbs.Get);
            }

            if (HttpMethods.IsPost(request.Method))
            {
                return(HttpVerbs.Post);
            }

            if (HttpMethods.IsPut(request.Method))
            {
                return(HttpVerbs.Put);
            }

            if (HttpMethods.IsDelete(request.Method))
            {
                return(HttpVerbs.Delete);
            }

            return(HttpVerbs.None);
        }
Пример #5
0
        public Task <IEndpointResult> ProcessAsync(HttpContext context)
        {
            _logger.LogTrace("Processing discovery request.");

            // validate HTTP
            if (!HttpMethods.IsGet(context.Request.Method))
            {
                _logger.LogWarning("Discovery endpoint only supports GET requests");
                return(Task.FromResult((IEndpointResult) new StatusCodeResult(HttpStatusCode.MethodNotAllowed)));
            }

            if (!_options.Discovery.ShowKeySet)
            {
                _logger.LogInformation("Key discovery disabled. 404.");
                return(Task.FromResult((IEndpointResult) new StatusCodeResult(HttpStatusCode.NotFound)));
            }

            // generate response
            _logger.LogTrace("Calling into discovery response generator: {type}", _responseGenerator.GetType().FullName);
            var response = _responseCache.GetOrAdd(1, _ => _responseGenerator.CreateJwkDocumentAsync().GetAwaiter().GetResult());

            _logger.LogTrace("Discovery request completed. Return JsonWebKeysResult");
            return(Task.FromResult((IEndpointResult) new JsonWebKeysResult(response, _options.Discovery.ResponseCacheInterval)));
        }
 private static HttpMethod GetMethod(string method)
 {
     if (HttpMethods.IsDelete(method))
     {
         return(HttpMethod.Delete);
     }
     if (HttpMethods.IsGet(method))
     {
         return(HttpMethod.Get);
     }
     if (HttpMethods.IsOptions(method))
     {
         return(HttpMethod.Options);
     }
     if (HttpMethods.IsPost(method))
     {
         return(HttpMethod.Post);
     }
     if (HttpMethods.IsPut(method))
     {
         return(HttpMethod.Put);
     }
     return(new HttpMethod(method));
 }
Пример #7
0
        public override async Task Invoke(HttpContext context)
        {
            if (HttpMethods.IsGet(context.Request.Method))
            {
                var tempData        = _tempDataFactory.GetTempData(context);
                var requestTempData = tempData.Get <Payment>(CloudPaymentsDefaults.TempDataKey);
                if (requestTempData == null)
                {
                    _logger.EmptyTempData(CloudPaymentsDefaults.TempDataKey);
                    context.Response.StatusCode = StatusCodes.Status404NotFound;
                    return;
                }

                tempData.Keep(CloudPaymentsDefaults.TempDataKey);
                tempData.Save();

                await new PaymentForm(new PaymentFormModel
                {
                    Amount            = requestTempData.Amount,
                    PublicId          = _clientOptions.PublicId,
                    AntiforgeryToken  = _antiforgery.GetHtml(context),
                    Action            = context.Request.GetEncodedUrl(),
                    PaymentFormHeader = requestTempData.Description
                }).ExecuteAsync(context);
                return;
            }

            if (HttpMethods.IsPost(context.Request.Method))
            {
                var valid = await _antiforgery.IsRequestValidAsync(context);

                if (!valid)
                {
                    context.Response.StatusCode = StatusCodes.Status400BadRequest;
                    return;
                }

                if (!context.Request.HasFormContentType)
                {
                    _logger.NotFormContentType();
                    context.Response.StatusCode = StatusCodes.Status400BadRequest;
                    return;
                }

                var form = await context.Request.ReadFormAsync();

                if (!(form.TryGetValue(CloudPaymentsDefaults.FormKeyName, out var name) &&
                      form.TryGetValue(CloudPaymentsDefaults.FormKeyCryptogram, out var cryptogram)))
                {
                    _logger.MissingRequiredFormKeys(CloudPaymentsDefaults.FormKeyName, CloudPaymentsDefaults.FormKeyCryptogram);
                    context.Response.StatusCode = StatusCodes.Status400BadRequest;
                    return;
                }

                var requestTempData = _tempDataFactory.GetTempData(context).Get <Payment>(CloudPaymentsDefaults.TempDataKey);
                var response        = await _client.PayCryptogram(new PaymentCryptogramRequest
                {
                    Name = name.ToString().ToUpperInvariant(),
                    CardCryptogramPacket = cryptogram.ToString(),
                    Amount      = requestTempData.Amount,
                    Currency    = requestTempData.Currency,
                    AccountId   = requestTempData.AccountId,
                    Description = requestTempData.Description,
                    Email       = requestTempData.Email,
                    InvoiceId   = requestTempData.InvoiceId,
                    JsonData    = requestTempData.JsonData,
                    IPAddress   = context.Connection.RemoteIpAddress.ToString()
                }, requestTempData.TwoStage);

                if (response.IsSecure3D)
                {
                    _tempDataFactory.GetTempData(context).Save(CloudPaymentsDefaults.TempDataKey, response);
                    var secure3dLocation = context.Request.BuildAbsolute(CloudPaymentsDefaults.Secure3dPath);
                    _logger.LocalRedirectExecuting(secure3dLocation);
                    context.Response.Redirect(secure3dLocation);
                    return;
                }

                if (response.Success)
                {
                    _logger.PaymentSuccess();
                }
                else
                {
                    _logger.PaymentFailed(response.Message);
                }

                _tempDataFactory.GetTempData(context).Save(CloudPaymentsDefaults.TempDataKey, response);
                var location = context.Request.BuildAbsolute(_options.ReturnUrl);
                _logger.LocalRedirectExecuting(location);
                context.Response.Redirect(location);
                return;
            }

            await _next(context);
        }
Пример #8
0
 public bool Matches(RouteData routeData, string httpMethod) =>
 HttpMethods.IsGet(httpMethod) &&
 string.Equals((string)routeData.Values["controller"], "orders", StringComparison.OrdinalIgnoreCase) &&
 !routeData.Values.ContainsKey("id");
        public async Task InvokeAsync(HttpContext context, ISchema schema, FillDataExtensions fillDataExtensions)
        {
            if (context.WebSockets.IsWebSocketRequest || !context.Request.Path.StartsWithSegments(_path))
            {
                await _next(context);

                return;
            }
            _fillDataExtensions = fillDataExtensions;
            // Handle requests as per recommendation at http://graphql.org/learn/serving-over-http/
            // Inspiration: https://github.com/graphql/express-graphql/blob/master/src/index.js
            var httpRequest  = context.Request;
            var httpResponse = context.Response;

            //var writer = context.RequestServices.GetRequiredService<IDocumentWriter>();
            var cancellationToken = GetCancellationToken(context);

            // GraphQL HTTP only supports GET and POST methods
            bool isGet  = HttpMethods.IsGet(httpRequest.Method);
            bool isPost = HttpMethods.IsPost(httpRequest.Method);

            if (!isGet && !isPost)
            {
                httpResponse.Headers["Allow"] = "GET, POST";
                await WriteErrorResponseAsync(context,
                                              $"Invalid HTTP method. Only GET and POST are supported. {DOCS_URL}",
                                              httpStatusCode : 405 // Method Not Allowed
                                              );

                return;
            }

            // Parse POST body
            GraphQLRequest bodyGQLRequest = null;

            GraphQLRequest[] bodyGQLBatchRequest = null;
            if (isPost)
            {
                if (!MediaTypeHeaderValue.TryParse(httpRequest.ContentType, out var mediaTypeHeader))
                {
                    await WriteErrorResponseAsync(context, $"Invalid 'Content-Type' header: value '{httpRequest.ContentType}' could not be parsed.");

                    return;
                }

                switch (mediaTypeHeader.MediaType)
                {
                case MediaType.JSON:
                    var deserializationResult = await _deserializer.DeserializeFromJsonBodyAsync(httpRequest, cancellationToken);

                    if (!deserializationResult.IsSuccessful)
                    {
                        await WriteErrorResponseAsync(context, "Body text could not be parsed. Body text should start with '{' for normal graphql query or with '[' for batched query.");

                        return;
                    }

                    bodyGQLRequest      = deserializationResult.Single;
                    bodyGQLBatchRequest = deserializationResult.Batch;
                    break;

                case MediaType.GRAPH_QL:
                    bodyGQLRequest = await DeserializeFromGraphBodyAsync(httpRequest.Body);

                    break;

                case MediaType.FORM:
                    var formCollection = await httpRequest.ReadFormAsync();

                    bodyGQLRequest = DeserializeFromFormBody(formCollection);
                    break;

                default:
                    await WriteErrorResponseAsync(context, $"Invalid 'Content-Type' header: non-supported media type. " +
                                                  $"Must be of '{MediaType.JSON}', '{MediaType.GRAPH_QL}' or '{MediaType.FORM}'. {DOCS_URL}");

                    return;
                }
            }

            // If we don't have a batch request, parse the URL too to determine the actual request to run
            // Querystring params take priority
            GraphQLRequest gqlRequest = null;

            if (bodyGQLBatchRequest == null)
            {
                // Parse URL
                var urlGQLRequest = DeserializeFromQueryString(httpRequest.Query);

                gqlRequest = new GraphQLRequest
                {
                    Query         = urlGQLRequest.Query ?? bodyGQLRequest?.Query,
                    Inputs        = urlGQLRequest.Inputs ?? bodyGQLRequest?.Inputs,
                    OperationName = urlGQLRequest.OperationName ?? bodyGQLRequest?.OperationName
                };
            }

            // Prepare context and execute
            var userContextBuilder = context.RequestServices.GetService <IUserContextBuilder>();
            var userContext        = userContextBuilder == null
                ? new Dictionary <string, object>() // in order to allow resolvers to exchange their state through this object
                : await userContextBuilder.BuildUserContext(context);

            var executer = context.RequestServices.GetRequiredService <IGraphQLExecuter <TSchema> >();

            // var executer = context.RequestServices.GetRequiredService<IDocumentExecuter>();

            // Normal execution with single graphql request
            if (bodyGQLBatchRequest == null)
            {
                var stopwatch = ValueStopwatch.StartNew();
                // var result = await ExecuteRequestAsync(schema, gqlRequest, context, executer, cancellationToken);
                var result = await ExecuteRequestAsync(gqlRequest, userContext, executer, context.RequestServices, cancellationToken);


                await RequestExecutedAsync(new GraphQLRequestExecutionResult(gqlRequest, result, stopwatch.Elapsed));

                if (result.Errors?.Count > 0)
                {
                    await WriteErrorResponseAsync(context, result);
                }
                else
                {
                    await WriteResponseAsync(context, result);
                }
            }
            // Execute multiple graphql requests in one batch
            else
            {
                var             error            = false;
                ExecutionResult executionResult  = null;
                var             executionResults = new ExecutionResult[bodyGQLBatchRequest.Length];
                for (int i = 0; i < bodyGQLBatchRequest.Length; ++i)
                {
                    var gqlRequestInBatch = bodyGQLBatchRequest[i];

                    var stopwatch = ValueStopwatch.StartNew();
                    // var result = await ExecuteRequestAsync(schema, gqlRequestInBatch, context, executer, cancellationToken);
                    var result = await ExecuteRequestAsync(gqlRequestInBatch, userContext, executer, context.RequestServices, cancellationToken);

                    await RequestExecutedAsync(new GraphQLRequestExecutionResult(gqlRequestInBatch, result, stopwatch.Elapsed, i));

                    if (result.Errors?.Count > 0)
                    {
                        error           = true;
                        executionResult = result;
                    }
                    executionResults[i] = result;
                }

                if (error)
                {
                    await WriteErrorResponseAsync(context, executionResult);
                }
                else
                {
                    // await WriteResponseAsync(context, executionResults);
                }
            }
        }
 public bool Matches(RouteData routeData, string httpVerb, HttpRequest request)
 {
     return(HttpMethods.IsGet(httpVerb) &&
            routeData.Values.ContainsKey("id"));
 }
Пример #11
0
        /// <summary>
        /// Overridden to handle remote signout requests
        /// </summary>
        /// <returns><see langword="true" /> if request processing should stop.</returns>
        public override Task <bool> HandleRequestAsync()
        {
            // RemoteSignOutPath and CallbackPath may be the same, fall through if the message doesn't match.
            if (Options.RemoteSignOutPath.HasValue && Options.RemoteSignOutPath == Request.Path && HttpMethods.IsGet(Request.Method) &&
                string.Equals(Request.Query[WsFederationConstants.WsFederationParameterNames.Wa],
                              WsFederationConstants.WsFederationActions.SignOutCleanup, StringComparison.OrdinalIgnoreCase))
            {
                // We've received a remote sign-out request
                return(HandleRemoteSignOutAsync());
            }

            return(base.HandleRequestAsync());
        }
Пример #12
0
 internal static bool IsGetOrHeadMethod(string method)
 {
     return(HttpMethods.IsGet(method) || HttpMethods.IsHead(method));
 }
        private async Task <bool> InvokeIntrospectionEndpointAsync()
        {
            OpenIdConnectRequest request;

            // See https://tools.ietf.org/html/rfc7662#section-2.1
            // and https://tools.ietf.org/html/rfc7662#section-4
            if (HttpMethods.IsGet(Request.Method))
            {
                request = new OpenIdConnectRequest(Request.Query);
            }

            else if (HttpMethods.IsPost(Request.Method))
            {
                // See http://openid.net/specs/openid-connect-core-1_0.html#FormSerialization
                if (string.IsNullOrEmpty(Request.ContentType))
                {
                    Logger.LogError("The introspection request was rejected because " +
                                    "the mandatory 'Content-Type' header was missing.");

                    return(await SendIntrospectionResponseAsync(new OpenIdConnectResponse
                    {
                        Error = OpenIdConnectConstants.Errors.InvalidRequest,
                        ErrorDescription = "The mandatory 'Content-Type' header must be specified."
                    }));
                }

                // May have media/type; charset=utf-8, allow partial match.
                if (!Request.ContentType.StartsWith("application/x-www-form-urlencoded", StringComparison.OrdinalIgnoreCase))
                {
                    Logger.LogError("The introspection request was rejected because an invalid 'Content-Type' " +
                                    "header was specified: {ContentType}.", Request.ContentType);

                    return(await SendIntrospectionResponseAsync(new OpenIdConnectResponse
                    {
                        Error = OpenIdConnectConstants.Errors.InvalidRequest,
                        ErrorDescription = "The specified 'Content-Type' header is not valid."
                    }));
                }

                request = new OpenIdConnectRequest(await Request.ReadFormAsync(Context.RequestAborted));
            }

            else
            {
                Logger.LogError("The introspection request was rejected because an invalid " +
                                "HTTP method was specified: {Method}.", Request.Method);

                return(await SendIntrospectionResponseAsync(new OpenIdConnectResponse
                {
                    Error = OpenIdConnectConstants.Errors.InvalidRequest,
                    ErrorDescription = "The specified HTTP method is not valid."
                }));
            }

            // Note: set the message type before invoking the ExtractIntrospectionRequest event.
            request.SetProperty(OpenIdConnectConstants.Properties.MessageType,
                                OpenIdConnectConstants.MessageTypes.IntrospectionRequest);

            // Store the introspection request in the ASP.NET context.
            Context.SetOpenIdConnectRequest(request);

            var @event = new ExtractIntrospectionRequestContext(Context, Scheme, Options, request);
            await Provider.ExtractIntrospectionRequest(@event);

            if (@event.Result != null)
            {
                if (@event.Result.Handled)
                {
                    Logger.LogDebug("The introspection request was handled in user code.");

                    return(true);
                }

                else if (@event.Result.Skipped)
                {
                    Logger.LogDebug("The default introspection request handling was skipped from user code.");

                    return(false);
                }
            }

            else if (@event.IsRejected)
            {
                Logger.LogError("The introspection request was rejected with the following error: {Error} ; {Description}",
                                /* Error: */ @event.Error ?? OpenIdConnectConstants.Errors.InvalidRequest,
                                /* Description: */ @event.ErrorDescription);

                return(await SendIntrospectionResponseAsync(new OpenIdConnectResponse
                {
                    Error = @event.Error ?? OpenIdConnectConstants.Errors.InvalidRequest,
                    ErrorDescription = @event.ErrorDescription,
                    ErrorUri = @event.ErrorUri
                }));
            }

            Logger.LogInformation("The introspection request was successfully extracted " +
                                  "from the HTTP request: {Request}.", request);

            if (string.IsNullOrEmpty(request.Token))
            {
                return(await SendIntrospectionResponseAsync(new OpenIdConnectResponse
                {
                    Error = OpenIdConnectConstants.Errors.InvalidRequest,
                    ErrorDescription = "The mandatory 'token' parameter is missing."
                }));
            }

            // Try to resolve the client credentials specified in the 'Authorization' header.
            // If they cannot be extracted, fallback to the client_id/client_secret parameters.
            var credentials = Request.Headers.GetClientCredentials();

            if (credentials != null)
            {
                // Reject requests that use multiple client authentication methods.
                // See https://tools.ietf.org/html/rfc6749#section-2.3 for more information.
                if (!string.IsNullOrEmpty(request.ClientSecret))
                {
                    Logger.LogError("The introspection request was rejected because " +
                                    "multiple client credentials were specified.");

                    return(await SendIntrospectionResponseAsync(new OpenIdConnectResponse
                    {
                        Error = OpenIdConnectConstants.Errors.InvalidRequest,
                        ErrorDescription = "Multiple client credentials cannot be specified."
                    }));
                }

                request.ClientId     = credentials?.Key;
                request.ClientSecret = credentials?.Value;
            }

            var context = new ValidateIntrospectionRequestContext(Context, Scheme, Options, request);
            await Provider.ValidateIntrospectionRequest(context);

            // If the validation context was set as fully validated,
            // mark the OpenID Connect request as confidential.
            if (context.IsValidated)
            {
                request.SetProperty(OpenIdConnectConstants.Properties.ConfidentialityLevel,
                                    OpenIdConnectConstants.ConfidentialityLevels.Private);
            }

            if (context.Result != null)
            {
                if (context.Result.Handled)
                {
                    Logger.LogDebug("The introspection request was handled in user code.");

                    return(true);
                }

                else if (context.Result.Skipped)
                {
                    Logger.LogDebug("The default introspection request handling was skipped from user code.");

                    return(false);
                }
            }

            else if (context.IsRejected)
            {
                Logger.LogError("The introspection request was rejected with the following error: {Error} ; {Description}",
                                /* Error: */ context.Error ?? OpenIdConnectConstants.Errors.InvalidRequest,
                                /* Description: */ context.ErrorDescription);

                return(await SendIntrospectionResponseAsync(new OpenIdConnectResponse
                {
                    Error = context.Error ?? OpenIdConnectConstants.Errors.InvalidRequest,
                    ErrorDescription = context.ErrorDescription,
                    ErrorUri = context.ErrorUri
                }));
            }

            // Store the validated client_id as a request property.
            request.SetProperty(OpenIdConnectConstants.Properties.ValidatedClientId, context.ClientId);

            Logger.LogInformation("The introspection request was successfully validated.");

            AuthenticationTicket ticket = null;

            // Note: use the "token_type_hint" parameter to determine
            // the type of the token sent by the client application.
            // See https://tools.ietf.org/html/rfc7662#section-2.1
            switch (request.TokenTypeHint)
            {
            case OpenIdConnectConstants.TokenTypeHints.AccessToken:
                ticket = await DeserializeAccessTokenAsync(request.Token, request);

                break;

            case OpenIdConnectConstants.TokenTypeHints.AuthorizationCode:
                ticket = await DeserializeAuthorizationCodeAsync(request.Token, request);

                break;

            case OpenIdConnectConstants.TokenTypeHints.IdToken:
                ticket = await DeserializeIdentityTokenAsync(request.Token, request);

                break;

            case OpenIdConnectConstants.TokenTypeHints.RefreshToken:
                ticket = await DeserializeRefreshTokenAsync(request.Token, request);

                break;
            }

            // Note: if the token can't be found using "token_type_hint",
            // the search must be extended to all supported token types.
            // See https://tools.ietf.org/html/rfc7662#section-2.1
            if (ticket == null)
            {
                // To avoid calling the same deserialization methods twice,
                // an additional check is made to exclude the corresponding
                // method when an explicit token_type_hint was specified.
                switch (request.TokenTypeHint)
                {
                case OpenIdConnectConstants.TokenTypeHints.AccessToken:
                    ticket = await DeserializeAuthorizationCodeAsync(request.Token, request) ??
                             await DeserializeIdentityTokenAsync(request.Token, request) ??
                             await DeserializeRefreshTokenAsync(request.Token, request);

                    break;

                case OpenIdConnectConstants.TokenTypeHints.AuthorizationCode:
                    ticket = await DeserializeAccessTokenAsync(request.Token, request) ??
                             await DeserializeIdentityTokenAsync(request.Token, request) ??
                             await DeserializeRefreshTokenAsync(request.Token, request);

                    break;

                case OpenIdConnectConstants.TokenTypeHints.IdToken:
                    ticket = await DeserializeAccessTokenAsync(request.Token, request) ??
                             await DeserializeAuthorizationCodeAsync(request.Token, request) ??
                             await DeserializeRefreshTokenAsync(request.Token, request);

                    break;

                case OpenIdConnectConstants.TokenTypeHints.RefreshToken:
                    ticket = await DeserializeAccessTokenAsync(request.Token, request) ??
                             await DeserializeAuthorizationCodeAsync(request.Token, request) ??
                             await DeserializeIdentityTokenAsync(request.Token, request);

                    break;

                default:
                    ticket = await DeserializeAccessTokenAsync(request.Token, request) ??
                             await DeserializeAuthorizationCodeAsync(request.Token, request) ??
                             await DeserializeIdentityTokenAsync(request.Token, request) ??
                             await DeserializeRefreshTokenAsync(request.Token, request);

                    break;
                }
            }

            if (ticket == null)
            {
                Logger.LogInformation("The introspection request was rejected because the token was invalid.");

                return(await SendIntrospectionResponseAsync(new OpenIdConnectResponse
                {
                    [OpenIdConnectConstants.Parameters.Active] = false
                }));
            }

            // Note: unlike refresh or identity tokens that can only be validated by client applications,
            // access tokens can be validated by either resource servers or client applications:
            // in both cases, the caller must be authenticated if the ticket is marked as confidential.
            if (context.IsSkipped && ticket.IsConfidential())
            {
                Logger.LogError("The introspection request was rejected because the caller was not authenticated.");

                return(await SendIntrospectionResponseAsync(new OpenIdConnectResponse
                {
                    [OpenIdConnectConstants.Parameters.Active] = false
                }));
            }

            // If the ticket is already expired, directly return active=false.
            if (ticket.Properties.ExpiresUtc.HasValue &&
                ticket.Properties.ExpiresUtc < Options.SystemClock.UtcNow)
            {
                Logger.LogInformation("The introspection request was rejected because the token was expired.");

                return(await SendIntrospectionResponseAsync(new OpenIdConnectResponse
                {
                    [OpenIdConnectConstants.Parameters.Active] = false
                }));
            }

            // When a client_id can be inferred from the introspection request,
            // ensure that the client application is a valid audience/presenter.
            if (!string.IsNullOrEmpty(context.ClientId))
            {
                if (ticket.IsAuthorizationCode() && ticket.HasPresenter() && !ticket.HasPresenter(context.ClientId))
                {
                    Logger.LogError("The introspection request was rejected because the " +
                                    "authorization code was issued to a different client.");

                    return(await SendIntrospectionResponseAsync(new OpenIdConnectResponse
                    {
                        [OpenIdConnectConstants.Parameters.Active] = false
                    }));
                }

                // Ensure the caller is listed as a valid audience or authorized presenter.
                else if (ticket.IsAccessToken() && ticket.HasAudience() && !ticket.HasAudience(context.ClientId) &&
                         ticket.HasPresenter() && !ticket.HasPresenter(context.ClientId))
                {
                    Logger.LogError("The introspection request was rejected because the access token " +
                                    "was issued to a different client or for another resource server.");

                    return(await SendIntrospectionResponseAsync(new OpenIdConnectResponse
                    {
                        [OpenIdConnectConstants.Parameters.Active] = false
                    }));
                }

                // Reject the request if the caller is not listed as a valid audience.
                else if (ticket.IsIdentityToken() && ticket.HasAudience() && !ticket.HasAudience(context.ClientId))
                {
                    Logger.LogError("The introspection request was rejected because the " +
                                    "identity token was issued to a different client.");

                    return(await SendIntrospectionResponseAsync(new OpenIdConnectResponse
                    {
                        [OpenIdConnectConstants.Parameters.Active] = false
                    }));
                }

                // Reject the introspection request if the caller doesn't
                // correspond to the client application the token was issued to.
                else if (ticket.IsRefreshToken() && ticket.HasPresenter() && !ticket.HasPresenter(context.ClientId))
                {
                    Logger.LogError("The introspection request was rejected because the " +
                                    "refresh token was issued to a different client.");

                    return(await SendIntrospectionResponseAsync(new OpenIdConnectResponse
                    {
                        [OpenIdConnectConstants.Parameters.Active] = false
                    }));
                }
            }

            var notification = new HandleIntrospectionRequestContext(Context, Scheme, Options, request, ticket)
            {
                Active     = true,
                Issuer     = Context.GetIssuer(Options),
                TokenId    = ticket.GetTokenId(),
                TokenUsage = ticket.GetProperty(OpenIdConnectConstants.Properties.TokenUsage),
                Subject    = ticket.Principal.GetClaim(OpenIdConnectConstants.Claims.Subject)
            };

            // Note: only set "token_type" when the received token is an access token.
            // See https://tools.ietf.org/html/rfc7662#section-2.2
            // and https://tools.ietf.org/html/rfc6749#section-5.1
            if (ticket.IsAccessToken())
            {
                notification.TokenType = OpenIdConnectConstants.TokenTypes.Bearer;
            }

            notification.IssuedAt  = ticket.Properties.IssuedUtc;
            notification.NotBefore = ticket.Properties.IssuedUtc;
            notification.ExpiresAt = ticket.Properties.ExpiresUtc;

            // Infer the audiences/client_id claims from the properties stored in the authentication ticket.
            // Note: the client_id claim must be a unique string so multiple presenters cannot be returned.
            // To work around this limitation, only the first one is returned if multiple values are listed.
            notification.Audiences.UnionWith(ticket.GetAudiences());
            notification.ClientId = ticket.GetPresenters().FirstOrDefault();

            // Note: non-metadata claims are only added if the caller's client_id is known
            // AND is in the specified audiences, unless there's no explicit audience.
            if (!ticket.HasAudience() || (!string.IsNullOrEmpty(context.ClientId) && ticket.HasAudience(context.ClientId)))
            {
                notification.Username = ticket.Principal.Identity?.Name;
                notification.Scopes.UnionWith(ticket.GetScopes());

                // Potentially sensitive claims are only exposed if the client was authenticated
                // and if the authentication ticket corresponds to an identity or access token.
                if (context.IsValidated && (ticket.IsAccessToken() || ticket.IsIdentityToken()))
                {
                    foreach (var grouping in ticket.Principal.Claims.GroupBy(claim => claim.Type))
                    {
                        // Exclude standard claims, that are already handled via strongly-typed properties.
                        // Make sure to always update this list when adding new built-in claim properties.
                        var type = grouping.Key;
                        switch (type)
                        {
                        case OpenIdConnectConstants.Claims.Audience:
                        case OpenIdConnectConstants.Claims.ExpiresAt:
                        case OpenIdConnectConstants.Claims.IssuedAt:
                        case OpenIdConnectConstants.Claims.Issuer:
                        case OpenIdConnectConstants.Claims.NotBefore:
                        case OpenIdConnectConstants.Claims.Scope:
                        case OpenIdConnectConstants.Claims.Subject:
                        case OpenIdConnectConstants.Claims.TokenType:
                        case OpenIdConnectConstants.Claims.TokenUsage:
                            continue;
                        }

                        var claims = grouping.ToArray();
                        switch (claims.Length)
                        {
                        case 0: continue;

                        // When there's only one claim with the same type, directly
                        // convert the claim as an OpenIdConnectParameter instance,
                        // whose token type is determined from the claim value type.
                        case 1:
                        {
                            notification.Claims[type] = claims[0].AsParameter();

                            continue;
                        }

                        // When multiple claims share the same type, convert all the claims
                        // to OpenIdConnectParameter instances, retrieve the underlying
                        // JSON values and add everything to a new JSON array.
                        default:
                        {
                            notification.Claims[type] = new JArray(claims.Select(claim => claim.AsParameter().Value));

                            continue;
                        }
                        }
                    }
                }
            }

            await Provider.HandleIntrospectionRequest(notification);

            if (notification.Result != null)
            {
                if (notification.Result.Handled)
                {
                    Logger.LogDebug("The introspection request was handled in user code.");

                    return(true);
                }

                else if (notification.Result.Skipped)
                {
                    Logger.LogDebug("The default introspection request handling was skipped from user code.");

                    return(false);
                }
            }

            else if (notification.IsRejected)
            {
                Logger.LogError("The introspection request was rejected with the following error: {Error} ; {Description}",
                                /* Error: */ notification.Error ?? OpenIdConnectConstants.Errors.InvalidRequest,
                                /* Description: */ notification.ErrorDescription);

                return(await SendIntrospectionResponseAsync(new OpenIdConnectResponse
                {
                    Error = notification.Error ?? OpenIdConnectConstants.Errors.InvalidRequest,
                    ErrorDescription = notification.ErrorDescription,
                    ErrorUri = notification.ErrorUri
                }));
            }

            var response = new OpenIdConnectResponse
            {
                [OpenIdConnectConstants.Claims.Active] = notification.Active
            };

            // Only add the other properties if
            // the token is considered as active.
            if (notification.Active)
            {
                response[OpenIdConnectConstants.Claims.Issuer]     = notification.Issuer;
                response[OpenIdConnectConstants.Claims.Username]   = notification.Username;
                response[OpenIdConnectConstants.Claims.Subject]    = notification.Subject;
                response[OpenIdConnectConstants.Claims.Scope]      = string.Join(" ", notification.Scopes);
                response[OpenIdConnectConstants.Claims.JwtId]      = notification.TokenId;
                response[OpenIdConnectConstants.Claims.TokenType]  = notification.TokenType;
                response[OpenIdConnectConstants.Claims.TokenUsage] = notification.TokenUsage;
                response[OpenIdConnectConstants.Claims.ClientId]   = notification.ClientId;

                if (notification.IssuedAt != null)
                {
                    response[OpenIdConnectConstants.Claims.IssuedAt] =
                        EpochTime.GetIntDate(notification.IssuedAt.Value.UtcDateTime);
                }

                if (notification.NotBefore != null)
                {
                    response[OpenIdConnectConstants.Claims.NotBefore] =
                        EpochTime.GetIntDate(notification.NotBefore.Value.UtcDateTime);
                }

                if (notification.ExpiresAt != null)
                {
                    response[OpenIdConnectConstants.Claims.ExpiresAt] =
                        EpochTime.GetIntDate(notification.ExpiresAt.Value.UtcDateTime);
                }

                switch (notification.Audiences.Count)
                {
                case 0: break;

                case 1:
                    response[OpenIdConnectConstants.Claims.Audience] = notification.Audiences.ElementAt(0);
                    break;

                default:
                    response[OpenIdConnectConstants.Claims.Audience] = new JArray(notification.Audiences);
                    break;
                }

                foreach (var claim in notification.Claims)
                {
                    response.SetParameter(claim.Key, claim.Value);
                }
            }

            return(await SendIntrospectionResponseAsync(response));
        }
        private async Task HandleHttpRequest(HttpContext context)
        {
            PathString path = context.Request.Path;

            if (path.ToString().Contains("lfs"))
            {
                await this.next(context);

                return;
            }

            var matchCriteria = new Regex("/api/([0-9]+)/([a-zA-Z0-9-\\.]*/?.*)");

            Match match = matchCriteria.Match(context.Request.Path.ToString());

            if (match.Success == false)
            {
                await this.next(context);

                return;
            }

            var dbContext = context.RequestServices.GetService <ApplicationDbContext>();

            if (int.TryParse(match.Groups[1].Value, out int hostIndex) == false)
            {
                await this.next(context);

                return;
            }

            GitHost gitHost = await dbContext.GitHost.SingleOrDefaultAsync(x => x.Id == hostIndex);

            if (gitHost == null)
            {
                await this.next(context);

                return;
            }

            var    requestMessage = new HttpRequestMessage();
            string requestMethod  = context.Request.Method;

            if (!HttpMethods.IsGet(requestMethod) && !HttpMethods.IsHead(requestMethod) &&
                !HttpMethods.IsDelete(requestMethod) && !HttpMethods.IsTrace(requestMethod))
            {
                var streamContent = new StreamContent(context.Request.Body);
                requestMessage.Content = streamContent;
            }

            // Copy the request headers
            foreach (KeyValuePair <string, StringValues> header in context.Request.Headers)
            {
                if (!requestMessage.Headers.TryAddWithoutValidation(header.Key, header.Value.ToArray()) &&
                    requestMessage.Content != null)
                {
                    requestMessage.Content?.Headers.TryAddWithoutValidation(header.Key, header.Value.ToArray());
                }
            }

            string newPath = match.Groups[2].Value;

            var uri = new Uri($"{gitHost.Href}/{context.Request.PathBase}{newPath}{context.Request.QueryString}");

            requestMessage.Headers.Host = uri.Host;
            requestMessage.RequestUri   = uri;
            requestMessage.Method       = new HttpMethod(context.Request.Method);
            using (HttpResponseMessage responseMessage = await this.httpClient.SendAsync(
                       requestMessage,
                       HttpCompletionOption.ResponseHeadersRead,
                       context.RequestAborted))
            {
                context.Response.StatusCode = (int)responseMessage.StatusCode;
                foreach (KeyValuePair <string, IEnumerable <string> > header in responseMessage.Headers)
                {
                    context.Response.Headers[header.Key] = header.Value.ToArray();
                }

                foreach (KeyValuePair <string, IEnumerable <string> > header in responseMessage.Content.Headers)
                {
                    context.Response.Headers[header.Key] = header.Value.ToArray();
                }

                // SendAsync removes chunking from the response. This removes the header so it doesn't expect a chunked response.
                context.Response.Headers.Remove("transfer-encoding");
                await responseMessage.Content.CopyToAsync(context.Response.Body);
            }
        }
Пример #15
0
        public async Task <IActionResult> CashOut(CashOutModel model)
        {
            var member = await HttpContext.GetMemberAsync();

            var issuers = await _paymentProcessor.GetIssuersAsync();

            model.MobileIssuerOptions.AddRange(SelectListHelper.GetSelectList(elements: issuers.Where(x => x.Mode == PaymentMode.Mobile), x => new SelectListItem <PaymentIssuer>(x.Name, x.Code)));
            model.MobileIssuer = member.MobileIssuer;
            model.MobileNumber = member.MobileNumber;

            model.BankIssuerOptions.AddRange(SelectListHelper.GetSelectList(elements: issuers.Where(x => x.Mode == PaymentMode.Bank), x => new SelectListItem <PaymentIssuer>(x.Name, x.Code)));
            model.BankIssuer = member.BankIssuer;
            model.BankNumber = member.BankNumber;

            model.Fee = Math.Round(((_appSettings.PaymentRate * 100) * model.Amount) / 100, 2, MidpointRounding.AwayFromZero);

            if (HttpMethods.IsGet(Request.Method))
            {
                ModelState.Clear();
            }
            else if (HttpMethods.IsPost(Request.Method))
            {
                if (ModelState.IsValid)
                {
                    var canWithdraw = _userService.CanWithdraw(member, model.Amount + model.Fee);

                    if (canWithdraw.Success)
                    {
                        if (!Request.IsCallBack())
                        {
                            ViewData["Persistent"] = true;
                        }
                        else
                        {
                            var transaction = new Transaction
                            {
                                Title          = $"Payment for Withdrawal.",
                                Description    = $"Payment for Withdrawal.",
                                Logo           = Url.ContentLink(Url.Content("~/img/logo.png")),
                                MemberId       = member.Id,
                                Reference      = model.Reference,
                                AccountName    = member.FullName,
                                AccountEmail   = member.Email,
                                AccountNumber  = model.AccountNumber,
                                AccountBalance = member.Balance,

                                TransactionCode   = await _paymentProcessor.GenerateTransactionCodeAsync(),
                                AuthorizationCode = await _paymentProcessor.GetAuthorizationCodeAsync(),

                                Issuer    = model.Issuer,
                                Mode      = model.Mode,
                                Processor = TransactionProcessor.External,

                                Amount = model.Amount,
                                Fee    = model.Fee,

                                IpAddress = await HttpContext.GetIpAddressAsync(),
                                UserAgent = Request.Headers["User-Agent"],
                                Type      = TransactionType.Withdrawal,
                            };

                            await _paymentProcessor.ProcessAsync(transaction);

                            if (transaction.Status == TransactionStatus.Pending)
                            {
                                await _paymentProcessor.VerifyAsync(transaction);
                            }

                            if (transaction.Status == TransactionStatus.Succeeded)
                            {
                                await _transactionService.CreateAsync(transaction);

                                await _userService.WithdrawAsync(member, transaction.Amount + transaction.Fee);

                                TempData.AddAlert(AlertMode.Notify, AlertType.Success, "Payment successful!");
                            }
                            else
                            {
                                ModelState.AddModelError(string.Empty, "Unable to process payment.");
                            }
                        }
                    }
                    else
                    {
                        ModelState.AddModelError(string.Empty, canWithdraw.Message);
                    }
                }
            }

            return(PartialView(model));
        }
Пример #16
0
        private StreamCopyHttpContent SetupRequestBodyCopy(HttpRequest request, bool isStreamingRequest, CancellationToken cancellation)
        {
            // If we generate an HttpContent without a Content-Length then for HTTP/1.1 HttpClient will add a Transfer-Encoding: chunked header
            // even if it's a GET request. Some servers reject requests containing a Transfer-Encoding header if they're not expecting a body.
            // Try to be as specific as possible about the client's intent to send a body. The one thing we don't want to do is to start
            // reading the body early because that has side-effects like 100-continue.
            var hasBody       = true;
            var contentLength = request.Headers.ContentLength;
            var method        = request.Method;

#if NET
            var canHaveBodyFeature = request.HttpContext.Features.Get <IHttpRequestBodyDetectionFeature>();
            if (canHaveBodyFeature != null)
            {
                // 5.0 servers provide a definitive answer for us.
                hasBody = canHaveBodyFeature.CanHaveBody;
            }
            else
#endif
            // https://tools.ietf.org/html/rfc7230#section-3.3.3
            // All HTTP/1.1 requests should have Transfer-Encoding or Content-Length.
            // Http.Sys/IIS will even add a Transfer-Encoding header to HTTP/2 requests with bodies for back-compat.
            // HTTP/1.0 Connection: close bodies are only allowed on responses, not requests.
            // https://tools.ietf.org/html/rfc1945#section-7.2.2
            //
            // Transfer-Encoding overrides Content-Length per spec
            if (request.Headers.TryGetValue(HeaderNames.TransferEncoding, out var transferEncoding) &&
                transferEncoding.Count == 1 &&
                string.Equals("chunked", transferEncoding.ToString(), StringComparison.OrdinalIgnoreCase))
            {
                hasBody = true;
            }
            else if (contentLength.HasValue)
            {
                hasBody = contentLength > 0;
            }
            // Kestrel HTTP/2: There are no required headers that indicate if there is a request body so we need to sniff other fields.
            else if (!ProtocolHelper.IsHttp2OrGreater(request.Protocol))
            {
                hasBody = false;
            }
            // https://tools.ietf.org/html/rfc7231#section-4.3.1
            // A payload within a GET/HEAD/DELETE/CONNECT request message has no defined semantics; sending a payload body on a
            // GET/HEAD/DELETE/CONNECT request might cause some existing implementations to reject the request.
            // https://tools.ietf.org/html/rfc7231#section-4.3.8
            // A client MUST NOT send a message body in a TRACE request.
            else if (HttpMethods.IsGet(method) ||
                     HttpMethods.IsHead(method) ||
                     HttpMethods.IsDelete(method) ||
                     HttpMethods.IsConnect(method) ||
                     HttpMethods.IsTrace(method))
            {
                hasBody = false;
            }
            // else hasBody defaults to true

            StreamCopyHttpContent requestContent = null;
            if (hasBody)
            {
                if (isStreamingRequest)
                {
                    DisableMinRequestBodyDataRateAndMaxRequestBodySize(request.HttpContext);
                }

                // Note on `autoFlushHttpClientOutgoingStream: isStreamingRequest`:
                // The.NET Core HttpClient stack keeps its own buffers on top of the underlying outgoing connection socket.
                // We flush those buffers down to the socket on every write when this is set,
                // but it does NOT result in calls to flush on the underlying socket.
                // This is necessary because we proxy http2 transparently,
                // and we are deliberately unaware of packet structure used e.g. in gRPC duplex channels.
                // Because the sockets aren't flushed, the perf impact of this choice is expected to be small.
                // Future: It may be wise to set this to true for *all* http2 incoming requests,
                // but for now, out of an abundance of caution, we only do it for requests that look like gRPC.
                requestContent = new StreamCopyHttpContent(
                    source: request.Body,
                    autoFlushHttpClientOutgoingStream: isStreamingRequest,
                    clock: _clock,
                    cancellation: cancellation);
            }

            return(requestContent);
        }
        private async Task <bool> InvokeAuthorizationEndpointAsync()
        {
            OpenIdConnectRequest request;

            if (HttpMethods.IsGet(Request.Method))
            {
                request = new OpenIdConnectRequest(Request.Query);
            }

            else if (HttpMethods.IsPost(Request.Method))
            {
                // See http://openid.net/specs/openid-connect-core-1_0.html#FormSerialization
                if (string.IsNullOrEmpty(Request.ContentType))
                {
                    Logger.LogError("The authorization request was rejected because " +
                                    "the mandatory 'Content-Type' header was missing.");

                    return(await SendAuthorizationResponseAsync(new OpenIdConnectResponse
                    {
                        Error = OpenIdConnectConstants.Errors.InvalidRequest,
                        ErrorDescription = "The mandatory 'Content-Type' header must be specified."
                    }));
                }

                // May have media/type; charset=utf-8, allow partial match.
                if (!Request.ContentType.StartsWith("application/x-www-form-urlencoded", StringComparison.OrdinalIgnoreCase))
                {
                    Logger.LogError("The authorization request was rejected because an invalid 'Content-Type' " +
                                    "header was specified: {ContentType}.", Request.ContentType);

                    return(await SendAuthorizationResponseAsync(new OpenIdConnectResponse
                    {
                        Error = OpenIdConnectConstants.Errors.InvalidRequest,
                        ErrorDescription = "The specified 'Content-Type' header is not valid."
                    }));
                }

                request = new OpenIdConnectRequest(await Request.ReadFormAsync(Context.RequestAborted));
            }

            else
            {
                Logger.LogError("The authorization request was rejected because an invalid " +
                                "HTTP method was specified: {Method}.", Request.Method);

                return(await SendAuthorizationResponseAsync(new OpenIdConnectResponse
                {
                    Error = OpenIdConnectConstants.Errors.InvalidRequest,
                    ErrorDescription = "The specified HTTP method is not valid."
                }));
            }

            // Note: set the message type before invoking the ExtractAuthorizationRequest event.
            request.SetProperty(OpenIdConnectConstants.Properties.MessageType,
                                OpenIdConnectConstants.MessageTypes.AuthorizationRequest);

            // Store the authorization request in the ASP.NET context.
            Context.SetOpenIdConnectRequest(request);

            var @event = new ExtractAuthorizationRequestContext(Context, Scheme, Options, request);
            await Provider.ExtractAuthorizationRequest(@event);

            if (@event.Result != null)
            {
                if (@event.Result.Handled)
                {
                    Logger.LogDebug("The authorization request was handled in user code.");

                    return(true);
                }

                else if (@event.Result.Skipped)
                {
                    Logger.LogDebug("The default authorization request handling was skipped from user code.");

                    return(false);
                }
            }

            else if (@event.IsRejected)
            {
                Logger.LogError("The authorization request was rejected with the following error: {Error} ; {Description}",
                                /* Error: */ @event.Error ?? OpenIdConnectConstants.Errors.InvalidRequest,
                                /* Description: */ @event.ErrorDescription);

                return(await SendAuthorizationResponseAsync(new OpenIdConnectResponse
                {
                    Error = @event.Error ?? OpenIdConnectConstants.Errors.InvalidRequest,
                    ErrorDescription = @event.ErrorDescription,
                    ErrorUri = @event.ErrorUri
                }));
            }

            // Store the original redirect_uri sent by the client application for later comparison.
            request.SetProperty(OpenIdConnectConstants.Properties.OriginalRedirectUri, request.RedirectUri);

            Logger.LogInformation("The authorization request was successfully extracted " +
                                  "from the HTTP request: {Request}.", request);

            // client_id is mandatory parameter and MUST cause an error when missing.
            // See http://openid.net/specs/openid-connect-core-1_0.html#AuthRequest
            if (string.IsNullOrEmpty(request.ClientId))
            {
                Logger.LogError("The authorization request was rejected because " +
                                "the mandatory 'client_id' parameter was missing.");

                return(await SendAuthorizationResponseAsync(new OpenIdConnectResponse
                {
                    Error = OpenIdConnectConstants.Errors.InvalidRequest,
                    ErrorDescription = "The mandatory 'client_id' parameter is missing."
                }));
            }

            // While redirect_uri was not mandatory in OAuth2, this parameter
            // is now declared as REQUIRED and MUST cause an error when missing.
            // See http://openid.net/specs/openid-connect-core-1_0.html#AuthRequest
            // To keep AspNet.Security.OpenIdConnect.Server compatible with pure OAuth2 clients,
            // an error is only returned if the request was made by an OpenID Connect client.
            if (string.IsNullOrEmpty(request.RedirectUri) && request.HasScope(OpenIdConnectConstants.Scopes.OpenId))
            {
                Logger.LogError("The authorization request was rejected because " +
                                "the mandatory 'redirect_uri' parameter was missing.");

                return(await SendAuthorizationResponseAsync(new OpenIdConnectResponse
                {
                    Error = OpenIdConnectConstants.Errors.InvalidRequest,
                    ErrorDescription = "The mandatory 'redirect_uri' parameter is missing."
                }));
            }

            if (!string.IsNullOrEmpty(request.RedirectUri))
            {
                // Note: when specified, redirect_uri MUST be an absolute URI.
                // See http://tools.ietf.org/html/rfc6749#section-3.1.2
                // and http://openid.net/specs/openid-connect-core-1_0.html#AuthRequest
                //
                // Note: on Linux/macOS, "/path" URLs are treated as valid absolute file URLs.
                // To ensure relative redirect_uris are correctly rejected on these platforms,
                // an additional check using IsWellFormedOriginalString() is made here.
                // See https://github.com/dotnet/corefx/issues/22098 for more information.
                if (!Uri.TryCreate(request.RedirectUri, UriKind.Absolute, out Uri uri) || !uri.IsWellFormedOriginalString())
                {
                    Logger.LogError("The authorization request was rejected because the 'redirect_uri' parameter " +
                                    "didn't correspond to a valid absolute URL: {RedirectUri}.", request.RedirectUri);

                    return(await SendAuthorizationResponseAsync(new OpenIdConnectResponse
                    {
                        Error = OpenIdConnectConstants.Errors.InvalidRequest,
                        ErrorDescription = "The 'redirect_uri' parameter must be a valid absolute URL."
                    }));
                }

                // Note: when specified, redirect_uri MUST NOT include a fragment component.
                // See http://tools.ietf.org/html/rfc6749#section-3.1.2
                // and http://openid.net/specs/openid-connect-core-1_0.html#AuthRequest
                if (!string.IsNullOrEmpty(uri.Fragment))
                {
                    Logger.LogError("The authorization request was rejected because the 'redirect_uri' " +
                                    "contained a URL fragment: {RedirectUri}.", request.RedirectUri);

                    return(await SendAuthorizationResponseAsync(new OpenIdConnectResponse
                    {
                        Error = OpenIdConnectConstants.Errors.InvalidRequest,
                        ErrorDescription = "The 'redirect_uri' parameter must not include a fragment."
                    }));
                }
            }

            // Reject requests missing the mandatory response_type parameter.
            if (string.IsNullOrEmpty(request.ResponseType))
            {
                Logger.LogError("The authorization request was rejected because " +
                                "the mandatory 'response_type' parameter was missing.");

                return(await SendAuthorizationResponseAsync(new OpenIdConnectResponse
                {
                    Error = OpenIdConnectConstants.Errors.InvalidRequest,
                    ErrorDescription = "The mandatory 'response_type' parameter is missing."
                }));
            }

            // response_mode=query (explicit or not) and a response_type containing id_token
            // or token are not considered as a safe combination and MUST be rejected.
            // See http://openid.net/specs/oauth-v2-multiple-response-types-1_0.html#Security
            if (request.IsQueryResponseMode() && (request.HasResponseType(OpenIdConnectConstants.ResponseTypes.IdToken) ||
                                                  request.HasResponseType(OpenIdConnectConstants.ResponseTypes.Token)))
            {
                Logger.LogError("The authorization request was rejected because the 'response_type'/'response_mode' combination " +
                                "was invalid: {ResponseType} ; {ResponseMode}.", request.ResponseType, request.ResponseMode);

                return(await SendAuthorizationResponseAsync(new OpenIdConnectResponse
                {
                    Error = OpenIdConnectConstants.Errors.InvalidRequest,
                    ErrorDescription = "The specified 'response_type'/'response_mode' combination is invalid."
                }));
            }

            // Reject OpenID Connect implicit/hybrid requests missing the mandatory nonce parameter.
            // See http://openid.net/specs/openid-connect-core-1_0.html#AuthRequest,
            // http://openid.net/specs/openid-connect-implicit-1_0.html#RequestParameters
            // and http://openid.net/specs/openid-connect-core-1_0.html#HybridIDToken.
            if (string.IsNullOrEmpty(request.Nonce) && request.HasScope(OpenIdConnectConstants.Scopes.OpenId) &&
                (request.IsImplicitFlow() || request.IsHybridFlow()))
            {
                Logger.LogError("The authorization request was rejected because the mandatory 'nonce' parameter was missing.");

                return(await SendAuthorizationResponseAsync(new OpenIdConnectResponse
                {
                    Error = OpenIdConnectConstants.Errors.InvalidRequest,
                    ErrorDescription = "The mandatory 'nonce' parameter is missing."
                }));
            }

            // Reject requests containing the id_token response_type if no openid scope has been received.
            if (request.HasResponseType(OpenIdConnectConstants.ResponseTypes.IdToken) &&
                !request.HasScope(OpenIdConnectConstants.Scopes.OpenId))
            {
                Logger.LogError("The authorization request was rejected because the 'openid' scope was missing.");

                return(await SendAuthorizationResponseAsync(new OpenIdConnectResponse
                {
                    Error = OpenIdConnectConstants.Errors.InvalidRequest,
                    ErrorDescription = "The mandatory 'openid' scope is missing."
                }));
            }

            // Reject requests containing the id_token response_type if no asymmetric signing key has been registered.
            if (request.HasResponseType(OpenIdConnectConstants.ResponseTypes.IdToken) &&
                !Options.SigningCredentials.Any(credentials => credentials.Key is AsymmetricSecurityKey))
            {
                Logger.LogError("The authorization request was rejected because the 'id_token' response type could not be honored. " +
                                "To fix this error, consider registering a X.509 signing certificate or an ephemeral signing key.");

                return(await SendAuthorizationResponseAsync(new OpenIdConnectResponse
                {
                    Error = OpenIdConnectConstants.Errors.UnsupportedResponseType,
                    ErrorDescription = "The specified 'response_type' is not supported by this server."
                }));
            }

            // Reject requests containing the code response_type if the token endpoint has been disabled.
            if (request.HasResponseType(OpenIdConnectConstants.ResponseTypes.Code) && !Options.TokenEndpointPath.HasValue)
            {
                Logger.LogError("The authorization request was rejected because the authorization code flow was disabled.");

                return(await SendAuthorizationResponseAsync(new OpenIdConnectResponse
                {
                    Error = OpenIdConnectConstants.Errors.UnsupportedResponseType,
                    ErrorDescription = "The specified 'response_type' is not supported by this server."
                }));
            }

            // Reject requests specifying prompt=none with consent/login or select_account.
            if (request.HasPrompt(OpenIdConnectConstants.Prompts.None) && (request.HasPrompt(OpenIdConnectConstants.Prompts.Consent) ||
                                                                           request.HasPrompt(OpenIdConnectConstants.Prompts.Login) ||
                                                                           request.HasPrompt(OpenIdConnectConstants.Prompts.SelectAccount)))
            {
                Logger.LogError("The authorization request was rejected because an invalid prompt parameter was specified.");

                return(await SendAuthorizationResponseAsync(new OpenIdConnectResponse
                {
                    Error = OpenIdConnectConstants.Errors.InvalidRequest,
                    ErrorDescription = "The specified 'prompt' parameter is invalid."
                }));
            }

            if (!string.IsNullOrEmpty(request.CodeChallenge) || !string.IsNullOrEmpty(request.CodeChallengeMethod))
            {
                // When code_challenge or code_challenge_method is specified, ensure the response_type includes "code".
                if (!request.HasResponseType(OpenIdConnectConstants.ResponseTypes.Code))
                {
                    Logger.LogError("The authorization request was rejected because the response type " +
                                    "was not compatible with 'code_challenge'/'code_challenge_method'.");

                    return(await SendAuthorizationResponseAsync(new OpenIdConnectResponse
                    {
                        Error = OpenIdConnectConstants.Errors.InvalidRequest,
                        ErrorDescription = "The 'code_challenge' and 'code_challenge_method' parameters " +
                                           "can only be used with a response type containing 'code'."
                    }));
                }

                if (!string.IsNullOrEmpty(request.CodeChallengeMethod))
                {
                    // Ensure a code_challenge was specified if a code_challenge_method was used.
                    if (string.IsNullOrEmpty(request.CodeChallenge))
                    {
                        Logger.LogError("The authorization request was rejected because the code_challenge was missing.");

                        return(await SendAuthorizationResponseAsync(new OpenIdConnectResponse
                        {
                            Error = OpenIdConnectConstants.Errors.InvalidRequest,
                            ErrorDescription = "The 'code_challenge_method' parameter " +
                                               "cannot be used without 'code_challenge'."
                        }));
                    }

                    // If a code_challenge_method was specified, ensure the algorithm is supported.
                    if (request.CodeChallengeMethod != OpenIdConnectConstants.CodeChallengeMethods.Plain &&
                        request.CodeChallengeMethod != OpenIdConnectConstants.CodeChallengeMethods.Sha256)
                    {
                        Logger.LogError("The authorization request was rejected because " +
                                        "the specified code challenge was not supported.");

                        return(await SendAuthorizationResponseAsync(new OpenIdConnectResponse
                        {
                            Error = OpenIdConnectConstants.Errors.InvalidRequest,
                            ErrorDescription = "The specified code_challenge_method is not supported."
                        }));
                    }
                }
            }

            var context = new ValidateAuthorizationRequestContext(Context, Scheme, Options, request);
            await Provider.ValidateAuthorizationRequest(context);

            if (context.Result != null)
            {
                if (context.Result.Handled)
                {
                    Logger.LogDebug("The authorization request was handled in user code.");

                    return(true);
                }

                else if (context.Result.Skipped)
                {
                    Logger.LogDebug("The default authorization request handling was skipped from user code.");

                    return(false);
                }
            }

            else if (context.IsRejected)
            {
                Logger.LogError("The authorization request was rejected with the following error: {Error} ; {Description}",
                                /* Error: */ context.Error ?? OpenIdConnectConstants.Errors.InvalidRequest,
                                /* Description: */ context.ErrorDescription);

                return(await SendAuthorizationResponseAsync(new OpenIdConnectResponse
                {
                    Error = context.Error ?? OpenIdConnectConstants.Errors.InvalidRequest,
                    ErrorDescription = context.ErrorDescription,
                    ErrorUri = context.ErrorUri
                }));
            }

            // Store the validated client_id/redirect_uri as request properties.
            request.SetProperty(OpenIdConnectConstants.Properties.ValidatedClientId, context.ClientId)
            .SetProperty(OpenIdConnectConstants.Properties.ValidatedRedirectUri, context.RedirectUri);

            Logger.LogInformation("The authorization request was successfully validated.");

            var notification = new HandleAuthorizationRequestContext(Context, Scheme, Options, request);
            await Provider.HandleAuthorizationRequest(notification);

            if (notification.Result != null)
            {
                if (notification.Result.Handled)
                {
                    Logger.LogDebug("The authorization request was handled in user code.");

                    return(true);
                }

                else if (notification.Result.Skipped)
                {
                    Logger.LogDebug("The default authorization request handling was skipped from user code.");

                    return(false);
                }
            }

            else if (notification.IsRejected)
            {
                Logger.LogError("The authorization request was rejected with the following error: {Error} ; {Description}",
                                /* Error: */ notification.Error ?? OpenIdConnectConstants.Errors.InvalidRequest,
                                /* Description: */ notification.ErrorDescription);

                return(await SendAuthorizationResponseAsync(new OpenIdConnectResponse
                {
                    Error = notification.Error ?? OpenIdConnectConstants.Errors.InvalidRequest,
                    ErrorDescription = notification.ErrorDescription,
                    ErrorUri = notification.ErrorUri
                }));
            }

            // If an authentication ticket was provided, stop processing
            // the request and return an authorization response.
            var ticket = notification.Ticket;

            if (ticket == null)
            {
                return(false);
            }

            return(await SignInAsync(ticket));
        }
Пример #18
0
        protected virtual (RangeItemHeaderValue range, long rangeLength, bool serveBody) SetHeadersAndLog(
            ActionContext context,
            FileResult result,
            long?fileLength,
            DateTimeOffset?lastModified = null,
            EntityTagHeaderValue etag   = null,
            bool enableRangeProcessing  = true)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }
            if (result == null)
            {
                throw new ArgumentNullException(nameof(result));
            }

            SetContentType(context, result);
            SetContentDispositionHeader(context, result);
            Logger.FileResultExecuting(result.FileDownloadName);

            var request             = context.HttpContext.Request;
            var httpRequestHeaders  = request.GetTypedHeaders();
            var response            = context.HttpContext.Response;
            var httpResponseHeaders = response.GetTypedHeaders();

            if (lastModified.HasValue)
            {
                httpResponseHeaders.LastModified = lastModified;
            }
            if (etag != null)
            {
                httpResponseHeaders.ETag = etag;
            }

            var serveBody         = !HttpMethods.IsHead(request.Method);
            var preconditionState = GetPreconditionState(context, httpRequestHeaders, lastModified, etag);

            if (preconditionState == PreconditionState.NotModified)
            {
                serveBody           = false;
                response.StatusCode = StatusCodes.Status304NotModified;
            }
            else if (preconditionState == PreconditionState.PreconditionFailed)
            {
                serveBody           = false;
                response.StatusCode = StatusCodes.Status412PreconditionFailed;
            }

            if (fileLength.HasValue)
            {
                // Assuming the request is not a range request, the Content-Length header is set to the length of the entire file.
                // If the request is a valid range request, this header is overwritten with the length of the range as part of the
                // range processing (see method SetContentLength).
                if (serveBody)
                {
                    response.ContentLength = fileLength.Value;
                }

                if (enableRangeProcessing)
                {
                    SetAcceptRangeHeader(context);
                    if (HttpMethods.IsHead(request.Method) || HttpMethods.IsGet(request.Method))
                    {
                        if ((preconditionState == PreconditionState.Unspecified ||
                             preconditionState == PreconditionState.ShouldProcess))
                        {
                            if (IfRangeValid(context, httpRequestHeaders, lastModified, etag))
                            {
                                return(SetRangeHeaders(context, httpRequestHeaders, fileLength.Value));
                            }
                        }
                    }
                }
            }

            return(range : null, rangeLength : 0, serveBody : serveBody);
        }
Пример #19
0
 private static bool IsGetOrHeadMethod(string method) =>
 HttpMethods.IsGet(method) || HttpMethods.IsHead(method);
        /// <inheritdoc />
        public void OnResourceExecuting(ResourceExecutingContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            var routeData = context.RouteData;
            var request   = context.HttpContext.Request;

            if (routeData.TryGetWebHookReceiverName(out var receiverName) &&
                (HttpMethods.IsGet(request.Method) || HttpMethods.IsHead(request.Method)))
            {
                var getHeadRequestMetadata = _getHeadRequestMetadata
                                             .FirstOrDefault(metadata => metadata.IsApplicable(receiverName));
                if (getHeadRequestMetadata != null)
                {
                    // First verify that we have the secret key configuration value. This may be redundant if the
                    // receiver also implements IWebHookVerifyCodeMetadata in its metadata. However this verification
                    // is necessary for some receivers because signature verification (for example) is not possible
                    // without a body.
                    var secretKey = GetSecretKey(
                        receiverName,
                        routeData,
                        getHeadRequestMetadata.SecretKeyMinLength,
                        getHeadRequestMetadata.SecretKeyMaxLength);
                    if (secretKey == null)
                    {
                        // Have already logged about this case.
                        context.Result = new NotFoundResult();
                        return;
                    }

                    if (HttpMethods.IsHead(request.Method))
                    {
                        if (getHeadRequestMetadata.AllowHeadRequests)
                        {
                            // Success #1
                            Logger.LogInformation(
                                400,
                                "Received a HEAD request for the '{ReceiverName}' WebHook receiver -- ignoring.",
                                receiverName);
                            context.Result = new OkResult();
                        }

                        // Never respond to a HEAD request with a challenge response.
                        return;
                    }

                    if (getHeadRequestMetadata.ChallengeQueryParameterName == null)
                    {
                        // Success #2: Simple GET case. Have done all necessary verification.
                        Logger.LogInformation(
                            401,
                            "Received a GET request for the '{ReceiverName}' WebHook receiver -- ignoring.",
                            receiverName);
                        context.Result = new OkResult();
                        return;
                    }

                    // Success #3 unless required query parameter is missing.
                    context.Result = GetChallengeResponse(getHeadRequestMetadata, receiverName, request, routeData);
                }
            }
        }
        private async Task <bool> InvokeUserinfoEndpointAsync()
        {
            OpenIdConnectRequest request;

            if (HttpMethods.IsGet(Request.Method))
            {
                request = new OpenIdConnectRequest(Request.Query);
            }

            else if (HttpMethods.IsPost(Request.Method))
            {
                // Note: if no Content-Type header was specified, assume the userinfo request
                // doesn't contain any parameter and create an empty OpenIdConnectRequest.
                if (string.IsNullOrEmpty(Request.ContentType))
                {
                    request = new OpenIdConnectRequest();
                }

                else
                {
                    // May have media/type; charset=utf-8, allow partial match.
                    if (!Request.ContentType.StartsWith("application/x-www-form-urlencoded", StringComparison.OrdinalIgnoreCase))
                    {
                        Logger.LogError("The userinfo request was rejected because an invalid 'Content-Type' " +
                                        "header was specified: {ContentType}.", Request.ContentType);

                        return(await SendUserinfoResponseAsync(new OpenIdConnectResponse
                        {
                            Error = OpenIdConnectConstants.Errors.InvalidRequest,
                            ErrorDescription = "The specified 'Content-Type' header is not valid."
                        }));
                    }

                    request = new OpenIdConnectRequest(await Request.ReadFormAsync());
                }
            }

            else
            {
                Logger.LogError("The userinfo request was rejected because an invalid " +
                                "HTTP method was specified: {Method}.", Request.Method);

                return(await SendUserinfoResponseAsync(new OpenIdConnectResponse
                {
                    Error = OpenIdConnectConstants.Errors.InvalidRequest,
                    ErrorDescription = "The specified HTTP method is not valid."
                }));
            }

            // Note: set the message type before invoking the ExtractUserinfoRequest event.
            request.SetProperty(OpenIdConnectConstants.Properties.MessageType,
                                OpenIdConnectConstants.MessageTypes.UserinfoRequest);

            // Insert the userinfo request in the ASP.NET context.
            Context.SetOpenIdConnectRequest(request);

            var @event = new ExtractUserinfoRequestContext(Context, Scheme, Options, request);
            await Provider.ExtractUserinfoRequest(@event);

            if (@event.Result != null)
            {
                if (@event.Result.Handled)
                {
                    Logger.LogDebug("The userinfo request was handled in user code.");

                    return(true);
                }

                else if (@event.Result.Skipped)
                {
                    Logger.LogDebug("The default userinfo request handling was skipped from user code.");

                    return(false);
                }
            }

            else if (@event.IsRejected)
            {
                Logger.LogError("The userinfo request was rejected with the following error: {Error} ; {Description}",
                                /* Error: */ @event.Error ?? OpenIdConnectConstants.Errors.InvalidRequest,
                                /* Description: */ @event.ErrorDescription);

                return(await SendUserinfoResponseAsync(new OpenIdConnectResponse
                {
                    Error = @event.Error ?? OpenIdConnectConstants.Errors.InvalidRequest,
                    ErrorDescription = @event.ErrorDescription,
                    ErrorUri = @event.ErrorUri
                }));
            }

            Logger.LogInformation("The userinfo request was successfully extracted " +
                                  "from the HTTP request: {Request}.", request);

            string token = null;

            if (!string.IsNullOrEmpty(request.AccessToken))
            {
                token = request.AccessToken;
            }

            else
            {
                string header = Request.Headers[HeaderNames.Authorization];
                if (!string.IsNullOrEmpty(header))
                {
                    if (!header.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
                    {
                        Logger.LogError("The userinfo request was rejected because the " +
                                        "'Authorization' header was invalid: {Header}.", header);

                        return(await SendUserinfoResponseAsync(new OpenIdConnectResponse
                        {
                            Error = OpenIdConnectConstants.Errors.InvalidRequest,
                            ErrorDescription = "The specified 'Authorization' header is invalid."
                        }));
                    }

                    token = header.Substring("Bearer ".Length);
                }
            }

            if (string.IsNullOrEmpty(token))
            {
                Logger.LogError("The userinfo request was rejected because the access token was missing.");

                return(await SendUserinfoResponseAsync(new OpenIdConnectResponse
                {
                    Error = OpenIdConnectConstants.Errors.InvalidRequest,
                    ErrorDescription = "The mandatory 'access_token' parameter is missing."
                }));
            }

            var context = new ValidateUserinfoRequestContext(Context, Scheme, Options, request);
            await Provider.ValidateUserinfoRequest(context);

            if (context.Result != null)
            {
                if (context.Result.Handled)
                {
                    Logger.LogDebug("The userinfo request was handled in user code.");

                    return(true);
                }

                else if (context.Result.Skipped)
                {
                    Logger.LogDebug("The default userinfo request handling was skipped from user code.");

                    return(false);
                }
            }

            else if (context.IsRejected)
            {
                Logger.LogError("The userinfo request was rejected with the following error: {Error} ; {Description}",
                                /* Error: */ context.Error ?? OpenIdConnectConstants.Errors.InvalidRequest,
                                /* Description: */ context.ErrorDescription);

                return(await SendUserinfoResponseAsync(new OpenIdConnectResponse
                {
                    Error = context.Error ?? OpenIdConnectConstants.Errors.InvalidRequest,
                    ErrorDescription = context.ErrorDescription,
                    ErrorUri = context.ErrorUri
                }));
            }

            Logger.LogInformation("The userinfo request was successfully validated.");

            var ticket = await DeserializeAccessTokenAsync(token, request);

            if (ticket == null)
            {
                Logger.LogError("The userinfo request was rejected because the access token was invalid.");

                return(await SendUserinfoResponseAsync(new OpenIdConnectResponse
                {
                    Error = OpenIdConnectConstants.Errors.InvalidToken,
                    ErrorDescription = "The specified access token is not valid."
                }));
            }

            if (ticket.Properties.ExpiresUtc.HasValue &&
                ticket.Properties.ExpiresUtc < Options.SystemClock.UtcNow)
            {
                Logger.LogError("The userinfo request was rejected because the access token was expired.");

                return(await SendUserinfoResponseAsync(new OpenIdConnectResponse
                {
                    Error = OpenIdConnectConstants.Errors.InvalidToken,
                    ErrorDescription = "The specified access token is no longer valid."
                }));
            }

            var notification = new HandleUserinfoRequestContext(Context, Scheme, Options, request, ticket)
            {
                Issuer  = Context.GetIssuer(Options),
                Subject = ticket.Principal.GetClaim(OpenIdConnectConstants.Claims.Subject)
            };

            // Note: when receiving an access token, its audiences list cannot be used for the "aud" claim
            // as the client application is not the intented audience but only an authorized presenter.
            // See http://openid.net/specs/openid-connect-core-1_0.html#UserInfoResponse
            notification.Audiences.UnionWith(ticket.GetPresenters());

            // The following claims are all optional and should be excluded when
            // no corresponding value has been found in the authentication ticket.
            if (ticket.HasScope(OpenIdConnectConstants.Scopes.Profile))
            {
                notification.FamilyName = ticket.Principal.GetClaim(OpenIdConnectConstants.Claims.FamilyName);
                notification.GivenName  = ticket.Principal.GetClaim(OpenIdConnectConstants.Claims.GivenName);
                notification.BirthDate  = ticket.Principal.GetClaim(OpenIdConnectConstants.Claims.Birthdate);
            }

            if (ticket.HasScope(OpenIdConnectConstants.Scopes.Email))
            {
                notification.Email = ticket.Principal.GetClaim(OpenIdConnectConstants.Claims.Email);
            }

            if (ticket.HasScope(OpenIdConnectConstants.Scopes.Phone))
            {
                notification.PhoneNumber = ticket.Principal.GetClaim(OpenIdConnectConstants.Claims.PhoneNumber);
            }

            await Provider.HandleUserinfoRequest(notification);

            if (notification.Result != null)
            {
                if (notification.Result.Handled)
                {
                    Logger.LogDebug("The userinfo request was handled in user code.");

                    return(true);
                }

                else if (notification.Result.Skipped)
                {
                    Logger.LogDebug("The default userinfo request handling was skipped from user code.");

                    return(false);
                }
            }

            else if (notification.IsRejected)
            {
                Logger.LogError("The userinfo request was rejected with the following error: {Error} ; {Description}",
                                /* Error: */ notification.Error ?? OpenIdConnectConstants.Errors.InvalidRequest,
                                /* Description: */ notification.ErrorDescription);

                return(await SendUserinfoResponseAsync(new OpenIdConnectResponse
                {
                    Error = notification.Error ?? OpenIdConnectConstants.Errors.InvalidRequest,
                    ErrorDescription = notification.ErrorDescription,
                    ErrorUri = notification.ErrorUri
                }));
            }

            // Ensure the "sub" claim has been correctly populated.
            if (string.IsNullOrEmpty(notification.Subject))
            {
                throw new InvalidOperationException("The subject claim cannot be null or empty.");
            }

            var response = new OpenIdConnectResponse
            {
                [OpenIdConnectConstants.Claims.Subject]             = notification.Subject,
                [OpenIdConnectConstants.Claims.Address]             = notification.Address,
                [OpenIdConnectConstants.Claims.Birthdate]           = notification.BirthDate,
                [OpenIdConnectConstants.Claims.Email]               = notification.Email,
                [OpenIdConnectConstants.Claims.EmailVerified]       = notification.EmailVerified,
                [OpenIdConnectConstants.Claims.FamilyName]          = notification.FamilyName,
                [OpenIdConnectConstants.Claims.GivenName]           = notification.GivenName,
                [OpenIdConnectConstants.Claims.Issuer]              = notification.Issuer,
                [OpenIdConnectConstants.Claims.PhoneNumber]         = notification.PhoneNumber,
                [OpenIdConnectConstants.Claims.PhoneNumberVerified] = notification.PhoneNumberVerified,
                [OpenIdConnectConstants.Claims.PreferredUsername]   = notification.PreferredUsername,
                [OpenIdConnectConstants.Claims.Profile]             = notification.Profile,
                [OpenIdConnectConstants.Claims.Website]             = notification.Website
            };

            switch (notification.Audiences.Count)
            {
            case 0: break;

            case 1:
                response[OpenIdConnectConstants.Claims.Audience] = notification.Audiences.ElementAt(0);
                break;

            default:
                response[OpenIdConnectConstants.Claims.Audience] = new JArray(notification.Audiences);
                break;
            }

            foreach (var claim in notification.Claims)
            {
                response.SetParameter(claim.Key, claim.Value);
            }

            return(await SendUserinfoResponseAsync(response));
        }
Пример #22
0
            /// <summary>
            /// Called before the action executes, after model binding is complete
            /// </summary>
            /// <param name="context">A context for action filters</param>
            public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
            {
                await next();

                if (context == null || context.HttpContext == null || context.HttpContext.Request == null)
                {
                    return;
                }

                if (!DataSettingsHelper.DatabaseIsInstalled())
                {
                    return;
                }

                //only in GET requests
                if (!HttpMethods.IsGet(context.HttpContext.Request.Method))
                {
                    return;
                }

                //update last activity date
                if (_workContext.CurrentCustomer.LastActivityDateUtc.AddMinutes(1.0) < DateTime.UtcNow)
                {
                    await _customerService.UpdateCustomerField(_workContext.CurrentCustomer, x => x.LastActivityDateUtc, DateTime.UtcNow);
                }

                //get current IP address
                var currentIpAddress = context.HttpContext?.Connection?.RemoteIpAddress?.ToString();

                //update customer's IP address
                if (!string.IsNullOrEmpty(currentIpAddress) && !currentIpAddress.Equals(_workContext.CurrentCustomer.LastIpAddress, StringComparison.OrdinalIgnoreCase))
                {
                    _workContext.CurrentCustomer.LastIpAddress = currentIpAddress;
                    await _customerService.UpdateCustomerField(_workContext.CurrentCustomer, x => x.LastIpAddress, currentIpAddress);
                }

                //whether is need to store last visited page URL
                if (!_customerSettings.StoreLastVisitedPage)
                {
                    return;
                }

                //get current page
                var pageUrl = context.HttpContext?.Request?.GetDisplayUrl();

                if (string.IsNullOrEmpty(pageUrl))
                {
                    return;
                }

                //get previous last page
                var previousPageUrl = _workContext.CurrentCustomer.GetUserFieldFromEntity <string>(SystemCustomerFieldNames.LastVisitedPage);

                //save new one if don't match
                if (!pageUrl.Equals(previousPageUrl, StringComparison.OrdinalIgnoreCase))
                {
                    await _userFieldService.SaveField(_workContext.CurrentCustomer, SystemCustomerFieldNames.LastVisitedPage, pageUrl);
                }

                if (!string.IsNullOrEmpty(context.HttpContext.Request.Headers[HeaderNames.Referer]))
                {
                    if (!context.HttpContext.Request.Headers[HeaderNames.Referer].ToString().Contains(context.HttpContext.Request.Host.ToString()))
                    {
                        var previousUrlReferrer = _workContext.CurrentCustomer.GetUserField <string>(_userFieldService, SystemCustomerFieldNames.LastUrlReferrer);
                        var actualUrlReferrer   = context.HttpContext.Request.Headers[HeaderNames.Referer];
                        if (previousUrlReferrer != actualUrlReferrer)
                        {
                            await _userFieldService.SaveField(_workContext.CurrentCustomer, SystemCustomerFieldNames.LastUrlReferrer, actualUrlReferrer);
                        }
                    }
                }

                if (_customerSettings.SaveVisitedPage)
                {
                    if (!_workContext.CurrentCustomer.IsSearchEngineAccount())
                    {
                        //activity
                        await _customerActivityService.InsertActivity("PublicStore.Url", pageUrl, pageUrl, _workContext.CurrentCustomer);

                        //action
                        await _customerActionEventService.Url(_workContext.CurrentCustomer, context.HttpContext?.Request?.Path.ToString(), context.HttpContext?.Request?.Headers["Referer"]);
                    }
                }
            }
Пример #23
0
 private bool isGetRequest(HttpContext context)
 {
     return(HttpMethods.IsGet(context.Request.Method));
 }
        private async Task <bool> InvokeLogoutEndpointAsync()
        {
            OpenIdConnectRequest request;

            // Note: logout requests must be made via GET but POST requests
            // are also accepted to allow flowing large logout payloads.
            // See https://openid.net/specs/openid-connect-session-1_0.html#RPLogout
            if (HttpMethods.IsGet(Request.Method))
            {
                request = new OpenIdConnectRequest(Request.Query);
            }

            else if (HttpMethods.IsPost(Request.Method))
            {
                // See http://openid.net/specs/openid-connect-core-1_0.html#FormSerialization
                if (string.IsNullOrEmpty(Request.ContentType))
                {
                    Logger.LogError("The logout request was rejected because " +
                                    "the mandatory 'Content-Type' header was missing.");

                    return(await SendLogoutResponseAsync(new OpenIdConnectResponse
                    {
                        Error = OpenIdConnectConstants.Errors.InvalidRequest,
                        ErrorDescription = "The mandatory 'Content-Type' header must be specified."
                    }));
                }

                // May have media/type; charset=utf-8, allow partial match.
                if (!Request.ContentType.StartsWith("application/x-www-form-urlencoded", StringComparison.OrdinalIgnoreCase))
                {
                    Logger.LogError("The logout request was rejected because an invalid 'Content-Type' " +
                                    "header was specified: {ContentType}.", Request.ContentType);

                    return(await SendLogoutResponseAsync(new OpenIdConnectResponse
                    {
                        Error = OpenIdConnectConstants.Errors.InvalidRequest,
                        ErrorDescription = "The specified 'Content-Type' header is not valid."
                    }));
                }

                request = new OpenIdConnectRequest(await Request.ReadFormAsync(Context.RequestAborted));
            }

            else
            {
                Logger.LogError("The logout request was rejected because an invalid " +
                                "HTTP method was specified: {Method}.", Request.Method);

                return(await SendLogoutResponseAsync(new OpenIdConnectResponse
                {
                    Error = OpenIdConnectConstants.Errors.InvalidRequest,
                    ErrorDescription = "The specified HTTP method is not valid."
                }));
            }

            // Note: set the message type before invoking the ExtractLogoutRequest event.
            request.SetProperty(OpenIdConnectConstants.Properties.MessageType,
                                OpenIdConnectConstants.MessageTypes.LogoutRequest);

            // Store the logout request in the ASP.NET context.
            Context.SetOpenIdConnectRequest(request);

            var @event = new ExtractLogoutRequestContext(Context, Scheme, Options, request);
            await Provider.ExtractLogoutRequest(@event);

            if (@event.Result != null)
            {
                if (@event.Result.Handled)
                {
                    Logger.LogDebug("The logout request was handled in user code.");

                    return(true);
                }

                else if (@event.Result.Skipped)
                {
                    Logger.LogDebug("The default logout request handling was skipped from user code.");

                    return(false);
                }
            }

            else if (@event.IsRejected)
            {
                Logger.LogError("The logout request was rejected with the following error: {Error} ; {Description}",
                                /* Error: */ @event.Error ?? OpenIdConnectConstants.Errors.InvalidRequest,
                                /* Description: */ @event.ErrorDescription);

                return(await SendLogoutResponseAsync(new OpenIdConnectResponse
                {
                    Error = @event.Error ?? OpenIdConnectConstants.Errors.InvalidRequest,
                    ErrorDescription = @event.ErrorDescription,
                    ErrorUri = @event.ErrorUri
                }));
            }

            Logger.LogInformation("The logout request was successfully extracted " +
                                  "from the HTTP request: {Request}.", request);

            var context = new ValidateLogoutRequestContext(Context, Scheme, Options, request);
            await Provider.ValidateLogoutRequest(context);

            if (context.Result != null)
            {
                if (context.Result.Handled)
                {
                    Logger.LogDebug("The logout request was handled in user code.");

                    return(true);
                }

                else if (context.Result.Skipped)
                {
                    Logger.LogDebug("The default logout request handling was skipped from user code.");

                    return(false);
                }
            }

            else if (context.IsRejected)
            {
                Logger.LogError("The logout request was rejected with the following error: {Error} ; {Description}",
                                /* Error: */ context.Error ?? OpenIdConnectConstants.Errors.InvalidRequest,
                                /* Description: */ context.ErrorDescription);

                return(await SendLogoutResponseAsync(new OpenIdConnectResponse
                {
                    Error = context.Error ?? OpenIdConnectConstants.Errors.InvalidRequest,
                    ErrorDescription = context.ErrorDescription,
                    ErrorUri = context.ErrorUri
                }));
            }

            // Store the validated post_logout_redirect_uri as a request property.
            request.SetProperty(OpenIdConnectConstants.Properties.PostLogoutRedirectUri, context.PostLogoutRedirectUri);

            Logger.LogInformation("The logout request was successfully validated.");

            var notification = new HandleLogoutRequestContext(Context, Scheme, Options, request);
            await Provider.HandleLogoutRequest(notification);

            if (notification.Result != null)
            {
                if (notification.Result.Handled)
                {
                    Logger.LogDebug("The logout request was handled in user code.");

                    return(true);
                }

                else if (notification.Result.Skipped)
                {
                    Logger.LogDebug("The default logout request handling was skipped from user code.");

                    return(false);
                }
            }

            else if (notification.IsRejected)
            {
                Logger.LogError("The logout request was rejected with the following error: {Error} ; {Description}",
                                /* Error: */ notification.Error ?? OpenIdConnectConstants.Errors.InvalidRequest,
                                /* Description: */ notification.ErrorDescription);

                return(await SendLogoutResponseAsync(new OpenIdConnectResponse
                {
                    Error = notification.Error ?? OpenIdConnectConstants.Errors.InvalidRequest,
                    ErrorDescription = notification.ErrorDescription,
                    ErrorUri = notification.ErrorUri
                }));
            }

            return(false);
        }
        public async Task Invoke(HttpContext context)
        {
            var requestFeatures = context.Features.Get <IHttpRequestFeature>();

            if (!_settings.Enabled || !HttpMethods.IsGet(requestFeatures.Method) || (context.Request.GetTypedHeaders().CacheControl?.NoCache ?? false))
            {
                await _next.Invoke(context);

                return;
            }

            var cacheKey = requestFeatures.RawTarget;

            try
            {
                CachedResponse cachedResponse = null;
                if (context.User.Identity.IsAuthenticated)
                {
                    cachedResponse = _memoryCache.Get <CachedResponse>($"{cacheKey}_u:{context.User.FindFirst(ClaimTypes.NameIdentifier).Value}");
                    if (cachedResponse == null)
                    {
                        cachedResponse = _memoryCache.Get <CachedResponse>(cacheKey);

                        if (cachedResponse?.ForAnonymusUsers ?? false)
                        {
                            cachedResponse = null;
                        }
                    }
                }
                else
                {
                    cachedResponse = _memoryCache.Get <CachedResponse>(cacheKey);
                }

                if (cachedResponse != null)
                {
                    await cachedResponse.Apply(context).ConfigureAwait(false);

                    return;
                }
            }
            catch (Exception ex) {
                _logger.Exception(ex);
            }

            var cachedItem = await CaptureResponse(context).ConfigureAwait(false);

            if (cachedItem != null)
            {
                try
                {
                    if (context.Items.TryGetValue(Consts.IsCachebleKey, out object isCacheble) && (isCacheble is bool) && (bool)isCacheble)
                    {
                        cacheKey = requestFeatures.RawTarget;
                        if (context.Items.TryGetValue(Consts.IsUserSpecificKey, out object isUserSpecific) &&
                            (isUserSpecific is bool) && (bool)isUserSpecific)
                        {
                            if (context.User.Identity.IsAuthenticated)
                            {
                                cacheKey = $"{cacheKey}_u:{context.User.FindFirst(ClaimTypes.NameIdentifier).Value}";
                            }
                            else
                            {
                                cachedItem.ForAnonymusUsers = true;
                            }
                        }

                        var slidingDuration = (context.Items.TryGetValue(Consts.SlidingDurationKey, out object slidingDurationO) && (slidingDurationO is int)) ?
                                              TimeSpan.FromSeconds((int)slidingDurationO) : TimeSpan.Zero;

                        var duration = (context.Items.TryGetValue(Consts.DurationKey, out object durationO) && (durationO is int)) ?
                                       TimeSpan.FromSeconds((int)durationO) :
                                       TimeSpan.FromSeconds(_settings.MaxCacheInSec);

                        var options = new MemoryCacheEntryOptions();
                        if (slidingDuration > TimeSpan.Zero)
                        {
                            options.SlidingExpiration = slidingDuration;
                        }
                        else
                        {
                            options.AbsoluteExpirationRelativeToNow = duration;
                        }

                        Dictionary <string, string> tagsToApply = null;
                        if (context.Items.TryGetValue(Consts.CachedObjectTags, out var tagsO) && tagsO is Dictionary <string, string> tags)
                        {
                            tagsToApply = tags;
                        }

                        _memoryCache.Add(cacheKey, cachedItem, options, tagsToApply);
                    }
                }
                catch (Exception ex)
                {
                    _logger.Exception(ex);
                }
            }
        }
Пример #26
0
        protected virtual (RangeItemHeaderValue range, long rangeLength, bool serveBody) SetHeadersAndLog(
            ActionContext context,
            FileResult result,
            long?fileLength,
            bool enableRangeProcessing,
            DateTimeOffset?lastModified = null,
            EntityTagHeaderValue etag   = null)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }
            if (result == null)
            {
                throw new ArgumentNullException(nameof(result));
            }

            SetContentType(context, result);
            SetContentDispositionHeader(context, result);

            var request            = context.Context.Request;
            var httpRequestHeaders = request.GetTypedHeaders();

            // Since the 'Last-Modified' and other similar http date headers are rounded down to whole seconds,
            // round down current file's last modified to whole seconds for correct comparison.
            if (lastModified.HasValue)
            {
                lastModified = RoundDownToWholeSeconds(lastModified.Value);
            }

            var preconditionState = GetPreconditionState(httpRequestHeaders, lastModified, etag);

            var response = context.Context.Response;

            SetLastModifiedAndEtagHeaders(response, lastModified, etag);

            // Short circuit if the preconditional headers process to 304 (NotModified) or 412 (PreconditionFailed)
            if (preconditionState == PreconditionState.NotModified)
            {
                response.StatusCode = StatusCodes.Status304NotModified;
                return(range : null, rangeLength : 0, serveBody : false);
            }
            else if (preconditionState == PreconditionState.PreconditionFailed)
            {
                response.StatusCode = StatusCodes.Status412PreconditionFailed;
                return(range : null, rangeLength : 0, serveBody : false);
            }

            if (fileLength.HasValue)
            {
                // Assuming the request is not a range request, and the response body is not empty, the Content-Length header is set to
                // the length of the entire file.
                // If the request is a valid range request, this header is overwritten with the length of the range as part of the
                // range processing (see method SetContentLength).

                response.ContentLength = fileLength.Value;

                // Handle range request
                if (enableRangeProcessing)
                {
                    SetAcceptRangeHeader(response);

                    // If the request method is HEAD or GET, PreconditionState is Unspecified or ShouldProcess, and IfRange header is valid,
                    // range should be processed and Range headers should be set
                    if ((HttpMethods.IsHead(request.Method) || HttpMethods.IsGet(request.Method)) &&
                        (preconditionState == PreconditionState.Unspecified || preconditionState == PreconditionState.ShouldProcess) &&
                        (IfRangeValid(httpRequestHeaders, lastModified, etag)))
                    {
                        return(SetRangeHeaders(context, httpRequestHeaders, fileLength.Value));
                    }
                }
                else
                {
                    Logger.NotEnabledForRangeProcessing();
                }
            }

            return(range : null, rangeLength : 0, serveBody : !HttpMethods.IsHead(request.Method));
        }
Пример #27
0
        public async Task InvokeAsync(HttpContext context, ISchema schema)
        {
            if (!context.Request.Path.StartsWithSegments(_settings.Path))
            {
                await _next(context);

                return;
            }

            // Handle requests as per recommendation at http://graphql.org/learn/serving-over-http/
            var httpRequest = context.Request;
            var request     = new Request();

            if (HttpMethods.IsGet(httpRequest.Method) || (HttpMethods.IsPost(httpRequest.Method) && httpRequest.Query.ContainsKey(Request.QueryKey)))
            {
                ExtractGraphQLRequestFromQueryString(httpRequest.Query, request);
            }
            else if (HttpMethods.IsPost(httpRequest.Method))
            {
                if (!MediaTypeHeaderValue.TryParse(httpRequest.ContentType, out var mediaTypeHeader))
                {
                    await WriteBadRequestResponseAsync(context, _writer, $"Invalid 'Content-Type' header: value '{httpRequest.ContentType}' could not be parsed.");

                    return;
                }

                switch (mediaTypeHeader.MediaType)
                {
                case "application/json":
                    request = Deserialize <Request>(httpRequest.Body);
                    break;

                case "application/graphql":
                    request.Query = await ReadAsStringAsync(httpRequest.Body);

                    break;

                case "application/x-www-form-urlencoded":
                    var formCollection = await httpRequest.ReadFormAsync();

                    ExtractGraphQLRequestFromPostBody(formCollection, request);
                    break;

                default:
                    await WriteBadRequestResponseAsync(context, _writer, "Invalid 'Content-Type' header: non-supported media type. See: http://graphql.org/learn/serving-over-http/.");

                    return;
                }
            }
            else
            {
                await WriteBadRequestResponseAsync(context, _writer, $"Invalid request method: value '{httpRequest.Method}' could not be handled. See: http://graphql.org/learn/serving-over-http/.");
            }

            var start  = DateTime.UtcNow;
            var result = await _executer.ExecuteAsync(_ =>
            {
                _.Schema        = schema;
                _.Query         = request?.Query;
                _.OperationName = request?.OperationName;
                _.Inputs        = request?.Variables.ToInputs();
                _.UserContext   = new UserContext
                {
                    User = context.User
                };
                _.FieldMiddleware.Use <InstrumentFieldsMiddleware>();
                _.ValidationRules  = DocumentValidator.CoreRules().Concat(_rules);
                _.EnableMetrics    = _settings.EnableMetrics;
                _.ExposeExceptions = _settings.ExposeExceptions;
            });

            if (_settings.EnableMetrics && result.Errors == null)
            {
                result.EnrichWithApolloTracing(start);
            }

            if (result.Errors != null)
            {
                _logger.LogError("GraphQL execution error(s): {Errors}", result.Errors);
            }

            await WriteResponseAsync(context, result);
        }
        protected virtual async Task ProxyRequestAsync(HttpContext context, string serviceHost)
        {
            using (var requestMessage = new HttpRequestMessage())
            {
                var requestMethod = context.Request.Method;
                if (!HttpMethods.IsGet(requestMethod) &&
                    !HttpMethods.IsHead(requestMethod) &&
                    !HttpMethods.IsDelete(requestMethod) &&
                    !HttpMethods.IsTrace(requestMethod))
                {
                    var streamContent = new StreamContent(context.Request.Body);
                    requestMessage.Content = streamContent;
                }

                var xForwardedForHeaderValues = new List <string>();

                // copy the request headers
                foreach (var header in context.Request.Headers)
                {
                    if (string.Equals(header.Key, XForwardedForHeaderKey, StringComparison.OrdinalIgnoreCase))
                    {
                        xForwardedForHeaderValues = header.Value.ToList();
                        continue;
                    }

                    if (!requestMessage.Headers.TryAddWithoutValidation(header.Key, header.Value.ToArray()))
                    {
                        requestMessage.Content?.Headers.TryAddWithoutValidation(header.Key, header.Value.ToArray());
                    }
                }

                // add remote ip address to x-forwarded-for header
                xForwardedForHeaderValues.Add(context.Connection.RemoteIpAddress.ToString());
                requestMessage.Headers.TryAddWithoutValidation(XForwardedForHeaderKey, xForwardedForHeaderValues);

                var requestUri = new Uri($"{context.Request.Scheme}://{serviceHost}{context.Request.Path}{context.Request.QueryString}");
                requestMessage.Headers.Host = requestUri.Authority;
                requestMessage.RequestUri   = requestUri;
                requestMessage.Method       = new HttpMethod(requestMethod);

                _logger.LogInformation($"ProxyRequestAsync RequestUri: {requestUri}");

                var httpClient = _httpClientFactory.CreateClient(nameof(ReverseProxyMiddleware));
                using (var responseMessage = await httpClient.SendAsync(requestMessage, HttpCompletionOption.ResponseHeadersRead, context.RequestAborted))
                {
                    context.Response.StatusCode = (int)responseMessage.StatusCode;

                    foreach (var header in responseMessage.Headers)
                    {
                        context.Response.Headers[header.Key] = header.Value.ToArray();
                    }

                    foreach (var header in responseMessage.Content.Headers)
                    {
                        context.Response.Headers[header.Key] = header.Value.ToArray();
                    }

                    // SendAsync removes chunking from the response. This removes the header so it doesn't expect a chunked response.
                    context.Response.Headers.Remove("transfer-encoding");

                    await responseMessage.Content.CopyToAsync(context.Response.Body);
                }
            }
        }
Пример #29
0
 private bool IsRequestingAsyncApiIndex(HttpRequest request)
 {
     return(HttpMethods.IsGet(request.Method) &&
            string.Equals(request.Path, "/asyncapi/index.html", StringComparison.OrdinalIgnoreCase));
 }
Пример #30
0
 private bool IsRequestingAsyncApiSchema(HttpRequest request)
 {
     return(HttpMethods.IsGet(request.Method) &&
            string.Equals(request.Path, _asyncApiOptionsAccessor.Value.Route, StringComparison.OrdinalIgnoreCase));
 }