private async Task <AuthenticationResult> Authenticate(HttpContext httpContext, Common.IanvsContext ianvsContext, IIanvsConfigurationStore ianvsConfiguration, AuthenticatorFactory authenticatorFactory) { // If multiple schemes are defined on the operation, only one can apply to the request; check which one foreach (Ianvs::SecurityRequirement securityRequirement in ianvsContext.Security) { Ianvs::SecurityScheme schemeDefinition = ianvsConfiguration.SecuritySchemes? .Find(s => s.Name == securityRequirement.SchemeName); if (schemeDefinition != null) { ianvsContext.SecurityScheme = schemeDefinition; IAuthenticationHandler authenticator = authenticatorFactory.GetAuthenticator(ianvsContext.SecurityScheme); if (authenticator.CanAuthenticate(httpContext, ianvsContext)) { ianvsContext.SecurityRequirement = securityRequirement; return(await authenticator.Authenticate(httpContext, ianvsContext)); } ianvsContext.SecurityScheme = null; } } // Couldn't apply security requirements return(new AuthenticationResult() { Authenticated = false, Error = "No Matching Security Scheme" }); }
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); }
public async Task InvokeAsync(HttpContext httpContext, Ianvs::IanvsContext ianvsContext, IIanvsConfigurationStore ianvsConfiguration, AuthenticatorFactory authenticatorFactory) { // TODO: Implement Security // https://github.com/onyx-ws/ianvs/issues/8 // Any security requirements? ianvsContext.Security = GetSecurityRequirements(ianvsContext); // Yes if (ianvsContext.Security?.Count > 0) { _logger.LogInformation($"{Environment.MachineName} {ianvsContext.RequestId} Authenticating request"); AuthenticationResult authResult = await Authenticate(httpContext, ianvsContext, ianvsConfiguration, authenticatorFactory); if (!authResult.Authenticated) { _logger.LogWarning($"{Environment.MachineName} {ianvsContext.RequestId} Request authentication failed"); _logger.LogWarning($"{Environment.MachineName} {ianvsContext.RequestId} Authentication Error: {authResult.Error}"); SetUnAuthorizedResponse(ianvsContext); return; } else { _logger.LogInformation($"{Environment.MachineName} {ianvsContext.RequestId} Request authenticated successfully"); ianvsContext.Principal = authResult.Principal; _logger.LogInformation($"{Environment.MachineName} {ianvsContext.RequestId} Authorizing request"); authResult = await Authorize(httpContext, ianvsContext); if (!authResult.Authenticated) { _logger.LogWarning($"{Environment.MachineName} {ianvsContext.RequestId} Request authorization failed"); _logger.LogWarning($"{Environment.MachineName} {ianvsContext.RequestId} Authorization Error: {authResult.Error}"); SetForbiddenResponse(ianvsContext); return; } else { _logger.LogInformation($"{Environment.MachineName} {ianvsContext.RequestId} Request authorized successfully"); } } } // request authenticated or no security requirement await _next(httpContext); }
public async Task InvokeAsync(HttpContext httpContext, Ianvs::IanvsContext ianvsContext, IIanvsConfigurationStore ianvsConfiguration, Tracer tracer) { // TODO: Implement Routing // WIP - https://github.com/onyx-ws/ianvs/issues/1 // TODO: Handle endpoints with templates // https://github.com/onyx-ws/ianvs/issues/2 var routingSpan = tracer.StartSpan("ianvs-routing"); // Find a matching Path string requestPath = httpContext.Request.Path; _logger.LogInformation($"{Environment.MachineName} {ianvsContext.RequestId} Looking for a configured route"); Ianvs::Endpoint matchedEndpoint = ianvsConfiguration.Endpoints.FirstOrDefault( endpoint => requestPath == (!string.IsNullOrWhiteSpace(endpoint.IanvsUrl) ? endpoint.IanvsUrl : endpoint.Url) ); if (matchedEndpoint is null) { // Path not found // Return 404 - Not Found (https://tools.ietf.org/html/rfc7231#section-6.5.4) routingSpan.AddEvent("Route not matched"); _logger.LogWarning($"{Environment.MachineName} {ianvsContext.RequestId} No configured route found"); ianvsContext.StatusCode = 404; ianvsContext.Response = ""; } else { ianvsContext.MatchedEndpoint = matchedEndpoint; _logger.LogInformation($"{Environment.MachineName} {ianvsContext.RequestId} Possible matching route found at {ianvsContext.MatchedEndpoint.Url}"); // Path found - Match Operation string requestMethod = httpContext.Request.Method; Ianvs::Operation matchedOperation = matchedEndpoint.Operations.FirstOrDefault( operation => operation.Method.Equals(requestMethod, StringComparison.InvariantCultureIgnoreCase) ); if (matchedOperation is null) { // Operation not found // Return 405 - Method Not Allowed (https://tools.ietf.org/html/rfc7231#section-6.5.5) routingSpan.AddEvent("Method not matched"); _logger.LogWarning($"{Environment.MachineName} {ianvsContext.RequestId} No configured operation found"); ianvsContext.StatusCode = 405; ianvsContext.Response = ""; } else { // Operation found // Simulate operation routingSpan.AddEvent("Route found"); ianvsContext.MatchedOperation = matchedOperation; _logger.LogInformation($"{Environment.MachineName} {ianvsContext.RequestId} Matched route at {ianvsContext.MatchedOperation.OperationId} {ianvsContext.MatchedOperation.Method} {ianvsContext.MatchedEndpoint.Url}"); await _next(httpContext); } } routingSpan.End(); }