Exemplo n.º 1
0
        public async Task <object> Dispatch(IanvsContext ianvsContext)
        {
            HttpRequestMessage downstreamMsg = ianvsContext.BackendMessage.Message;

            try
            {
                using HttpClient client = new HttpClient(
                          new HttpClientHandler()
                {
                    AllowAutoRedirect = false
                }
                          );

                _logger.LogInformation($"{Environment.MachineName} {ianvsContext.RequestId} Sending request to {downstreamMsg.Method} {downstreamMsg.RequestUri.ToString()}");
                HttpResponseMessage backendResponse = await client.SendAsync(downstreamMsg);

                _logger.LogInformation($"{Environment.MachineName} {ianvsContext.RequestId} Received response from {downstreamMsg.Method} {downstreamMsg.RequestUri.ToString()}");

                return(backendResponse);
            }
            catch (Exception e)
            {
                // Ok, something wrong happened - call the SWAT team
                _logger.LogError($"{Environment.MachineName} {ianvsContext.RequestId} Error occured sending request to {downstreamMsg.Method} {downstreamMsg.RequestUri.ToString()}");
                _logger.LogError($"{Environment.MachineName} {ianvsContext.RequestId} Error: {e.ToString()}");

                HttpResponseMessage error = new HttpResponseMessage(System.Net.HttpStatusCode.InternalServerError);
                return(error);
            }
        }
Exemplo n.º 2
0
        /// <summary>
        /// Promote request context variables to be used in variable swap operations
        /// </summary>
        /// <param name="httpContext">The incoming HTTP context</param>
        /// <param name="ianvsContext">The Ianvs processing context</param>
        public void PromoteVariables(HttpContext httpContext, IanvsContext ianvsContext)
        {
            ianvsContext.Variables = new Dictionary <string, string>();
            if (httpContext.Request.Body != null && httpContext.Request.Body.CanRead)
            {
                ianvsContext.IncomingRequest = httpContext.Request.ReadAsStringAsync().Result;
                ianvsContext.Variables.TryAdd("{request.body}", ianvsContext.IncomingRequest);
                ianvsContext.Variables.TryAdd("{request.header.content-Type}", httpContext.Request.ContentType);
                if (httpContext.Request.ContentLength.HasValue)
                {
                    ianvsContext.Variables.TryAdd("{request.header.content-Length}", httpContext.Request.ContentLength.Value.ToString());
                }
                else
                {
                    ianvsContext.Variables.TryAdd("{request.header.content-Length}", ianvsContext.IncomingRequest.Length.ToString());
                }
            }

            foreach (var header in httpContext.Request.Headers)
            {
                ianvsContext.Variables.TryAdd($"{{request.header.{header.Key}}}", header.Value.ToString());
            }
            foreach (var query in httpContext.Request.Query)
            {
                ianvsContext.Variables.TryAdd($"{{request.query.{query.Key}}}", query.Value.ToString());
            }
            ianvsContext.Variables.TryAdd("{request.received-at}", ianvsContext.ReceivedAt.ToUnixTimeMilliseconds().ToString());
            ianvsContext.Variables.TryAdd("{request.id}", ianvsContext.RequestId);
            ianvsContext.Variables.TryAdd("{request.track-id}", ianvsContext.TrackId);
            ianvsContext.Variables.TryAdd("{url}", ianvsContext.Url);
            ianvsContext.Variables.TryAdd("{method}", ianvsContext.Method);
            ianvsContext.Variables.TryAdd("{protocol}", ianvsContext.Protocol);
        }
