mirror of
				https://github.com/nsnail/Ocelot.git
				synced 2025-11-04 19:30:49 +08:00 
			
		
		
		
	Merge pull request #51 from ThreeMammals/develop
Feature/adding some re route specific validation tests (#590)
This commit is contained in:
		@@ -62,7 +62,8 @@ The secret is the client secret that Ocelot's internal IdentityServer will use t
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
In order for the administration API to work, Ocelot / IdentityServer must be able to call itself for validation. This means that you need to add the base url of Ocelot 
 | 
			
		||||
to global configuration if it is not default (http://localhost:5000). This can be done as follows..
 | 
			
		||||
to global configuration if it is not default (http://localhost:5000). Please note if you are using something like docker to host Ocelot it might not be able to 
 | 
			
		||||
call back to localhost etc and you need to know what you are doing with docker networking in this scenario. Anyway this can be done as follows..
 | 
			
		||||
 | 
			
		||||
If you want to run on a different host and port locally..
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using Ocelot.Errors;
 | 
			
		||||
 | 
			
		||||
namespace Ocelot.Configuration.Validator
 | 
			
		||||
namespace Ocelot.Configuration.Validator
 | 
			
		||||
{
 | 
			
		||||
    using System.Collections.Generic;
 | 
			
		||||
    using Ocelot.Errors;
 | 
			
		||||
 | 
			
		||||
    public class ConfigurationValidationResult
 | 
			
		||||
    {
 | 
			
		||||
        public ConfigurationValidationResult(bool isError)
 | 
			
		||||
@@ -11,12 +11,6 @@ namespace Ocelot.Configuration.Validator
 | 
			
		||||
            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;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,46 +1,42 @@
 | 
			
		||||
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
 | 
			
		||||
namespace Ocelot.Configuration.Validator
 | 
			
		||||
{
 | 
			
		||||
    using FluentValidation;
 | 
			
		||||
    using File;
 | 
			
		||||
    using Errors;
 | 
			
		||||
    using Responses;
 | 
			
		||||
    using System.Collections.Generic;
 | 
			
		||||
    using System.Linq;
 | 
			
		||||
    using System.Threading.Tasks;
 | 
			
		||||
    using System;
 | 
			
		||||
    using Microsoft.Extensions.DependencyInjection;
 | 
			
		||||
    using Ocelot.ServiceDiscovery;
 | 
			
		||||
    using Requester;
 | 
			
		||||
    using ServiceDiscovery;
 | 
			
		||||
 | 
			
		||||
    public class FileConfigurationFluentValidator : AbstractValidator<FileConfiguration>, IConfigurationValidator
 | 
			
		||||
    {
 | 
			
		||||
        private readonly QosDelegatingHandlerDelegate _qosDelegatingHandlerDelegate;
 | 
			
		||||
        private readonly List<ServiceDiscoveryFinderDelegate> _serviceDiscoveryFinderDelegates;
 | 
			
		||||
        public FileConfigurationFluentValidator(IAuthenticationSchemeProvider authenticationSchemeProvider, IServiceProvider provider)
 | 
			
		||||
 | 
			
		||||
        public FileConfigurationFluentValidator(IServiceProvider provider, ReRouteFluentValidator reRouteFluentValidator, FileGlobalConfigurationFluentValidator fileGlobalConfigurationFluentValidator)
 | 
			
		||||
        {
 | 
			
		||||
            _qosDelegatingHandlerDelegate = provider.GetService<QosDelegatingHandlerDelegate>();
 | 
			
		||||
            _serviceDiscoveryFinderDelegates = provider
 | 
			
		||||
                .GetServices<ServiceDiscoveryFinderDelegate>()
 | 
			
		||||
                .ToList();
 | 
			
		||||
 | 
			
		||||
            RuleFor(configuration => configuration.ReRoutes)
 | 
			
		||||
                .SetCollectionValidator(new ReRouteFluentValidator(authenticationSchemeProvider, _qosDelegatingHandlerDelegate));
 | 
			
		||||
                .SetCollectionValidator(reRouteFluentValidator);
 | 
			
		||||
 | 
			
		||||
            RuleFor(configuration => configuration.GlobalConfiguration)
 | 
			
		||||
                .SetValidator(new FileGlobalConfigurationFluentValidator(_qosDelegatingHandlerDelegate));
 | 
			
		||||
                .SetValidator(fileGlobalConfigurationFluentValidator);
 | 
			
		||||
 | 
			
		||||
            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) => HaveServiceDiscoveryProviderRegitered(reRoute, config.GlobalConfiguration.ServiceDiscoveryProvider))
 | 
			
		||||
                .Must((config, reRoute) => HaveServiceDiscoveryProviderRegistered(reRoute, config.GlobalConfiguration.ServiceDiscoveryProvider))
 | 
			
		||||
                .WithMessage((config, reRoute) => $"Unable to start Ocelot, errors are: Unable to start Ocelot because either a ReRoute or GlobalConfiguration are using ServiceDiscoveryOptions but no ServiceDiscoveryFinderDelegate has been registered in dependency injection container. Are you missing a package like Ocelot.Provider.Consul and services.AddConsul() or Ocelot.Provider.Eureka and services.AddEureka()?");
 | 
			
		||||
 | 
			
		||||
            RuleFor(configuration => configuration.GlobalConfiguration.ServiceDiscoveryProvider)
 | 
			
		||||
                .Must((config) => HaveServiceDiscoveryProviderRegitered(config))
 | 
			
		||||
                .Must(HaveServiceDiscoveryProviderRegistered)
 | 
			
		||||
                .WithMessage((config, reRoute) => $"Unable to start Ocelot, errors are: Unable to start Ocelot because either a ReRoute or GlobalConfiguration are using ServiceDiscoveryOptions but no ServiceDiscoveryFinderDelegate has been registered in dependency injection container. Are you missing a package like Ocelot.Provider.Consul and services.AddConsul() or Ocelot.Provider.Eureka and services.AddEureka()?");
 | 
			
		||||
 | 
			
		||||
            RuleForEach(configuration => configuration.ReRoutes)
 | 
			
		||||
@@ -60,7 +56,7 @@ namespace Ocelot.Configuration.Validator
 | 
			
		||||
                .WithMessage((config, aggregateReRoute) => $"{nameof(aggregateReRoute)} {aggregateReRoute.UpstreamPathTemplate} contains ReRoute with specific RequestIdKey, this is not possible with Aggregates");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private bool HaveServiceDiscoveryProviderRegitered(FileReRoute reRoute, FileServiceDiscoveryProvider serviceDiscoveryProvider)
 | 
			
		||||
        private bool HaveServiceDiscoveryProviderRegistered(FileReRoute reRoute, FileServiceDiscoveryProvider serviceDiscoveryProvider)
 | 
			
		||||
        {
 | 
			
		||||
            if (string.IsNullOrEmpty(reRoute.ServiceName))
 | 
			
		||||
            {
 | 
			
		||||
@@ -75,7 +71,7 @@ namespace Ocelot.Configuration.Validator
 | 
			
		||||
            return _serviceDiscoveryFinderDelegates.Any();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private bool HaveServiceDiscoveryProviderRegitered(FileServiceDiscoveryProvider serviceDiscoveryProvider)
 | 
			
		||||
        private bool HaveServiceDiscoveryProviderRegistered(FileServiceDiscoveryProvider serviceDiscoveryProvider)
 | 
			
		||||
        {
 | 
			
		||||
            if(serviceDiscoveryProvider == null)
 | 
			
		||||
            {
 | 
			
		||||
@@ -87,12 +83,7 @@ namespace Ocelot.Configuration.Validator
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if(string.IsNullOrEmpty(serviceDiscoveryProvider.Type))
 | 
			
		||||
            {
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return _serviceDiscoveryFinderDelegates.Any();
 | 
			
		||||
            return string.IsNullOrEmpty(serviceDiscoveryProvider.Type) || _serviceDiscoveryFinderDelegates.Any();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public async Task<Response<ConfigurationValidationResult>> IsValid(FileConfiguration configuration)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,21 +1,14 @@
 | 
			
		||||
using FluentValidation;
 | 
			
		||||
using Ocelot.Configuration.File;
 | 
			
		||||
 | 
			
		||||
namespace Ocelot.Configuration.Validator
 | 
			
		||||
{
 | 
			
		||||
    using Requester;
 | 
			
		||||
    using FluentValidation;
 | 
			
		||||
    using File;
 | 
			
		||||
 | 
			
		||||
    public class FileGlobalConfigurationFluentValidator : AbstractValidator<FileGlobalConfiguration>
 | 
			
		||||
    {
 | 
			
		||||
        private readonly QosDelegatingHandlerDelegate _qosDelegatingHandlerDelegate;
 | 
			
		||||
 | 
			
		||||
        public FileGlobalConfigurationFluentValidator(QosDelegatingHandlerDelegate qosDelegatingHandlerDelegate)
 | 
			
		||||
        public FileGlobalConfigurationFluentValidator(FileQoSOptionsFluentValidator fileQoSOptionsFluentValidator)
 | 
			
		||||
        {
 | 
			
		||||
            _qosDelegatingHandlerDelegate = qosDelegatingHandlerDelegate;
 | 
			
		||||
            
 | 
			
		||||
           RuleFor(configuration => configuration.QoSOptions)
 | 
			
		||||
                .SetValidator(new FileQoSOptionsFluentValidator(_qosDelegatingHandlerDelegate));
 | 
			
		||||
            RuleFor(configuration => configuration.QoSOptions)
 | 
			
		||||
                .SetValidator(fileQoSOptionsFluentValidator);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,17 +1,18 @@
 | 
			
		||||
using FluentValidation;
 | 
			
		||||
using Ocelot.Configuration.File;
 | 
			
		||||
 | 
			
		||||
namespace Ocelot.Configuration.Validator
 | 
			
		||||
{
 | 
			
		||||
    using System;
 | 
			
		||||
    using FluentValidation;
 | 
			
		||||
    using File;
 | 
			
		||||
    using Microsoft.Extensions.DependencyInjection;
 | 
			
		||||
    using Requester;
 | 
			
		||||
 | 
			
		||||
    public class FileQoSOptionsFluentValidator : AbstractValidator<FileQoSOptions>
 | 
			
		||||
    {
 | 
			
		||||
        private readonly QosDelegatingHandlerDelegate _qosDelegatingHandlerDelegate;
 | 
			
		||||
 | 
			
		||||
        public FileQoSOptionsFluentValidator(QosDelegatingHandlerDelegate qosDelegatingHandlerDelegate)
 | 
			
		||||
        public FileQoSOptionsFluentValidator(IServiceProvider provider)
 | 
			
		||||
        {
 | 
			
		||||
            _qosDelegatingHandlerDelegate = qosDelegatingHandlerDelegate;
 | 
			
		||||
            _qosDelegatingHandlerDelegate = provider.GetService<QosDelegatingHandlerDelegate>();
 | 
			
		||||
 | 
			
		||||
            When(qosOptions => qosOptions.TimeoutValue > 0 && qosOptions.ExceptionsAllowedBeforeBreaking > 0, () => {
 | 
			
		||||
                RuleFor(qosOptions => qosOptions)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,13 +1,11 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using Ocelot.Errors;
 | 
			
		||||
 | 
			
		||||
namespace Ocelot.Configuration.Validator
 | 
			
		||||
namespace Ocelot.Configuration.Validator
 | 
			
		||||
{
 | 
			
		||||
    using Errors;
 | 
			
		||||
 | 
			
		||||
    public class FileValidationFailedError : Error
 | 
			
		||||
    {
 | 
			
		||||
        public FileValidationFailedError(string message) : base(message, OcelotErrorCode.FileValidationFailedError)
 | 
			
		||||
        public FileValidationFailedError(string message) 
 | 
			
		||||
            : base(message, OcelotErrorCode.FileValidationFailedError)
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,13 +1,15 @@
 | 
			
		||||
using FluentValidation;
 | 
			
		||||
using Ocelot.Configuration.File;
 | 
			
		||||
 | 
			
		||||
namespace Ocelot.Configuration.Validator
 | 
			
		||||
{
 | 
			
		||||
    using FluentValidation;
 | 
			
		||||
    using Ocelot.Configuration.File;
 | 
			
		||||
 | 
			
		||||
    public class HostAndPortValidator : AbstractValidator<FileHostAndPort>
 | 
			
		||||
    {
 | 
			
		||||
        public HostAndPortValidator()
 | 
			
		||||
        {
 | 
			
		||||
            RuleFor(r => r.Host).NotEmpty().WithMessage("When not using service discovery Host must be set on DownstreamHostAndPorts if you are not using ReRoute.Host or Ocelot cannot find your service!");
 | 
			
		||||
            RuleFor(r => r.Host)
 | 
			
		||||
                .NotEmpty()
 | 
			
		||||
                .WithMessage("When not using service discovery Host must be set on DownstreamHostAndPorts if you are not using ReRoute.Host or Ocelot cannot find your service!");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,9 @@
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using Ocelot.Configuration.File;
 | 
			
		||||
using Ocelot.Responses;
 | 
			
		||||
 | 
			
		||||
namespace Ocelot.Configuration.Validator
 | 
			
		||||
namespace Ocelot.Configuration.Validator
 | 
			
		||||
{
 | 
			
		||||
    using System.Threading.Tasks;
 | 
			
		||||
    using Ocelot.Configuration.File;
 | 
			
		||||
    using Ocelot.Responses;
 | 
			
		||||
 | 
			
		||||
    public interface IConfigurationValidator
 | 
			
		||||
    {
 | 
			
		||||
        Task<Response<ConfigurationValidationResult>> IsValid(FileConfiguration configuration);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,59 +1,77 @@
 | 
			
		||||
namespace Ocelot.Configuration.Validator
 | 
			
		||||
{
 | 
			
		||||
    using System;
 | 
			
		||||
    using FluentValidation;
 | 
			
		||||
    using Microsoft.AspNetCore.Authentication;
 | 
			
		||||
    using Ocelot.Configuration.File;
 | 
			
		||||
    using File;
 | 
			
		||||
    using System.Linq;
 | 
			
		||||
    using System.Text.RegularExpressions;
 | 
			
		||||
    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)
 | 
			
		||||
        public ReRouteFluentValidator(IAuthenticationSchemeProvider authenticationSchemeProvider, HostAndPortValidator hostAndPortValidator, FileQoSOptionsFluentValidator fileQoSOptionsFluentValidator)
 | 
			
		||||
        {
 | 
			
		||||
            _authenticationSchemeProvider = authenticationSchemeProvider;
 | 
			
		||||
            _qosDelegatingHandlerDelegate = qosDelegatingHandlerDelegate;
 | 
			
		||||
 | 
			
		||||
            RuleFor(reRoute => reRoute.QoSOptions)
 | 
			
		||||
                .SetValidator(new FileQoSOptionsFluentValidator(_qosDelegatingHandlerDelegate));
 | 
			
		||||
                .SetValidator(fileQoSOptionsFluentValidator);
 | 
			
		||||
 | 
			
		||||
            RuleFor(reRoute => reRoute.DownstreamPathTemplate)
 | 
			
		||||
                .Must(path => path.StartsWith("/"))
 | 
			
		||||
                .WithMessage("{PropertyName} {PropertyValue} doesnt start with forward slash");
 | 
			
		||||
                .NotEmpty()
 | 
			
		||||
                .WithMessage("{PropertyName} cannot be empty");
 | 
			
		||||
 | 
			
		||||
            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.");
 | 
			
		||||
                .NotEmpty()
 | 
			
		||||
                .WithMessage("{PropertyName} cannot be empty");
 | 
			
		||||
 | 
			
		||||
            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.");
 | 
			
		||||
            When(reRoute => !string.IsNullOrEmpty(reRoute.DownstreamPathTemplate), () =>
 | 
			
		||||
            {
 | 
			
		||||
                RuleFor(reRoute => reRoute.DownstreamPathTemplate)
 | 
			
		||||
                    .Must(path => path.StartsWith("/"))
 | 
			
		||||
                    .WithMessage("{PropertyName} {PropertyValue} doesnt start with forward slash");
 | 
			
		||||
 | 
			
		||||
            RuleFor(reRoute => reRoute.UpstreamPathTemplate)
 | 
			
		||||
                .Must(path => path.StartsWith("/"))
 | 
			
		||||
                .WithMessage("{PropertyName} {PropertyValue} doesnt start with forward slash");
 | 
			
		||||
                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.DownstreamPathTemplate)
 | 
			
		||||
                .Must(path => !path.Contains("https://") && !path.Contains("http://"))
 | 
			
		||||
                .WithMessage("{PropertyName} {PropertyValue} contains scheme");
 | 
			
		||||
                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");
 | 
			
		||||
            When(reRoute => !string.IsNullOrEmpty(reRoute.UpstreamPathTemplate), () =>
 | 
			
		||||
            {
 | 
			
		||||
                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.RateLimitOptions)
 | 
			
		||||
                .Must(IsValidPeriod)
 | 
			
		||||
                .WithMessage("RateLimitOptions.Period does not contains (s,m,h,d)");
 | 
			
		||||
                RuleFor(reRoute => reRoute.UpstreamPathTemplate)
 | 
			
		||||
                    .Must(path => path.StartsWith("/"))
 | 
			
		||||
                    .WithMessage("{PropertyName} {PropertyValue} doesnt start with forward slash");
 | 
			
		||||
 | 
			
		||||
                RuleFor(reRoute => reRoute.UpstreamPathTemplate)
 | 
			
		||||
                    .Must(path => !path.Contains("https://") && !path.Contains("http://"))
 | 
			
		||||
                    .WithMessage("{PropertyName} {PropertyValue} contains scheme");
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            When(reRoute => reRoute.RateLimitOptions.EnableRateLimiting, () =>
 | 
			
		||||
            {
 | 
			
		||||
                RuleFor(reRoute => reRoute.RateLimitOptions.Period)
 | 
			
		||||
                    .NotEmpty()
 | 
			
		||||
                    .WithMessage("RateLimitOptions.Period is empty");
 | 
			
		||||
 | 
			
		||||
                RuleFor(reRoute => reRoute.RateLimitOptions)
 | 
			
		||||
                    .Must(IsValidPeriod)
 | 
			
		||||
                    .WithMessage("RateLimitOptions.Period does not contain integer then s (second), m (minute), h (hour), d (day) e.g. 1m for 1 minute period");
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            RuleFor(reRoute => reRoute.AuthenticationOptions)
 | 
			
		||||
                .MustAsync(IsSupportedAuthenticationProviders)
 | 
			
		||||
                .WithMessage("{PropertyValue} is unsupported authentication provider");
 | 
			
		||||
                .WithMessage("{PropertyName} {PropertyValue} is unsupported authentication provider");
 | 
			
		||||
 | 
			
		||||
            When(reRoute => string.IsNullOrEmpty(reRoute.ServiceName), () => {
 | 
			
		||||
                RuleFor(r => r.DownstreamHostAndPorts).NotEmpty()
 | 
			
		||||
@@ -62,7 +80,7 @@
 | 
			
		||||
 | 
			
		||||
            When(reRoute => string.IsNullOrEmpty(reRoute.ServiceName), () => {
 | 
			
		||||
                RuleFor(reRoute => reRoute.DownstreamHostAndPorts)
 | 
			
		||||
                    .SetCollectionValidator(new HostAndPortValidator());
 | 
			
		||||
                    .SetCollectionValidator(hostAndPortValidator);
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -82,9 +100,22 @@
 | 
			
		||||
 | 
			
		||||
        private static bool IsValidPeriod(FileRateLimitRule rateLimitOptions)
 | 
			
		||||
        {
 | 
			
		||||
            string period = rateLimitOptions.Period;
 | 
			
		||||
            if (string.IsNullOrEmpty(rateLimitOptions.Period))
 | 
			
		||||
            {
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return !rateLimitOptions.EnableRateLimiting || period.Contains("s") || period.Contains("m") || period.Contains("h") || period.Contains("d");
 | 
			
		||||
            var period = rateLimitOptions.Period;
 | 
			
		||||
 | 
			
		||||
            var secondsRegEx = new Regex("^[0-9]+s");
 | 
			
		||||
            var minutesRegEx = new Regex("^[0-9]+m");
 | 
			
		||||
            var hoursRegEx = new Regex("^[0-9]+h");
 | 
			
		||||
            var daysRegEx = new Regex("^[0-9]+d");
 | 
			
		||||
 | 
			
		||||
            return secondsRegEx.Match(period).Success
 | 
			
		||||
                   || minutesRegEx.Match(period).Success
 | 
			
		||||
                   || hoursRegEx.Match(period).Success
 | 
			
		||||
                   || daysRegEx.Match(period).Success;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -55,6 +55,10 @@ namespace Ocelot.DependencyInjection
 | 
			
		||||
            Services.TryAddSingleton<IInternalConfigurationCreator, FileInternalConfigurationCreator>();
 | 
			
		||||
            Services.TryAddSingleton<IInternalConfigurationRepository, InMemoryInternalConfigurationRepository>();
 | 
			
		||||
            Services.TryAddSingleton<IConfigurationValidator, FileConfigurationFluentValidator>();
 | 
			
		||||
            Services.AddSingleton<HostAndPortValidator>();
 | 
			
		||||
            Services.AddSingleton<ReRouteFluentValidator>();
 | 
			
		||||
            Services.AddSingleton<FileGlobalConfigurationFluentValidator>();
 | 
			
		||||
            Services.AddSingleton<FileQoSOptionsFluentValidator>();
 | 
			
		||||
            Services.TryAddSingleton<IClaimsToThingCreator, ClaimsToThingCreator>();
 | 
			
		||||
            Services.TryAddSingleton<IAuthenticationOptionsCreator, AuthenticationOptionsCreator>();
 | 
			
		||||
            Services.TryAddSingleton<IUpstreamTemplatePatternCreator, UpstreamTemplatePatternCreator>();
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
namespace Ocelot.UnitTests.Configuration
 | 
			
		||||
namespace Ocelot.UnitTests.Configuration.Validation
 | 
			
		||||
{
 | 
			
		||||
    using System.Collections.Generic;
 | 
			
		||||
    using System.Security.Claims;
 | 
			
		||||
@@ -33,7 +33,8 @@
 | 
			
		||||
            _authProvider = new Mock<IAuthenticationSchemeProvider>();
 | 
			
		||||
            var provider = new ServiceCollection()
 | 
			
		||||
                .BuildServiceProvider();
 | 
			
		||||
            _configurationValidator = new FileConfigurationFluentValidator(_authProvider.Object, provider);
 | 
			
		||||
            // Todo - replace with mocks
 | 
			
		||||
            _configurationValidator = new FileConfigurationFluentValidator(provider, new ReRouteFluentValidator(_authProvider.Object, new HostAndPortValidator(), new FileQoSOptionsFluentValidator(provider)), new FileGlobalConfigurationFluentValidator(new FileQoSOptionsFluentValidator(provider)));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [Fact]
 | 
			
		||||
@@ -766,10 +767,11 @@
 | 
			
		||||
                .Then(x => x.ThenTheResultIsNotValid())
 | 
			
		||||
                .Then(x => x.ThenTheErrorIs<FileValidationFailedError>())
 | 
			
		||||
                .And(x => x.ThenTheErrorMessageAtPositionIs(0, "Downstream Path Template http://www.bbc.co.uk/api/products/{productId} doesnt start with forward slash"))
 | 
			
		||||
                .And(x => x.ThenTheErrorMessageAtPositionIs(1, "Upstream Path Template http://asdf.com contains double forward slash, Ocelot does not support this at the moment. Please raise an issue in GitHib if you need this feature."))
 | 
			
		||||
                .And(x => x.ThenTheErrorMessageAtPositionIs(2, "Downstream Path Template http://www.bbc.co.uk/api/products/{productId} contains double forward slash, Ocelot does not support this at the moment. Please raise an issue in GitHib if you need this feature."))
 | 
			
		||||
                .And(x => x.ThenTheErrorMessageAtPositionIs(3, "Upstream Path Template http://asdf.com doesnt start with forward slash"))
 | 
			
		||||
                .And(x => x.ThenTheErrorMessageAtPositionIs(4, "Downstream Path Template http://www.bbc.co.uk/api/products/{productId} contains scheme"))
 | 
			
		||||
                .And(x => x.ThenTheErrorMessageAtPositionIs(1, "Downstream Path Template http://www.bbc.co.uk/api/products/{productId} contains double forward slash, Ocelot does not support this at the moment. Please raise an issue in GitHib if you need this feature."))
 | 
			
		||||
                .And(x => x.ThenTheErrorMessageAtPositionIs(2, "Downstream Path Template http://www.bbc.co.uk/api/products/{productId} contains scheme"))
 | 
			
		||||
 | 
			
		||||
                .And(x => x.ThenTheErrorMessageAtPositionIs(3, "Upstream Path Template http://asdf.com contains double forward slash, Ocelot does not support this at the moment. Please raise an issue in GitHib if you need this feature."))
 | 
			
		||||
                .And(x => x.ThenTheErrorMessageAtPositionIs(4, "Upstream Path Template http://asdf.com doesnt start with forward slash"))
 | 
			
		||||
                .And(x => x.ThenTheErrorMessageAtPositionIs(5, "Upstream Path Template http://asdf.com contains scheme"))
 | 
			
		||||
                .BDDfy();
 | 
			
		||||
        }
 | 
			
		||||
@@ -947,7 +949,7 @@
 | 
			
		||||
            }))
 | 
			
		||||
                .When(x => x.WhenIValidateTheConfiguration())
 | 
			
		||||
                .Then(x => x.ThenTheResultIsNotValid())
 | 
			
		||||
                .And(x => x.ThenTheErrorMessageAtPositionIs(0, "AuthenticationProviderKey:Test,AllowedScopes:[] is unsupported authentication provider"))
 | 
			
		||||
                .And(x => x.ThenTheErrorMessageAtPositionIs(0, "Authentication Options AuthenticationProviderKey:Test,AllowedScopes:[] is unsupported authentication provider"))
 | 
			
		||||
                .BDDfy();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -1140,7 +1142,7 @@
 | 
			
		||||
            }))
 | 
			
		||||
                .When(x => x.WhenIValidateTheConfiguration())
 | 
			
		||||
                .Then(x => x.ThenTheResultIsNotValid())
 | 
			
		||||
                .And(x => x.ThenTheErrorMessageAtPositionIs(0, "RateLimitOptions.Period does not contains (s,m,h,d)"))
 | 
			
		||||
                .And(x => x.ThenTheErrorMessageAtPositionIs(0, "RateLimitOptions.Period does not contain integer then s (second), m (minute), h (hour), d (day) e.g. 1m for 1 minute period"))
 | 
			
		||||
                .BDDfy();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -1383,7 +1385,7 @@
 | 
			
		||||
            QosDelegatingHandlerDelegate del = (a,b) => new FakeDelegatingHandler();
 | 
			
		||||
            collection.AddSingleton<QosDelegatingHandlerDelegate>(del);
 | 
			
		||||
            var provider = collection.BuildServiceProvider();
 | 
			
		||||
            _configurationValidator = new FileConfigurationFluentValidator(_authProvider.Object, provider);
 | 
			
		||||
            _configurationValidator = new FileConfigurationFluentValidator(provider, new ReRouteFluentValidator(_authProvider.Object, new HostAndPortValidator(), new FileQoSOptionsFluentValidator(provider)), new FileGlobalConfigurationFluentValidator(new FileQoSOptionsFluentValidator(provider)));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void GivenAServiceDiscoveryHandler()
 | 
			
		||||
@@ -1392,7 +1394,7 @@
 | 
			
		||||
            ServiceDiscoveryFinderDelegate del = (a,b,c) => new FakeServiceDiscoveryProvider();
 | 
			
		||||
            collection.AddSingleton<ServiceDiscoveryFinderDelegate>(del);
 | 
			
		||||
            var provider = collection.BuildServiceProvider();
 | 
			
		||||
            _configurationValidator = new FileConfigurationFluentValidator(_authProvider.Object, provider);
 | 
			
		||||
            _configurationValidator = new FileConfigurationFluentValidator(provider, new ReRouteFluentValidator(_authProvider.Object, new HostAndPortValidator(), new FileQoSOptionsFluentValidator(provider)), new FileGlobalConfigurationFluentValidator(new FileQoSOptionsFluentValidator(provider)));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private class FakeServiceDiscoveryProvider : IServiceDiscoveryProvider
 | 
			
		||||
@@ -0,0 +1,106 @@
 | 
			
		||||
using System;
 | 
			
		||||
using FluentValidation.Results;
 | 
			
		||||
using Microsoft.Extensions.DependencyInjection;
 | 
			
		||||
using Moq;
 | 
			
		||||
using Ocelot.Configuration.File;
 | 
			
		||||
using Ocelot.Configuration.Validator;
 | 
			
		||||
using Ocelot.Requester;
 | 
			
		||||
using Shouldly;
 | 
			
		||||
using TestStack.BDDfy;
 | 
			
		||||
using Xunit;
 | 
			
		||||
 | 
			
		||||
namespace Ocelot.UnitTests.Configuration.Validation
 | 
			
		||||
{
 | 
			
		||||
    public class FileQoSOptionsFluentValidatorTests
 | 
			
		||||
    {
 | 
			
		||||
        private FileQoSOptionsFluentValidator _validator;
 | 
			
		||||
        private ServiceCollection _services;
 | 
			
		||||
        private ValidationResult _result;
 | 
			
		||||
        private FileQoSOptions _qosOptions;
 | 
			
		||||
 | 
			
		||||
        public FileQoSOptionsFluentValidatorTests()
 | 
			
		||||
        {
 | 
			
		||||
            _services = new ServiceCollection();
 | 
			
		||||
            var provider = _services.BuildServiceProvider();
 | 
			
		||||
            _validator = new FileQoSOptionsFluentValidator(provider);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [Fact]
 | 
			
		||||
        public void should_be_valid_as_nothing_set()
 | 
			
		||||
        {
 | 
			
		||||
            this.Given(_ => GivenThe(new FileQoSOptions()))
 | 
			
		||||
                .When(_ => WhenIValidate())
 | 
			
		||||
                .Then(_ => ThenTheResultIsValid())
 | 
			
		||||
                .BDDfy();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [Fact]
 | 
			
		||||
        public void should_be_valid_as_qos_delegate_set()
 | 
			
		||||
        {
 | 
			
		||||
            var qosOptions = new FileQoSOptions
 | 
			
		||||
            {
 | 
			
		||||
                TimeoutValue = 1,
 | 
			
		||||
                ExceptionsAllowedBeforeBreaking = 1
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            this.Given(_ => GivenThe(qosOptions))
 | 
			
		||||
                .And(_ => GivenAQosDelegate())
 | 
			
		||||
                .When(_ => WhenIValidate())
 | 
			
		||||
                .Then(_ => ThenTheResultIsValid())
 | 
			
		||||
                .BDDfy();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [Fact]
 | 
			
		||||
        public void should_be_invalid_as_no_qos_delegate()
 | 
			
		||||
        {
 | 
			
		||||
            var qosOptions = new FileQoSOptions
 | 
			
		||||
            {
 | 
			
		||||
                TimeoutValue = 1,
 | 
			
		||||
                ExceptionsAllowedBeforeBreaking = 1
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            this.Given(_ => GivenThe(qosOptions))
 | 
			
		||||
                .When(_ => WhenIValidate())
 | 
			
		||||
                .Then(_ => ThenTheResultIsInValid())
 | 
			
		||||
                .And(_ => ThenTheErrorIs())
 | 
			
		||||
                .BDDfy();
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void ThenTheErrorIs()
 | 
			
		||||
        {
 | 
			
		||||
            _result.Errors[0].ErrorMessage.ShouldBe("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 void ThenTheResultIsInValid()
 | 
			
		||||
        {
 | 
			
		||||
            _result.IsValid.ShouldBeFalse();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void GivenAQosDelegate()
 | 
			
		||||
        {
 | 
			
		||||
            QosDelegatingHandlerDelegate fake = (a, b) =>
 | 
			
		||||
            {
 | 
			
		||||
                return null;
 | 
			
		||||
            };
 | 
			
		||||
            _services.AddSingleton<QosDelegatingHandlerDelegate>(fake);
 | 
			
		||||
            var provider = _services.BuildServiceProvider();
 | 
			
		||||
            _validator = new FileQoSOptionsFluentValidator(provider);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void GivenThe(FileQoSOptions qosOptions)
 | 
			
		||||
        {
 | 
			
		||||
            _qosOptions = qosOptions;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void WhenIValidate()
 | 
			
		||||
        {
 | 
			
		||||
            _result = _validator.Validate(_qosOptions);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void ThenTheResultIsValid()
 | 
			
		||||
        {
 | 
			
		||||
            _result.IsValid.ShouldBeTrue();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,78 @@
 | 
			
		||||
using System;
 | 
			
		||||
using FluentValidation.Results;
 | 
			
		||||
using Ocelot.Configuration.File;
 | 
			
		||||
using Ocelot.Configuration.Validator;
 | 
			
		||||
using Shouldly;
 | 
			
		||||
using TestStack.BDDfy;
 | 
			
		||||
using Xunit;
 | 
			
		||||
 | 
			
		||||
namespace Ocelot.UnitTests.Configuration.Validation
 | 
			
		||||
{
 | 
			
		||||
    public class HostAndPortValidatorTests
 | 
			
		||||
    {
 | 
			
		||||
        private HostAndPortValidator _validator;
 | 
			
		||||
        private ValidationResult _result;
 | 
			
		||||
        private FileHostAndPort _hostAndPort;
 | 
			
		||||
 | 
			
		||||
        public HostAndPortValidatorTests()
 | 
			
		||||
        {
 | 
			
		||||
            _validator = new HostAndPortValidator();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [Theory]
 | 
			
		||||
        [InlineData(null)]
 | 
			
		||||
        [InlineData("")]
 | 
			
		||||
        public void should_be_invalid_because_host_empty(string host)
 | 
			
		||||
        {
 | 
			
		||||
            var fileHostAndPort = new FileHostAndPort
 | 
			
		||||
            {
 | 
			
		||||
                Host = host
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            this.Given(_ => GivenThe(fileHostAndPort))
 | 
			
		||||
               .When(_ => WhenIValidate())
 | 
			
		||||
               .Then(_ => ThenTheResultIsInValid())
 | 
			
		||||
               .And(_ => ThenTheErorrIs())
 | 
			
		||||
               .BDDfy();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [Fact]
 | 
			
		||||
        public void should_be_valid_because_host_set()
 | 
			
		||||
        {
 | 
			
		||||
            var fileHostAndPort = new FileHostAndPort
 | 
			
		||||
            {
 | 
			
		||||
                Host = "test"
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            this.Given(_ => GivenThe(fileHostAndPort))
 | 
			
		||||
                .When(_ => WhenIValidate())
 | 
			
		||||
                .Then(_ => ThenTheResultIsValid())
 | 
			
		||||
                .BDDfy();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void GivenThe(FileHostAndPort hostAndPort)
 | 
			
		||||
        {
 | 
			
		||||
            _hostAndPort = hostAndPort;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void WhenIValidate()
 | 
			
		||||
        {
 | 
			
		||||
            _result = _validator.Validate(_hostAndPort);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void ThenTheResultIsValid()
 | 
			
		||||
        {
 | 
			
		||||
            _result.IsValid.ShouldBeTrue();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void ThenTheErorrIs()
 | 
			
		||||
        {
 | 
			
		||||
            _result.Errors[0].ErrorMessage.ShouldBe("When not using service discovery Host must be set on DownstreamHostAndPorts if you are not using ReRoute.Host or Ocelot cannot find your service!");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void ThenTheResultIsInValid()
 | 
			
		||||
        {
 | 
			
		||||
            _result.IsValid.ShouldBeFalse();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,368 @@
 | 
			
		||||
namespace Ocelot.UnitTests.Configuration.Validation
 | 
			
		||||
{
 | 
			
		||||
    using System;
 | 
			
		||||
    using System.Collections.Generic;
 | 
			
		||||
    using System.Threading.Tasks;
 | 
			
		||||
    using FluentValidation.Results;
 | 
			
		||||
    using Microsoft.AspNetCore.Authentication;
 | 
			
		||||
    using Microsoft.AspNetCore.Http;
 | 
			
		||||
    using Moq;
 | 
			
		||||
    using Ocelot.Configuration.File;
 | 
			
		||||
    using Ocelot.Configuration.Validator;
 | 
			
		||||
    using Ocelot.Requester;
 | 
			
		||||
    using Shouldly;
 | 
			
		||||
    using TestStack.BDDfy;
 | 
			
		||||
    using Xunit;
 | 
			
		||||
 | 
			
		||||
    public class ReRouteFluentValidatorTests
 | 
			
		||||
    {
 | 
			
		||||
        private readonly ReRouteFluentValidator _validator;
 | 
			
		||||
        private readonly Mock<IAuthenticationSchemeProvider> _authProvider;
 | 
			
		||||
        private QosDelegatingHandlerDelegate _qosDelegatingHandler;
 | 
			
		||||
        private Mock<IServiceProvider> _serviceProvider;
 | 
			
		||||
        private FileReRoute _reRoute;
 | 
			
		||||
        private ValidationResult _result;
 | 
			
		||||
 | 
			
		||||
        public ReRouteFluentValidatorTests()
 | 
			
		||||
        {
 | 
			
		||||
            _authProvider = new Mock<IAuthenticationSchemeProvider>();
 | 
			
		||||
            _serviceProvider = new Mock<IServiceProvider>();
 | 
			
		||||
            // Todo - replace with mocks
 | 
			
		||||
            _validator = new ReRouteFluentValidator(_authProvider.Object, new HostAndPortValidator(), new FileQoSOptionsFluentValidator(_serviceProvider.Object));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [Fact]
 | 
			
		||||
        public void downstream_path_template_should_not_be_empty()
 | 
			
		||||
        {
 | 
			
		||||
            var fileReRoute = new FileReRoute();
 | 
			
		||||
 | 
			
		||||
            this.Given(_ => GivenThe(fileReRoute))
 | 
			
		||||
                .When(_ => WhenIValidate())
 | 
			
		||||
                .Then(_ => ThenTheResultIsInvalid())
 | 
			
		||||
                .And(_ => ThenTheErrorsContains("Downstream Path Template cannot be empty"))
 | 
			
		||||
                .BDDfy();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [Fact]
 | 
			
		||||
        public void upstream_path_template_should_not_be_empty()
 | 
			
		||||
        {
 | 
			
		||||
            var fileReRoute = new FileReRoute
 | 
			
		||||
            {
 | 
			
		||||
                DownstreamPathTemplate = "test"
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            this.Given(_ => GivenThe(fileReRoute))
 | 
			
		||||
                .When(_ => WhenIValidate())
 | 
			
		||||
                .Then(_ => ThenTheResultIsInvalid())
 | 
			
		||||
                .And(_ => ThenTheErrorsContains("Upstream Path Template cannot be empty"))
 | 
			
		||||
                .BDDfy();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [Fact]
 | 
			
		||||
        public void downstream_path_template_should_start_with_forward_slash()
 | 
			
		||||
        {
 | 
			
		||||
            var fileReRoute = new FileReRoute
 | 
			
		||||
            {
 | 
			
		||||
                DownstreamPathTemplate = "test"
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            this.Given(_ => GivenThe(fileReRoute))
 | 
			
		||||
                .When(_ => WhenIValidate())
 | 
			
		||||
                .Then(_ => ThenTheResultIsInvalid())
 | 
			
		||||
                .And(_ => ThenTheErrorsContains("Downstream Path Template test doesnt start with forward slash"))
 | 
			
		||||
                .BDDfy();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [Fact]
 | 
			
		||||
        public void downstream_path_template_should_not_contain_double_forward_slash()
 | 
			
		||||
        {
 | 
			
		||||
            var fileReRoute = new FileReRoute
 | 
			
		||||
            {
 | 
			
		||||
                DownstreamPathTemplate = "//test"
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            this.Given(_ => GivenThe(fileReRoute))
 | 
			
		||||
                .When(_ => WhenIValidate())
 | 
			
		||||
                .Then(_ => ThenTheResultIsInvalid())
 | 
			
		||||
                .And(_ => ThenTheErrorsContains("Downstream Path Template //test contains double forward slash, Ocelot does not support this at the moment. Please raise an issue in GitHib if you need this feature."))
 | 
			
		||||
                .BDDfy();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [Theory]
 | 
			
		||||
        [InlineData("https://test")]
 | 
			
		||||
        [InlineData("http://test")]
 | 
			
		||||
        [InlineData("/test/http://")]
 | 
			
		||||
        [InlineData("/test/https://")]
 | 
			
		||||
        public void downstream_path_template_should_not_contain_scheme(string downstreamPathTemplate)
 | 
			
		||||
        {
 | 
			
		||||
            var fileReRoute = new FileReRoute
 | 
			
		||||
            {
 | 
			
		||||
                DownstreamPathTemplate = downstreamPathTemplate
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            this.Given(_ => GivenThe(fileReRoute))
 | 
			
		||||
                .When(_ => WhenIValidate())
 | 
			
		||||
                .Then(_ => ThenTheResultIsInvalid())
 | 
			
		||||
                .And(_ => ThenTheErrorsContains($"Downstream Path Template {downstreamPathTemplate} contains double forward slash, Ocelot does not support this at the moment. Please raise an issue in GitHib if you need this feature."))
 | 
			
		||||
                .BDDfy();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [Fact]
 | 
			
		||||
        public void upstream_path_template_should_start_with_forward_slash()
 | 
			
		||||
        {
 | 
			
		||||
            var fileReRoute = new FileReRoute
 | 
			
		||||
            {
 | 
			
		||||
                DownstreamPathTemplate = "/test",
 | 
			
		||||
                UpstreamPathTemplate = "test"
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            this.Given(_ => GivenThe(fileReRoute))
 | 
			
		||||
                .When(_ => WhenIValidate())
 | 
			
		||||
                .Then(_ => ThenTheResultIsInvalid())
 | 
			
		||||
                .And(_ => ThenTheErrorsContains("Upstream Path Template test doesnt start with forward slash"))
 | 
			
		||||
                .BDDfy();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [Fact]
 | 
			
		||||
        public void upstream_path_template_should_not_contain_double_forward_slash()
 | 
			
		||||
        {
 | 
			
		||||
            var fileReRoute = new FileReRoute
 | 
			
		||||
            {
 | 
			
		||||
                DownstreamPathTemplate = "/test",
 | 
			
		||||
                UpstreamPathTemplate = "//test"
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            this.Given(_ => GivenThe(fileReRoute))
 | 
			
		||||
                .When(_ => WhenIValidate())
 | 
			
		||||
                .Then(_ => ThenTheResultIsInvalid())
 | 
			
		||||
                .And(_ => ThenTheErrorsContains("Upstream Path Template //test contains double forward slash, Ocelot does not support this at the moment. Please raise an issue in GitHib if you need this feature."))
 | 
			
		||||
                .BDDfy();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [Theory]
 | 
			
		||||
        [InlineData("https://test")]
 | 
			
		||||
        [InlineData("http://test")]
 | 
			
		||||
        [InlineData("/test/http://")]
 | 
			
		||||
        [InlineData("/test/https://")]
 | 
			
		||||
        public void upstream_path_template_should_not_contain_scheme(string upstreamPathTemplate)
 | 
			
		||||
        {
 | 
			
		||||
            var fileReRoute = new FileReRoute
 | 
			
		||||
            {
 | 
			
		||||
                DownstreamPathTemplate = "/test",
 | 
			
		||||
                UpstreamPathTemplate = upstreamPathTemplate
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            this.Given(_ => GivenThe(fileReRoute))
 | 
			
		||||
                .When(_ => WhenIValidate())
 | 
			
		||||
                .Then(_ => ThenTheResultIsInvalid())
 | 
			
		||||
                .And(_ => ThenTheErrorsContains($"Upstream Path Template {upstreamPathTemplate} contains double forward slash, Ocelot does not support this at the moment. Please raise an issue in GitHib if you need this feature."))
 | 
			
		||||
                .BDDfy();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [Fact]
 | 
			
		||||
        public void should_not_be_valid_if_enable_rate_limiting_true_and_period_is_empty()
 | 
			
		||||
        {
 | 
			
		||||
            var fileReRoute = new FileReRoute
 | 
			
		||||
            {
 | 
			
		||||
                DownstreamPathTemplate = "/test",
 | 
			
		||||
                UpstreamPathTemplate = "/test",
 | 
			
		||||
                RateLimitOptions = new FileRateLimitRule
 | 
			
		||||
                {
 | 
			
		||||
                    EnableRateLimiting = true
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            this.Given(_ => GivenThe(fileReRoute))
 | 
			
		||||
                .When(_ => WhenIValidate())
 | 
			
		||||
                .Then(_ => ThenTheResultIsInvalid())
 | 
			
		||||
                .And(_ => ThenTheErrorsContains("RateLimitOptions.Period is empty"))
 | 
			
		||||
                .BDDfy();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [Fact]
 | 
			
		||||
        public void should_not_be_valid_if_enable_rate_limiting_true_and_period_has_value()
 | 
			
		||||
        {
 | 
			
		||||
            var fileReRoute = new FileReRoute
 | 
			
		||||
            {
 | 
			
		||||
                DownstreamPathTemplate = "/test",
 | 
			
		||||
                UpstreamPathTemplate = "/test",
 | 
			
		||||
                RateLimitOptions = new FileRateLimitRule
 | 
			
		||||
                {
 | 
			
		||||
                    EnableRateLimiting = true,
 | 
			
		||||
                    Period = "test"
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            this.Given(_ => GivenThe(fileReRoute))
 | 
			
		||||
                .When(_ => WhenIValidate())
 | 
			
		||||
                .Then(_ => ThenTheResultIsInvalid())
 | 
			
		||||
                .And(_ => ThenTheErrorsContains("RateLimitOptions.Period does not contain integer then s (second), m (minute), h (hour), d (day) e.g. 1m for 1 minute period"))
 | 
			
		||||
                .BDDfy();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [Fact]
 | 
			
		||||
        public void should_not_be_valid_if_specified_authentication_provider_isnt_registered()
 | 
			
		||||
        {
 | 
			
		||||
            var fileReRoute = new FileReRoute
 | 
			
		||||
            {
 | 
			
		||||
                DownstreamPathTemplate = "/test",
 | 
			
		||||
                UpstreamPathTemplate = "/test",
 | 
			
		||||
                AuthenticationOptions = new FileAuthenticationOptions
 | 
			
		||||
                {
 | 
			
		||||
                    AuthenticationProviderKey = "JwtLads"
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            this.Given(_ => GivenThe(fileReRoute))
 | 
			
		||||
                .When(_ => WhenIValidate())
 | 
			
		||||
                .Then(_ => ThenTheResultIsInvalid())
 | 
			
		||||
                .And(_ => ThenTheErrorsContains($"Authentication Options AuthenticationProviderKey:JwtLads,AllowedScopes:[] is unsupported authentication provider"))
 | 
			
		||||
                .BDDfy();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [Fact]
 | 
			
		||||
        public void should_not_be_valid_if_not_using_service_discovery_and_no_host_and_ports()
 | 
			
		||||
        {
 | 
			
		||||
            var fileReRoute = new FileReRoute
 | 
			
		||||
            {
 | 
			
		||||
                DownstreamPathTemplate = "/test",
 | 
			
		||||
                UpstreamPathTemplate = "/test",
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            this.Given(_ => GivenThe(fileReRoute))
 | 
			
		||||
                .When(_ => WhenIValidate())
 | 
			
		||||
                .Then(_ => ThenTheResultIsInvalid())
 | 
			
		||||
                .And(_ => ThenTheErrorsContains("When not using service discovery DownstreamHostAndPorts must be set and not empty or Ocelot cannot find your service!"))
 | 
			
		||||
                .BDDfy();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [Fact]
 | 
			
		||||
        public void should_be_valid_if_using_service_discovery_and_no_host_and_ports()
 | 
			
		||||
        {
 | 
			
		||||
            var fileReRoute = new FileReRoute
 | 
			
		||||
            {
 | 
			
		||||
                DownstreamPathTemplate = "/test",
 | 
			
		||||
                UpstreamPathTemplate = "/test",
 | 
			
		||||
                ServiceName = "Lads"
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            this.Given(_ => GivenThe(fileReRoute))
 | 
			
		||||
                .When(_ => WhenIValidate())
 | 
			
		||||
                .Then(_ => ThenTheResultIsValid())
 | 
			
		||||
                .BDDfy();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [Fact]
 | 
			
		||||
        public void should_be_valid_re_route_using_host_and_port_and_paths()
 | 
			
		||||
        {
 | 
			
		||||
            var fileReRoute = new FileReRoute
 | 
			
		||||
            {
 | 
			
		||||
                DownstreamPathTemplate = "/test",
 | 
			
		||||
                UpstreamPathTemplate = "/test",
 | 
			
		||||
                DownstreamHostAndPorts = new List<FileHostAndPort>
 | 
			
		||||
                {
 | 
			
		||||
                    new FileHostAndPort
 | 
			
		||||
                    {
 | 
			
		||||
                        Host = "localhost",
 | 
			
		||||
                        Port = 5000
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            this.Given(_ => GivenThe(fileReRoute))
 | 
			
		||||
                .When(_ => WhenIValidate())
 | 
			
		||||
                .Then(_ => ThenTheResultIsValid())
 | 
			
		||||
                .BDDfy();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [Fact]
 | 
			
		||||
        public void should_be_valid_if_specified_authentication_provider_is_registered()
 | 
			
		||||
        {
 | 
			
		||||
            const string key = "JwtLads";
 | 
			
		||||
 | 
			
		||||
            var fileReRoute = new FileReRoute
 | 
			
		||||
            {
 | 
			
		||||
                DownstreamPathTemplate = "/test",
 | 
			
		||||
                UpstreamPathTemplate = "/test",
 | 
			
		||||
                AuthenticationOptions = new FileAuthenticationOptions
 | 
			
		||||
                {
 | 
			
		||||
                    AuthenticationProviderKey = key
 | 
			
		||||
                },
 | 
			
		||||
                DownstreamHostAndPorts = new List<FileHostAndPort>
 | 
			
		||||
                {
 | 
			
		||||
                    new FileHostAndPort
 | 
			
		||||
                    {
 | 
			
		||||
                        Host = "localhost",
 | 
			
		||||
                        Port = 5000
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            this.Given(_ => GivenThe(fileReRoute))
 | 
			
		||||
                .And(_ => GivenAnAuthProvider(key))
 | 
			
		||||
                .When(_ => WhenIValidate())
 | 
			
		||||
                .Then(_ => ThenTheResultIsValid())
 | 
			
		||||
                .BDDfy();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void GivenAnAuthProvider(string key)
 | 
			
		||||
        {
 | 
			
		||||
            var schemes = new List<AuthenticationScheme>
 | 
			
		||||
            {
 | 
			
		||||
                new AuthenticationScheme(key, key, typeof(FakeAutheHandler))
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            _authProvider
 | 
			
		||||
                .Setup(x => x.GetAllSchemesAsync())
 | 
			
		||||
                .ReturnsAsync(schemes);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void ThenTheResultIsValid()
 | 
			
		||||
        {
 | 
			
		||||
            _result.IsValid.ShouldBeTrue();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void GivenThe(FileReRoute reRoute)
 | 
			
		||||
        {
 | 
			
		||||
            _reRoute = reRoute;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void WhenIValidate()
 | 
			
		||||
        {
 | 
			
		||||
            _result = _validator.Validate(_reRoute);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void ThenTheResultIsInvalid()
 | 
			
		||||
        {
 | 
			
		||||
            _result.IsValid.ShouldBeFalse();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void ThenTheErrorsContains(string expected)
 | 
			
		||||
        {
 | 
			
		||||
            _result.Errors.ShouldContain(x => x.ErrorMessage == expected);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        class FakeAutheHandler : IAuthenticationHandler
 | 
			
		||||
        {
 | 
			
		||||
            public Task InitializeAsync(AuthenticationScheme scheme, HttpContext context)
 | 
			
		||||
            {
 | 
			
		||||
                throw new System.NotImplementedException();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            public Task<AuthenticateResult> AuthenticateAsync()
 | 
			
		||||
            {
 | 
			
		||||
                throw new System.NotImplementedException();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            public Task ChallengeAsync(AuthenticationProperties properties)
 | 
			
		||||
            {
 | 
			
		||||
                throw new System.NotImplementedException();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            public Task ForbidAsync(AuthenticationProperties properties)
 | 
			
		||||
            {
 | 
			
		||||
                throw new System.NotImplementedException();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user