mirror of
https://github.com/nsnail/Ocelot.git
synced 2025-06-19 06:38:14 +08:00
* #568 worked out how to check if qos handler present and kill Ocelot if options specified but no handler, need to refactor this into fluent validation style * #568 acceptance tests to make sure Ocelot won't start if the user specifies QoSOptions but doesnt have a QoSHandler registered
This commit is contained in:
@ -1,24 +1,30 @@
|
||||
using System.Collections.Generic;
|
||||
using Ocelot.Errors;
|
||||
|
||||
namespace Ocelot.Configuration.Validator
|
||||
{
|
||||
public class ConfigurationValidationResult
|
||||
{
|
||||
public ConfigurationValidationResult(bool isError)
|
||||
{
|
||||
IsError = isError;
|
||||
Errors = new List<Error>();
|
||||
}
|
||||
|
||||
public ConfigurationValidationResult(bool isError, List<Error> errors)
|
||||
{
|
||||
IsError = isError;
|
||||
Errors = errors;
|
||||
}
|
||||
|
||||
public bool IsError { get; }
|
||||
|
||||
public List<Error> Errors { get; }
|
||||
}
|
||||
}
|
||||
using System.Collections.Generic;
|
||||
using Ocelot.Errors;
|
||||
|
||||
namespace Ocelot.Configuration.Validator
|
||||
{
|
||||
public class ConfigurationValidationResult
|
||||
{
|
||||
public ConfigurationValidationResult(bool isError)
|
||||
{
|
||||
IsError = isError;
|
||||
Errors = new List<Error>();
|
||||
}
|
||||
|
||||
public ConfigurationValidationResult(bool isError, Error error)
|
||||
{
|
||||
IsError = isError;
|
||||
Errors = new List<Error> { error };
|
||||
}
|
||||
|
||||
public ConfigurationValidationResult(bool isError, List<Error> errors)
|
||||
{
|
||||
IsError = isError;
|
||||
Errors = errors;
|
||||
}
|
||||
|
||||
public bool IsError { get; }
|
||||
|
||||
public List<Error> Errors { get; }
|
||||
}
|
||||
}
|
||||
|
@ -1,129 +1,133 @@
|
||||
using FluentValidation;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Ocelot.Configuration.File;
|
||||
using Ocelot.Errors;
|
||||
using Ocelot.Responses;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ocelot.Configuration.Validator
|
||||
{
|
||||
using System;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Requester;
|
||||
|
||||
public class FileConfigurationFluentValidator : AbstractValidator<FileConfiguration>, IConfigurationValidator
|
||||
{
|
||||
private readonly IServiceProvider _provider;
|
||||
|
||||
public FileConfigurationFluentValidator(IAuthenticationSchemeProvider authenticationSchemeProvider, IServiceProvider provider)
|
||||
{
|
||||
_provider = provider;
|
||||
RuleFor(configuration => configuration.ReRoutes)
|
||||
.SetCollectionValidator(new ReRouteFluentValidator(authenticationSchemeProvider, provider));
|
||||
|
||||
RuleForEach(configuration => configuration.ReRoutes)
|
||||
.Must((config, reRoute) => IsNotDuplicateIn(reRoute, config.ReRoutes))
|
||||
.WithMessage((config, reRoute) => $"{nameof(reRoute)} {reRoute.UpstreamPathTemplate} has duplicate");
|
||||
|
||||
RuleForEach(configuration => configuration.ReRoutes)
|
||||
.Must((config, reRoute) => IsNotDuplicateIn(reRoute, config.Aggregates))
|
||||
.WithMessage((config, reRoute) => $"{nameof(reRoute)} {reRoute.UpstreamPathTemplate} has duplicate aggregate");
|
||||
|
||||
RuleForEach(configuration => configuration.Aggregates)
|
||||
.Must((config, aggregateReRoute) => IsNotDuplicateIn(aggregateReRoute, config.Aggregates))
|
||||
.WithMessage((config, aggregate) => $"{nameof(aggregate)} {aggregate.UpstreamPathTemplate} has duplicate aggregate");
|
||||
|
||||
RuleForEach(configuration => configuration.Aggregates)
|
||||
.Must((config, aggregateReRoute) => AllReRoutesForAggregateExist(aggregateReRoute, config.ReRoutes))
|
||||
.WithMessage((config, aggregateReRoute) => $"ReRoutes for {nameof(aggregateReRoute)} {aggregateReRoute.UpstreamPathTemplate} either do not exist or do not have correct ServiceName property");
|
||||
|
||||
RuleForEach(configuration => configuration.Aggregates)
|
||||
.Must((config, aggregateReRoute) => DoesNotContainReRoutesWithSpecificRequestIdKeys(aggregateReRoute, config.ReRoutes))
|
||||
.WithMessage((config, aggregateReRoute) => $"{nameof(aggregateReRoute)} {aggregateReRoute.UpstreamPathTemplate} contains ReRoute with specific RequestIdKey, this is not possible with Aggregates");
|
||||
}
|
||||
|
||||
private bool AllReRoutesForAggregateExist(FileAggregateReRoute fileAggregateReRoute, List<FileReRoute> reRoutes)
|
||||
{
|
||||
var reRoutesForAggregate = reRoutes.Where(r => fileAggregateReRoute.ReRouteKeys.Contains(r.Key));
|
||||
|
||||
return reRoutesForAggregate.Count() == fileAggregateReRoute.ReRouteKeys.Count;
|
||||
}
|
||||
|
||||
public async Task<Response<ConfigurationValidationResult>> IsValid(FileConfiguration configuration)
|
||||
{
|
||||
var validateResult = await ValidateAsync(configuration);
|
||||
|
||||
if (validateResult.IsValid)
|
||||
{
|
||||
return new OkResponse<ConfigurationValidationResult>(new ConfigurationValidationResult(false));
|
||||
}
|
||||
|
||||
var errors = validateResult.Errors.Select(failure => new FileValidationFailedError(failure.ErrorMessage));
|
||||
|
||||
var result = new ConfigurationValidationResult(true, errors.Cast<Error>().ToList());
|
||||
|
||||
return new OkResponse<ConfigurationValidationResult>(result);
|
||||
}
|
||||
|
||||
private static bool DoesNotContainReRoutesWithSpecificRequestIdKeys(FileAggregateReRoute fileAggregateReRoute,
|
||||
List<FileReRoute> reRoutes)
|
||||
{
|
||||
var reRoutesForAggregate = reRoutes.Where(r => fileAggregateReRoute.ReRouteKeys.Contains(r.Key));
|
||||
|
||||
return reRoutesForAggregate.All(r => string.IsNullOrEmpty(r.RequestIdKey));
|
||||
}
|
||||
|
||||
private static bool IsNotDuplicateIn(FileReRoute reRoute,
|
||||
List<FileReRoute> reRoutes)
|
||||
{
|
||||
var matchingReRoutes = reRoutes
|
||||
.Where(r => r.UpstreamPathTemplate == reRoute.UpstreamPathTemplate
|
||||
&& (r.UpstreamHost != reRoute.UpstreamHost || reRoute.UpstreamHost == null))
|
||||
.ToList();
|
||||
|
||||
if(matchingReRoutes.Count == 1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var allowAllVerbs = matchingReRoutes.Any(x => x.UpstreamHttpMethod.Count == 0);
|
||||
|
||||
var duplicateAllowAllVerbs = matchingReRoutes.Count(x => x.UpstreamHttpMethod.Count == 0) > 1;
|
||||
|
||||
var specificVerbs = matchingReRoutes.Any(x => x.UpstreamHttpMethod.Count != 0);
|
||||
|
||||
var duplicateSpecificVerbs = matchingReRoutes.SelectMany(x => x.UpstreamHttpMethod).GroupBy(x => x.ToLower()).SelectMany(x => x.Skip(1)).Any();
|
||||
|
||||
if (duplicateAllowAllVerbs || duplicateSpecificVerbs || (allowAllVerbs && specificVerbs))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool IsNotDuplicateIn(FileReRoute reRoute,
|
||||
List<FileAggregateReRoute> aggregateReRoutes)
|
||||
{
|
||||
var duplicate = aggregateReRoutes
|
||||
.Any(a => a.UpstreamPathTemplate == reRoute.UpstreamPathTemplate
|
||||
&& a.UpstreamHost == reRoute.UpstreamHost
|
||||
&& reRoute.UpstreamHttpMethod.Select(x => x.ToLower()).Contains("get"));
|
||||
|
||||
return !duplicate;
|
||||
}
|
||||
|
||||
private static bool IsNotDuplicateIn(FileAggregateReRoute reRoute,
|
||||
List<FileAggregateReRoute> aggregateReRoutes)
|
||||
{
|
||||
var matchingReRoutes = aggregateReRoutes
|
||||
.Where(r => r.UpstreamPathTemplate == reRoute.UpstreamPathTemplate
|
||||
&& r.UpstreamHost == reRoute.UpstreamHost)
|
||||
.ToList();
|
||||
|
||||
return matchingReRoutes.Count <= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
using FluentValidation;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Ocelot.Configuration.File;
|
||||
using Ocelot.Errors;
|
||||
using Ocelot.Responses;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ocelot.Configuration.Validator
|
||||
{
|
||||
using System;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Requester;
|
||||
|
||||
public class FileConfigurationFluentValidator : AbstractValidator<FileConfiguration>, IConfigurationValidator
|
||||
{
|
||||
private readonly QosDelegatingHandlerDelegate _qosDelegatingHandlerDelegate;
|
||||
|
||||
public FileConfigurationFluentValidator(IAuthenticationSchemeProvider authenticationSchemeProvider, IServiceProvider provider)
|
||||
{
|
||||
_qosDelegatingHandlerDelegate = provider.GetService<QosDelegatingHandlerDelegate>();
|
||||
|
||||
RuleFor(configuration => configuration.ReRoutes)
|
||||
.SetCollectionValidator(new ReRouteFluentValidator(authenticationSchemeProvider, _qosDelegatingHandlerDelegate));
|
||||
|
||||
RuleFor(configuration => configuration.GlobalConfiguration)
|
||||
.SetValidator(new FileGlobalConfigurationFluentValidator(_qosDelegatingHandlerDelegate));
|
||||
|
||||
RuleForEach(configuration => configuration.ReRoutes)
|
||||
.Must((config, reRoute) => IsNotDuplicateIn(reRoute, config.ReRoutes))
|
||||
.WithMessage((config, reRoute) => $"{nameof(reRoute)} {reRoute.UpstreamPathTemplate} has duplicate");
|
||||
|
||||
RuleForEach(configuration => configuration.ReRoutes)
|
||||
.Must((config, reRoute) => IsNotDuplicateIn(reRoute, config.Aggregates))
|
||||
.WithMessage((config, reRoute) => $"{nameof(reRoute)} {reRoute.UpstreamPathTemplate} has duplicate aggregate");
|
||||
|
||||
RuleForEach(configuration => configuration.Aggregates)
|
||||
.Must((config, aggregateReRoute) => IsNotDuplicateIn(aggregateReRoute, config.Aggregates))
|
||||
.WithMessage((config, aggregate) => $"{nameof(aggregate)} {aggregate.UpstreamPathTemplate} has duplicate aggregate");
|
||||
|
||||
RuleForEach(configuration => configuration.Aggregates)
|
||||
.Must((config, aggregateReRoute) => AllReRoutesForAggregateExist(aggregateReRoute, config.ReRoutes))
|
||||
.WithMessage((config, aggregateReRoute) => $"ReRoutes for {nameof(aggregateReRoute)} {aggregateReRoute.UpstreamPathTemplate} either do not exist or do not have correct ServiceName property");
|
||||
|
||||
RuleForEach(configuration => configuration.Aggregates)
|
||||
.Must((config, aggregateReRoute) => DoesNotContainReRoutesWithSpecificRequestIdKeys(aggregateReRoute, config.ReRoutes))
|
||||
.WithMessage((config, aggregateReRoute) => $"{nameof(aggregateReRoute)} {aggregateReRoute.UpstreamPathTemplate} contains ReRoute with specific RequestIdKey, this is not possible with Aggregates");
|
||||
}
|
||||
|
||||
public async Task<Response<ConfigurationValidationResult>> IsValid(FileConfiguration configuration)
|
||||
{
|
||||
var validateResult = await ValidateAsync(configuration);
|
||||
|
||||
if (validateResult.IsValid)
|
||||
{
|
||||
return new OkResponse<ConfigurationValidationResult>(new ConfigurationValidationResult(false));
|
||||
}
|
||||
|
||||
var errors = validateResult.Errors.Select(failure => new FileValidationFailedError(failure.ErrorMessage));
|
||||
|
||||
var result = new ConfigurationValidationResult(true, errors.Cast<Error>().ToList());
|
||||
|
||||
return new OkResponse<ConfigurationValidationResult>(result);
|
||||
}
|
||||
|
||||
private bool AllReRoutesForAggregateExist(FileAggregateReRoute fileAggregateReRoute, List<FileReRoute> reRoutes)
|
||||
{
|
||||
var reRoutesForAggregate = reRoutes.Where(r => fileAggregateReRoute.ReRouteKeys.Contains(r.Key));
|
||||
|
||||
return reRoutesForAggregate.Count() == fileAggregateReRoute.ReRouteKeys.Count;
|
||||
}
|
||||
|
||||
private static bool DoesNotContainReRoutesWithSpecificRequestIdKeys(FileAggregateReRoute fileAggregateReRoute,
|
||||
List<FileReRoute> reRoutes)
|
||||
{
|
||||
var reRoutesForAggregate = reRoutes.Where(r => fileAggregateReRoute.ReRouteKeys.Contains(r.Key));
|
||||
|
||||
return reRoutesForAggregate.All(r => string.IsNullOrEmpty(r.RequestIdKey));
|
||||
}
|
||||
|
||||
private static bool IsNotDuplicateIn(FileReRoute reRoute,
|
||||
List<FileReRoute> reRoutes)
|
||||
{
|
||||
var matchingReRoutes = reRoutes
|
||||
.Where(r => r.UpstreamPathTemplate == reRoute.UpstreamPathTemplate
|
||||
&& (r.UpstreamHost != reRoute.UpstreamHost || reRoute.UpstreamHost == null))
|
||||
.ToList();
|
||||
|
||||
if (matchingReRoutes.Count == 1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var allowAllVerbs = matchingReRoutes.Any(x => x.UpstreamHttpMethod.Count == 0);
|
||||
|
||||
var duplicateAllowAllVerbs = matchingReRoutes.Count(x => x.UpstreamHttpMethod.Count == 0) > 1;
|
||||
|
||||
var specificVerbs = matchingReRoutes.Any(x => x.UpstreamHttpMethod.Count != 0);
|
||||
|
||||
var duplicateSpecificVerbs = matchingReRoutes.SelectMany(x => x.UpstreamHttpMethod).GroupBy(x => x.ToLower()).SelectMany(x => x.Skip(1)).Any();
|
||||
|
||||
if (duplicateAllowAllVerbs || duplicateSpecificVerbs || (allowAllVerbs && specificVerbs))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool IsNotDuplicateIn(FileReRoute reRoute,
|
||||
List<FileAggregateReRoute> aggregateReRoutes)
|
||||
{
|
||||
var duplicate = aggregateReRoutes
|
||||
.Any(a => a.UpstreamPathTemplate == reRoute.UpstreamPathTemplate
|
||||
&& a.UpstreamHost == reRoute.UpstreamHost
|
||||
&& reRoute.UpstreamHttpMethod.Select(x => x.ToLower()).Contains("get"));
|
||||
|
||||
return !duplicate;
|
||||
}
|
||||
|
||||
private static bool IsNotDuplicateIn(FileAggregateReRoute reRoute,
|
||||
List<FileAggregateReRoute> aggregateReRoutes)
|
||||
{
|
||||
var matchingReRoutes = aggregateReRoutes
|
||||
.Where(r => r.UpstreamPathTemplate == reRoute.UpstreamPathTemplate
|
||||
&& r.UpstreamHost == reRoute.UpstreamHost)
|
||||
.ToList();
|
||||
|
||||
return matchingReRoutes.Count <= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,21 @@
|
||||
using FluentValidation;
|
||||
using Ocelot.Configuration.File;
|
||||
|
||||
namespace Ocelot.Configuration.Validator
|
||||
{
|
||||
using Requester;
|
||||
|
||||
public class FileGlobalConfigurationFluentValidator : AbstractValidator<FileGlobalConfiguration>
|
||||
{
|
||||
private readonly QosDelegatingHandlerDelegate _qosDelegatingHandlerDelegate;
|
||||
|
||||
public FileGlobalConfigurationFluentValidator(QosDelegatingHandlerDelegate qosDelegatingHandlerDelegate)
|
||||
{
|
||||
_qosDelegatingHandlerDelegate = qosDelegatingHandlerDelegate;
|
||||
|
||||
RuleFor(configuration => configuration.QoSOptions)
|
||||
.SetValidator(new FileQoSOptionsFluentValidator(_qosDelegatingHandlerDelegate));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
using FluentValidation;
|
||||
using Ocelot.Configuration.File;
|
||||
|
||||
namespace Ocelot.Configuration.Validator
|
||||
{
|
||||
using Requester;
|
||||
|
||||
public class FileQoSOptionsFluentValidator : AbstractValidator<FileQoSOptions>
|
||||
{
|
||||
private readonly QosDelegatingHandlerDelegate _qosDelegatingHandlerDelegate;
|
||||
|
||||
public FileQoSOptionsFluentValidator(QosDelegatingHandlerDelegate qosDelegatingHandlerDelegate)
|
||||
{
|
||||
_qosDelegatingHandlerDelegate = qosDelegatingHandlerDelegate;
|
||||
|
||||
When(qosOptions => qosOptions.TimeoutValue > 0 && qosOptions.ExceptionsAllowedBeforeBreaking > 0, () => {
|
||||
RuleFor(qosOptions => qosOptions)
|
||||
.Must(HaveQosHandlerRegistered)
|
||||
.WithMessage("Unable to start Ocelot because either a ReRoute or GlobalConfiguration are using QoSOptions but no QosDelegatingHandlerDelegate has been registered in dependency injection container. Are you missing a package like Ocelot.Provider.Polly and services.AddPolly()?");
|
||||
});
|
||||
}
|
||||
|
||||
private bool HaveQosHandlerRegistered(FileQoSOptions arg)
|
||||
{
|
||||
return _qosDelegatingHandlerDelegate != null;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,92 +1,95 @@
|
||||
namespace Ocelot.Configuration.Validator
|
||||
{
|
||||
using FluentValidation;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Ocelot.Configuration.File;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Requester;
|
||||
|
||||
public class ReRouteFluentValidator : AbstractValidator<FileReRoute>
|
||||
{
|
||||
private readonly IAuthenticationSchemeProvider _authenticationSchemeProvider;
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
|
||||
public ReRouteFluentValidator(IAuthenticationSchemeProvider authenticationSchemeProvider, IServiceProvider serviceProvider)
|
||||
{
|
||||
_authenticationSchemeProvider = authenticationSchemeProvider;
|
||||
_serviceProvider = serviceProvider;
|
||||
|
||||
RuleFor(reRoute => reRoute.DownstreamPathTemplate)
|
||||
.Must(path => path.StartsWith("/"))
|
||||
.WithMessage("{PropertyName} {PropertyValue} doesnt start with forward slash");
|
||||
|
||||
RuleFor(reRoute => reRoute.UpstreamPathTemplate)
|
||||
.Must(path => !path.Contains("//"))
|
||||
.WithMessage("{PropertyName} {PropertyValue} contains double forward slash, Ocelot does not support this at the moment. Please raise an issue in GitHib if you need this feature.");
|
||||
|
||||
RuleFor(reRoute => reRoute.DownstreamPathTemplate)
|
||||
.Must(path => !path.Contains("//"))
|
||||
.WithMessage("{PropertyName} {PropertyValue} contains double forward slash, Ocelot does not support this at the moment. Please raise an issue in GitHib if you need this feature.");
|
||||
|
||||
RuleFor(reRoute => reRoute.UpstreamPathTemplate)
|
||||
.Must(path => path.StartsWith("/"))
|
||||
.WithMessage("{PropertyName} {PropertyValue} doesnt start with forward slash");
|
||||
|
||||
RuleFor(reRoute => reRoute.DownstreamPathTemplate)
|
||||
.Must(path => !path.Contains("https://") && !path.Contains("http://"))
|
||||
.WithMessage("{PropertyName} {PropertyValue} contains scheme");
|
||||
|
||||
RuleFor(reRoute => reRoute.UpstreamPathTemplate)
|
||||
.Must(path => !path.Contains("https://") && !path.Contains("http://"))
|
||||
.WithMessage("{PropertyName} {PropertyValue} contains scheme");
|
||||
|
||||
RuleFor(reRoute => reRoute.RateLimitOptions)
|
||||
.Must(IsValidPeriod)
|
||||
.WithMessage("RateLimitOptions.Period does not contains (s,m,h,d)");
|
||||
|
||||
RuleFor(reRoute => reRoute.AuthenticationOptions)
|
||||
.MustAsync(IsSupportedAuthenticationProviders)
|
||||
.WithMessage("{PropertyValue} is unsupported authentication provider");
|
||||
|
||||
When(reRoute => reRoute.UseServiceDiscovery, () => {
|
||||
RuleFor(r => r.ServiceName).NotEmpty()
|
||||
.WithMessage("ServiceName cannot be empty or null when using service discovery or Ocelot cannot look up your service!");
|
||||
});
|
||||
|
||||
When(reRoute => !reRoute.UseServiceDiscovery, () => {
|
||||
RuleFor(r => r.DownstreamHostAndPorts).NotEmpty()
|
||||
.WithMessage("When not using service discovery DownstreamHostAndPorts must be set and not empty or Ocelot cannot find your service!");
|
||||
});
|
||||
|
||||
When(reRoute => !reRoute.UseServiceDiscovery, () => {
|
||||
RuleFor(reRoute => reRoute.DownstreamHostAndPorts)
|
||||
.SetCollectionValidator(new HostAndPortValidator());
|
||||
});
|
||||
}
|
||||
|
||||
private async Task<bool> IsSupportedAuthenticationProviders(FileAuthenticationOptions authenticationOptions, CancellationToken cancellationToken)
|
||||
{
|
||||
if (string.IsNullOrEmpty(authenticationOptions.AuthenticationProviderKey))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var schemes = await _authenticationSchemeProvider.GetAllSchemesAsync();
|
||||
|
||||
var supportedSchemes = schemes.Select(scheme => scheme.Name).ToList();
|
||||
|
||||
return supportedSchemes.Contains(authenticationOptions.AuthenticationProviderKey);
|
||||
}
|
||||
|
||||
private static bool IsValidPeriod(FileRateLimitRule rateLimitOptions)
|
||||
{
|
||||
string period = rateLimitOptions.Period;
|
||||
|
||||
return !rateLimitOptions.EnableRateLimiting || period.Contains("s") || period.Contains("m") || period.Contains("h") || period.Contains("d");
|
||||
}
|
||||
}
|
||||
}
|
||||
namespace Ocelot.Configuration.Validator
|
||||
{
|
||||
using FluentValidation;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Ocelot.Configuration.File;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Requester;
|
||||
|
||||
public class ReRouteFluentValidator : AbstractValidator<FileReRoute>
|
||||
{
|
||||
private readonly IAuthenticationSchemeProvider _authenticationSchemeProvider;
|
||||
private readonly QosDelegatingHandlerDelegate _qosDelegatingHandlerDelegate;
|
||||
|
||||
public ReRouteFluentValidator(IAuthenticationSchemeProvider authenticationSchemeProvider, QosDelegatingHandlerDelegate qosDelegatingHandlerDelegate)
|
||||
{
|
||||
_authenticationSchemeProvider = authenticationSchemeProvider;
|
||||
_qosDelegatingHandlerDelegate = qosDelegatingHandlerDelegate;
|
||||
|
||||
RuleFor(reRoute => reRoute.QoSOptions)
|
||||
.SetValidator(new FileQoSOptionsFluentValidator(_qosDelegatingHandlerDelegate));
|
||||
|
||||
RuleFor(reRoute => reRoute.DownstreamPathTemplate)
|
||||
.Must(path => path.StartsWith("/"))
|
||||
.WithMessage("{PropertyName} {PropertyValue} doesnt start with forward slash");
|
||||
|
||||
RuleFor(reRoute => reRoute.UpstreamPathTemplate)
|
||||
.Must(path => !path.Contains("//"))
|
||||
.WithMessage("{PropertyName} {PropertyValue} contains double forward slash, Ocelot does not support this at the moment. Please raise an issue in GitHib if you need this feature.");
|
||||
|
||||
RuleFor(reRoute => reRoute.DownstreamPathTemplate)
|
||||
.Must(path => !path.Contains("//"))
|
||||
.WithMessage("{PropertyName} {PropertyValue} contains double forward slash, Ocelot does not support this at the moment. Please raise an issue in GitHib if you need this feature.");
|
||||
|
||||
RuleFor(reRoute => reRoute.UpstreamPathTemplate)
|
||||
.Must(path => path.StartsWith("/"))
|
||||
.WithMessage("{PropertyName} {PropertyValue} doesnt start with forward slash");
|
||||
|
||||
RuleFor(reRoute => reRoute.DownstreamPathTemplate)
|
||||
.Must(path => !path.Contains("https://") && !path.Contains("http://"))
|
||||
.WithMessage("{PropertyName} {PropertyValue} contains scheme");
|
||||
|
||||
RuleFor(reRoute => reRoute.UpstreamPathTemplate)
|
||||
.Must(path => !path.Contains("https://") && !path.Contains("http://"))
|
||||
.WithMessage("{PropertyName} {PropertyValue} contains scheme");
|
||||
|
||||
RuleFor(reRoute => reRoute.RateLimitOptions)
|
||||
.Must(IsValidPeriod)
|
||||
.WithMessage("RateLimitOptions.Period does not contains (s,m,h,d)");
|
||||
|
||||
RuleFor(reRoute => reRoute.AuthenticationOptions)
|
||||
.MustAsync(IsSupportedAuthenticationProviders)
|
||||
.WithMessage("{PropertyValue} is unsupported authentication provider");
|
||||
|
||||
When(reRoute => reRoute.UseServiceDiscovery, () => {
|
||||
RuleFor(r => r.ServiceName).NotEmpty()
|
||||
.WithMessage("ServiceName cannot be empty or null when using service discovery or Ocelot cannot look up your service!");
|
||||
});
|
||||
|
||||
When(reRoute => !reRoute.UseServiceDiscovery, () => {
|
||||
RuleFor(r => r.DownstreamHostAndPorts).NotEmpty()
|
||||
.WithMessage("When not using service discovery DownstreamHostAndPorts must be set and not empty or Ocelot cannot find your service!");
|
||||
});
|
||||
|
||||
When(reRoute => !reRoute.UseServiceDiscovery, () => {
|
||||
RuleFor(reRoute => reRoute.DownstreamHostAndPorts)
|
||||
.SetCollectionValidator(new HostAndPortValidator());
|
||||
});
|
||||
}
|
||||
|
||||
private async Task<bool> IsSupportedAuthenticationProviders(FileAuthenticationOptions authenticationOptions, CancellationToken cancellationToken)
|
||||
{
|
||||
if (string.IsNullOrEmpty(authenticationOptions.AuthenticationProviderKey))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var schemes = await _authenticationSchemeProvider.GetAllSchemesAsync();
|
||||
|
||||
var supportedSchemes = schemes.Select(scheme => scheme.Name).ToList();
|
||||
|
||||
return supportedSchemes.Contains(authenticationOptions.AuthenticationProviderKey);
|
||||
}
|
||||
|
||||
private static bool IsValidPeriod(FileRateLimitRule rateLimitOptions)
|
||||
{
|
||||
string period = rateLimitOptions.Period;
|
||||
|
||||
return !rateLimitOptions.EnableRateLimiting || period.Contains("s") || period.Contains("m") || period.Contains("h") || period.Contains("d");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user