Exemplo n.º 3
0
        /// <summary>
        /// Retireves the token value from the incoming request
        /// </summary>
        /// <param name="httpContext">The incoming request HTTP context</param>
        /// <param name="ianvsContext">The incoming request Ianvs context</param>
        /// <returns>The token value</returns>
        private string GetTokenValue(HttpContext httpContext, IanvsContext ianvsContext)
        {
            string tokenIn = ianvsContext.SecurityScheme.In ?? "header";

            return(tokenIn switch
            {
                "header" => GetTokenFromHeader(httpContext),
                "cookie" => GetTokenFromCookie(httpContext, ianvsContext.SecurityScheme.BearerFormat),
                _ => GetTokenFromHeader(httpContext)
            });
Exemplo n.º 4
0
        public bool CanAuthenticate(HttpContext httpContext, IanvsContext ianvsContext)
        {
            // Get token value, only header and cookie are supported
            string tokenValue = GetTokenValue(httpContext, ianvsContext);

            if (!string.IsNullOrWhiteSpace(tokenValue))
            {
                return(true);
            }
            return(false);
        }
Exemplo n.º 5
0
        public async Task InvokeAsync(HttpContext httpContext, IanvsContext ianvsContext)
        {
            // TODO: Implement Transformation
            // https://github.com/onyx-ws/ianvs/issues/10

            ianvsContext.BackendMessage = new BackendMessage();

            await _next(httpContext);

            ianvsContext.Response = ianvsContext.BackendResponse.Content;
        }
Exemplo n.º 6
0
        public async Task InvokeAsync(HttpContext context, IanvsContext ianvsContext,
                                      IIanvsConfigurationStore ianvsConfiguration, LoadBalancerFactory loadBalancerFactory)
        {
            // TODO: Implement Load Balancing
            // WIP - https://github.com/onyx-ws/ianvs/issues/5

            // Support for different load balancing modes - Random/Round Robin/etc.
            _logger.LogInformation($"{Environment.MachineName} {ianvsContext.RequestId} finding load balancer");
            string loadBalancerMode = ianvsContext.MatchedOperation.LoadBalancerMethod;

            if (string.IsNullOrWhiteSpace(loadBalancerMode))
            {
                loadBalancerMode = ianvsContext.MatchedEndpoint.LoadBalancerMethod;
            }
            if (string.IsNullOrWhiteSpace(loadBalancerMode))
            {
                loadBalancerMode = ianvsConfiguration.LoadBalancerMethod;
            }

            ILoadBalancer loadBalancer = loadBalancerFactory.GetLoadBalancer(loadBalancerMode);

            _logger.LogInformation($"{Environment.MachineName} {ianvsContext.RequestId} {loadBalancerMode} load balancer found");

            // Operation level servers take priority
            if (!(ianvsContext.MatchedOperation.Servers?.Count == 0))
            {
                _logger.LogInformation($"{Environment.MachineName} {ianvsContext.RequestId} Selecting server out of {ianvsContext.MatchedOperation.Servers.Count} server(s)");
                ianvsContext.TargetServer = await loadBalancer.Next(ianvsContext.MatchedOperation.Servers);

                _logger.LogInformation($"{Environment.MachineName} {ianvsContext.RequestId} Server {ianvsContext.TargetServer.Url} selected");
            }
            // If not operation level servers then use Endpoint level servers
            else if (!(ianvsContext.MatchedEndpoint.Servers?.Count == 0))
            {
                _logger.LogInformation($"{Environment.MachineName} {ianvsContext.RequestId} Selecting server out of {ianvsContext.MatchedEndpoint.Servers.Count} server(s)");
                ianvsContext.TargetServer = await loadBalancer.Next(ianvsContext.MatchedEndpoint.Servers);

                _logger.LogInformation($"{Environment.MachineName} {ianvsContext.RequestId} Server {ianvsContext.TargetServer.Url} selected");
            }
            // Else use global servers defined
            else
            {
                _logger.LogInformation($"{Environment.MachineName} {ianvsContext.RequestId} Selecting server out of {ianvsConfiguration.Servers.Count} server(s)");
                ianvsContext.TargetServer = await loadBalancer.Next(ianvsConfiguration.Servers);

                _logger.LogInformation($"{Environment.MachineName} {ianvsContext.RequestId} Server {ianvsContext.TargetServer.Url} selected");
            }

            ianvsContext.TargetUrl = ianvsContext.TargetServer.Url + (ianvsContext.MatchedEndpoint.Url == "/" ? "" : ianvsContext.MatchedEndpoint.Url);

            await _next(context);
        }
Exemplo n.º 7
0
        /// <summary>
        /// Prepare HTTP request message
        /// </summary>
        /// <param name="ianvsContext">Ianvs processing context</param>
        /// <returns>The HTTP request message to send to the backend</returns>
        public object PrepareRequest(IanvsContext ianvsContext, ISpan egressSpan)
        {
            string targetUrl = $"{ianvsContext.TargetUrl}?";

            foreach (var parameter in ianvsContext.MatchedOperation.Parameters)
            {
                parameter.To ??= parameter.In;
                if (parameter.To == "query")
                {
                    if (ianvsContext.Variables.TryGetValue($"{{request.query.{parameter.Name}}}", out string value))
                    {
                        targetUrl += $"{parameter.Name}={value}&";
                    }
                    else if (parameter.Default != null)
                    {
                        targetUrl += $"{parameter.Name}={parameter.Default}&";
                    }
                }
            }
            // Remove last character - either ? or &
            targetUrl = targetUrl.Remove(targetUrl.Length - 1, 1);

            HttpRequestMessage downstreamMsg = new HttpRequestMessage
            {
                Method     = new HttpMethod(ianvsContext.MatchedOperation.Method),
                RequestUri = new Uri(targetUrl)
            };

            if (!string.IsNullOrWhiteSpace(ianvsContext.IncomingRequest))
            {
                downstreamMsg.Content = new StringContent(
                    ianvsContext.IncomingRequest,
                    Encoding.UTF8,
                    ianvsContext.Variables.GetValueOrDefault("{request.header.content-Type}", "application/json")
                    );
            }
            if (egressSpan.Context.IsValid)
            {
                ianvsContext.Tracer.TextFormat.Inject(
                    egressSpan.Context,
                    downstreamMsg.Headers,
                    (headers, name, value) => headers.Add(name, value));
            }
            return(downstreamMsg);
        }
Exemplo n.º 8
0
        public async Task InvokeAsync(HttpContext httpContext, IanvsContext ianvsContext, DispatcherFactory dispatcherFactory, Tracer tracer)
        {
            var egressSpan = tracer.StartSpan("ianvs-egress");

            // TODO: Implement Protocol Translation - e.g. REST to gRPC
            // https://github.com/onyx-ws/ianvs/issues/11

            // Get the dispatcher matching for the backend
            ianvsContext.Dispatcher = dispatcherFactory.GetDispatcher(ianvsContext.MatchedEndpoint.Protocol);
            egressSpan.AddEvent("Preparing Request");
            // Prepare backend request message
            ianvsContext.BackendMessage.Message = ianvsContext.Dispatcher.PrepareRequest(ianvsContext, egressSpan);
            egressSpan.AddEvent("Request prepared");

            Stopwatch backendTimer = new Stopwatch();

            backendTimer.Start();

            egressSpan.AddEvent("Sending backend request");

            _logger.LogInformation($"{Environment.MachineName} {ianvsContext.RequestId} Sending request to backend service {ianvsContext.MatchedOperation.OperationId} {ianvsContext.MatchedOperation.Method} {ianvsContext.MatchedEndpoint.Url} {ianvsContext.TargetServer.Url}");
            Task <object> backendResponse = ianvsContext.Dispatcher.Dispatch(ianvsContext);

            _logger.LogInformation($"{Environment.MachineName} {ianvsContext.RequestId} Waiting for backend service response");
            backendResponse.Wait();
            backendTimer.Stop();
            egressSpan.AddEvent("Backend service response received");

            ianvsContext.BackendResponse = new BackendMessage()
            {
                Message = backendResponse.Result
            };

            ianvsContext.Dispatcher.ProcessResponse(ianvsContext);
            _logger.LogInformation($"{Environment.MachineName} {ianvsContext.RequestId} Backend service response received in {backendTimer.ElapsedMilliseconds}ms {ianvsContext.StatusCode}");

            egressSpan.End();
            // Egress middleware is the last in the Ianvs processing chain
            // No further calls made to _next
        }
Exemplo n.º 9
0
        /// <summary>
        /// Gets the OpenId config for the authentication authority
        /// </summary>
        /// <param name="ianvsContext">The incoming request Ianvs processing context</param>
        /// <returns>The OpenId config for the authentication authority</returns>
        private static async Task <OpenIdConnectConfiguration> GetIssuerConfig(IanvsContext ianvsContext)
        {
            OpenIdConnectConfiguration openIdConfig = new OpenIdConnectConfiguration();

            if (!string.IsNullOrWhiteSpace(ianvsContext.SecurityScheme.OpenIdConnectUrl))
            {
                IConfigurationManager <OpenIdConnectConfiguration> configurationManager =
                    new ConfigurationManager <OpenIdConnectConfiguration>(
                        ianvsContext.SecurityScheme.OpenIdConnectUrl,
                        new OpenIdConnectConfigurationRetriever(),
                        new HttpDocumentRetriever()
                {
                    RequireHttps = false
                }
                        );
                openIdConfig = await configurationManager.GetConfigurationAsync(CancellationToken.None);
            }

            // If user defined x-issuer then use this value instead of discovered value
            openIdConfig.Issuer = ianvsContext.SecurityScheme.Issuer ?? openIdConfig.Issuer;

            return(openIdConfig);
        }
Exemplo n.º 10
0
        /// <summary>
        /// Process backend HTTP response message into common Ianvs data
        /// </summary>
        /// <param name="ianvsContext">The Ianvs processing context</param>
        public async void ProcessResponse(IanvsContext ianvsContext)
        {
            HttpResponseMessage backendHttpResponse = ianvsContext.BackendResponse.Message as HttpResponseMessage;

            ianvsContext.StatusCode = (int)backendHttpResponse.StatusCode;

            if (backendHttpResponse.Content != null)
            {
                ianvsContext.BackendResponse.Content =
                    await backendHttpResponse.ReadBodyAsStringAsync();

                ianvsContext.BackendResponse.Headers =
                    backendHttpResponse.Headers.ToDictionary(
                        header => header.Key,
                        header => header.Value
                        )
                    .Concat(
                        backendHttpResponse.Content.Headers.ToDictionary(
                            header => header.Key,
                            header => header.Value
                            ))
                    .ToDictionary(
                        header => header.Key,
                        header => header.Value
                        );
            }
            else
            {
                ianvsContext.BackendResponse.Content = string.Empty;

                ianvsContext.BackendResponse.Headers =
                    backendHttpResponse.Headers.ToDictionary(
                        header => header.Key,
                        header => header.Value
                        );
            }
        }
Exemplo n.º 11
0
        public async Task InvokeAsync(HttpContext httpContext, IanvsContext ianvsContext, Tracer tracer)
        {
            using (tracer.StartActiveSpan("ianvs-ingress", SpanKind.Server, out ISpan ianvsSpan))
            {
                ianvsContext.TraceSpan = ianvsSpan;
                ianvsContext.Tracer    = tracer;

                try
                {
                    // TODO: Implement Ingress operations
                    Stopwatch ianvsTimer = new Stopwatch();
                    ianvsTimer.Start();

                    ianvsContext.ReceivedAt = DateTimeOffset.UtcNow;
                    ianvsContext.RequestId  = httpContext.TraceIdentifier;
                    ianvsContext.Url        = httpContext.Request.GetDisplayUrl();
                    ianvsContext.Method     = httpContext.Request.Method;
                    ianvsContext.Protocol   = httpContext.Request.Protocol;

                    _logger.LogInformation($"{Environment.MachineName} {ianvsContext.RequestId} Request starting {ianvsContext.Protocol} {ianvsContext.Method} {ianvsContext.Url}");
                    _logger.LogInformation($"{Environment.MachineName} {ianvsContext.RequestId} Request recieved at {ianvsContext.ReceivedAt}");

                    if (httpContext.Request.Headers.TryGetValue("x-ianvs-trackid", out StringValues trackId))
                    {
                        ianvsContext.TrackId = trackId.ToString();
                    }
                    else
                    {
                        ianvsContext.TrackId = Guid.NewGuid().ToString();
                    }

                    PromoteVariables(httpContext, ianvsContext);

                    // When response is starting capture and log time Ianvs took to process
                    httpContext.Response.OnStarting(async() =>
                    {
                        ianvsContext.ProcessingTime = ianvsTimer.ElapsedMilliseconds;
                        ianvsContext.ResponseSentAt = DateTimeOffset.UtcNow;
                        _logger.LogInformation($"{Environment.MachineName} {ianvsContext.RequestId} Response being sent at {ianvsContext.ResponseSentAt}.");
                        _logger.LogInformation($"{Environment.MachineName} {ianvsContext.RequestId} Processing took {ianvsContext.ProcessingTime}ms");
                    });

                    // When response is completed (i.e. Received by client) capture and log time it took
                    httpContext.Response.OnCompleted(async() =>
                    {
                        ianvsTimer.Stop();
                        ianvsContext.ProcessingCompletedAt = DateTimeOffset.UtcNow;
                        _logger.LogInformation($"{Environment.MachineName} {ianvsContext.RequestId} Response received by client at {ianvsContext.ProcessingCompletedAt}");
                        _logger.LogInformation($"{Environment.MachineName} {ianvsContext.RequestId} Request finished in {ianvsTimer.ElapsedMilliseconds}ms {httpContext.Response.StatusCode}");
                    });

                    await _next(httpContext);

                    httpContext.Response.StatusCode = ianvsContext.StatusCode;
                    await httpContext.Response.WriteAsync(ianvsContext.Response);
                }

                catch (Exception e)
                {
                    // Ok, something wrong happened - call the SWAT team
                    _logger.LogError($"{Environment.MachineName} {ianvsContext.RequestId} Error occured processing request");
                    _logger.LogError($"{Environment.MachineName} {ianvsContext.RequestId} Error: {e.ToString()}");

                    ianvsContext.ResponseSentAt     = DateTimeOffset.UtcNow;
                    ianvsContext.StatusCode         = 500;
                    httpContext.Response.StatusCode = ianvsContext.StatusCode;

                    await httpContext.Response.WriteAsync(string.Empty);
                }
            }
        }
Exemplo n.º 12
0
 private static TokenValidationParameters GetValidationParameters(OpenIdConnectConfiguration issureConfig, IanvsContext ianvsContext)
 {
     return(new TokenValidationParameters
     {
         ValidIssuer = issureConfig.Issuer,
         ValidAudiences = ianvsContext.SecurityScheme.Audiences,
         IssuerSigningKeys = issureConfig.SigningKeys
     });
 }
Exemplo n.º 13
0
        /// <summary>
        /// Authenticates the incoming request using JWT
        /// </summary>
        /// <param name="httpContext">The incoming request HTTP context</param>
        /// <param name="ianvsContext">The incoming request Ianvs context</param>
        /// <returns>The Ianvs Authentication result</returns>
        public async Task <AuthenticationResult> Authenticate(HttpContext httpContext, IanvsContext ianvsContext)
        {
            _logger.LogInformation($"{Environment.MachineName} {ianvsContext.RequestId} Starting JWT Validation");

            // Get token value, only header and cookie are supported
            string tokenValue = GetTokenValue(httpContext, ianvsContext);

            if (string.IsNullOrWhiteSpace(tokenValue))
            {
                _logger.LogError($"{Environment.MachineName} {ianvsContext.RequestId} No token found");

                return(new AuthenticationResult()
                {
                    Authenticated = false,
                    Error = "Token missing"
                });
            }

            _logger.LogInformation($"{Environment.MachineName} {ianvsContext.RequestId} Preparing validation parameters");

            // Get issuer configuration values
            OpenIdConnectConfiguration issureConfig = await GetIssuerConfig(ianvsContext);

            TokenValidationParameters validationParameters = GetValidationParameters(issureConfig, ianvsContext);

            ClaimsPrincipal principal;

            try
            {
                _logger.LogInformation($"{Environment.MachineName} {ianvsContext.RequestId} Validating request token");
                JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler();
                principal = handler.ValidateToken(tokenValue, validationParameters, out SecurityToken validatedToken);
            }
            catch (Exception e)
            {
                _logger.LogError($"{Environment.MachineName} {ianvsContext.RequestId} Token validation failed. {e.Message}");

                return(new AuthenticationResult()
                {
                    Authenticated = false,
                    Error = e.Message
                });
            }

            _logger.LogInformation($"{Environment.MachineName} {ianvsContext.RequestId} Token validated successfully");

            return(new AuthenticationResult()
            {
                Authenticated = true,
                Principal = principal
            });
        }
Exemplo n.º 14
0
 public async Task InvokeAsync(HttpContext context, IanvsContext ianvsContext)
 {
     await _next(context);
 }