mirror of
https://github.com/nsnail/Ocelot.git
synced 2025-08-04 16:28:28 +08:00
Feature/more validation (#174)
* added message assertion for validation test * another message assertion * more validation tests
This commit is contained in:
@ -15,35 +15,50 @@ namespace Ocelot.Configuration.Validator
|
||||
{
|
||||
RuleFor(configuration => configuration.ReRoutes)
|
||||
.SetCollectionValidator(new ReRouteFluentValidator(authenticationSchemeProvider));
|
||||
|
||||
RuleForEach(configuration => configuration.ReRoutes)
|
||||
.Must((config, reRoute) => IsNotDuplicateIn(reRoute, config.ReRoutes))
|
||||
.WithMessage((config, reRoute) => $"duplicate downstreampath {reRoute.UpstreamPathTemplate}");
|
||||
.WithMessage((config, reRoute) => $"{nameof(reRoute)} {reRoute.UpstreamPathTemplate} has duplicate");
|
||||
}
|
||||
|
||||
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 IsNotDuplicateIn(FileReRoute reRoute, List<FileReRoute> routes)
|
||||
private static bool IsNotDuplicateIn(FileReRoute reRoute, List<FileReRoute> reRoutes)
|
||||
{
|
||||
var reRoutesWithUpstreamPathTemplate = routes.Where(r => r.UpstreamPathTemplate == reRoute.UpstreamPathTemplate).ToList();
|
||||
var hasEmptyListToAllowAllHttpVerbs = reRoutesWithUpstreamPathTemplate.Any(x => x.UpstreamHttpMethod.Count == 0);
|
||||
var hasDuplicateEmptyListToAllowAllHttpVerbs = reRoutesWithUpstreamPathTemplate.Count(x => x.UpstreamHttpMethod.Count == 0) > 1;
|
||||
var matchingReRoutes = reRoutes.Where(r => r.UpstreamPathTemplate == reRoute.UpstreamPathTemplate).ToList();
|
||||
|
||||
var hasSpecificHttpVerbs = reRoutesWithUpstreamPathTemplate.Any(x => x.UpstreamHttpMethod.Count != 0);
|
||||
var hasDuplicateSpecificHttpVerbs = reRoutesWithUpstreamPathTemplate.SelectMany(x => x.UpstreamHttpMethod).GroupBy(x => x.ToLower()).SelectMany(x => x.Skip(1)).Any();
|
||||
if (hasDuplicateEmptyListToAllowAllHttpVerbs || hasDuplicateSpecificHttpVerbs || (hasEmptyListToAllowAllHttpVerbs && hasSpecificHttpVerbs))
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -17,19 +17,35 @@ namespace Ocelot.Configuration.Validator
|
||||
|
||||
RuleFor(reRoute => reRoute.DownstreamPathTemplate)
|
||||
.Must(path => path.StartsWith("/"))
|
||||
.WithMessage("downstream path {PropertyValue} doesnt start with forward slash");
|
||||
.WithMessage("{PropertyName} {PropertyValue} doesnt start with forward slash");
|
||||
|
||||
RuleFor(reRoute => reRoute.UpstreamPathTemplate)
|
||||
.Must(path => path.StartsWith("/"))
|
||||
.WithMessage("upstream path {PropertyValue} doesnt start with forward slash");
|
||||
.WithMessage("{PropertyName} {PropertyValue} doesnt start with forward slash");
|
||||
|
||||
RuleFor(reRoute => reRoute.DownstreamPathTemplate)
|
||||
.Must(path => !path.Contains("https://") && !path.Contains("http://"))
|
||||
.WithMessage("downstream path {PropertyValue} contains scheme");
|
||||
.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("rate limit period {PropertyValue} not contains (s,m,h,d)");
|
||||
.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.DownstreamHost).NotEmpty().WithMessage("When not using service discover DownstreamHost must be set or Ocelot cannot find your service!");
|
||||
});
|
||||
}
|
||||
|
||||
private async Task<bool> IsSupportedAuthenticationProviders(FileAuthenticationOptions authenticationOptions, CancellationToken cancellationToken)
|
||||
@ -39,6 +55,7 @@ namespace Ocelot.Configuration.Validator
|
||||
return true;
|
||||
}
|
||||
var schemes = await _authenticationSchemeProvider.GetAllSchemesAsync();
|
||||
|
||||
var supportedSchemes = schemes.Select(scheme => scheme.Name).ToList();
|
||||
|
||||
return supportedSchemes.Contains(authenticationOptions.AuthenticationProviderKey);
|
||||
|
Reference in New Issue
Block a user