Added tests for identity server reference tokens, general refactoring and come config validation

This commit is contained in:
TomPallister 2016-10-16 20:28:23 +01:00
parent 7289cd803b
commit ce84ad4fc2
26 changed files with 565 additions and 150 deletions

View File

@ -31,6 +31,19 @@ Priorities
- Rate Limiting - Rate Limiting
- Then a big list of cool things... - Then a big list of cool things...
## How to use # How to use
TBC.... # Configuration
TBC really but example configuration for a route below.
ReRoutes:
- DownstreamTemplate: http://localhost:51876/
UpstreamTemplate: /
UpstreamHttpMethod: Post
AuthenticationOptions:
Provider: IdentityServer.AccessToken
ProviderRootUrl: http://localhost:51888
ScopeName: api
AdditionalScopes: []
ScopeSecret: secret

View File

@ -1,6 +1,8 @@
using IdentityServer4.AccessTokenValidation;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Ocelot.Library.Infrastructure.Responses; using Ocelot.Library.Infrastructure.Responses;
using AuthenticationOptions = Ocelot.Library.Infrastructure.Configuration.AuthenticationOptions;
namespace Ocelot.Library.Infrastructure.Authentication namespace Ocelot.Library.Infrastructure.Authentication
{ {
@ -9,17 +11,18 @@ namespace Ocelot.Library.Infrastructure.Authentication
/// </summary> /// </summary>
public class AuthenticationHandlerCreator : IAuthenticationHandlerCreator public class AuthenticationHandlerCreator : IAuthenticationHandlerCreator
{ {
public Response<RequestDelegate> CreateIdentityServerAuthenticationHandler(IApplicationBuilder app) public Response<RequestDelegate> CreateIdentityServerAuthenticationHandler(IApplicationBuilder app, AuthenticationOptions authOptions)
{ {
var builder = app.New(); var builder = app.New();
builder.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions builder.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
{ {
//todo sort these options out Authority = authOptions.ProviderRootUrl,
Authority = "http://localhost:51888", ScopeName = authOptions.ScopeName,
ScopeName = "api", RequireHttpsMetadata = authOptions.RequireHttps,
AdditionalScopes = authOptions.AdditionalScopes,
RequireHttpsMetadata = false SupportedTokens = SupportedTokens.Both,
ScopeSecret = authOptions.ScopeSecret
}); });
builder.UseMvc(); builder.UseMvc();

View File

@ -2,6 +2,7 @@ using System.Collections.Generic;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Ocelot.Library.Infrastructure.Errors; using Ocelot.Library.Infrastructure.Errors;
using Ocelot.Library.Infrastructure.Responses; using Ocelot.Library.Infrastructure.Responses;
using AuthenticationOptions = Ocelot.Library.Infrastructure.Configuration.AuthenticationOptions;
namespace Ocelot.Library.Infrastructure.Authentication namespace Ocelot.Library.Infrastructure.Authentication
{ {
@ -14,18 +15,18 @@ namespace Ocelot.Library.Infrastructure.Authentication
_creator = creator; _creator = creator;
} }
public Response<AuthenticationHandler> Get(string provider, IApplicationBuilder app) public Response<AuthenticationHandler> Get(IApplicationBuilder app, AuthenticationOptions authOptions)
{ {
var handler = _creator.CreateIdentityServerAuthenticationHandler(app); var handler = _creator.CreateIdentityServerAuthenticationHandler(app, authOptions);
if (!handler.IsError) if (!handler.IsError)
{ {
return new OkResponse<AuthenticationHandler>(new AuthenticationHandler(provider, handler.Data)); return new OkResponse<AuthenticationHandler>(new AuthenticationHandler(authOptions.Provider, handler.Data));
} }
return new ErrorResponse<AuthenticationHandler>(new List<Error> return new ErrorResponse<AuthenticationHandler>(new List<Error>
{ {
new UnableToCreateAuthenticationHandlerError($"Unable to create authentication handler for {provider}") new UnableToCreateAuthenticationHandlerError($"Unable to create authentication handler for {authOptions.Provider}")
}); });
} }
} }

View File

@ -1,11 +1,12 @@
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Ocelot.Library.Infrastructure.Responses; using Ocelot.Library.Infrastructure.Responses;
using AuthenticationOptions = Ocelot.Library.Infrastructure.Configuration.AuthenticationOptions;
namespace Ocelot.Library.Infrastructure.Authentication namespace Ocelot.Library.Infrastructure.Authentication
{ {
public interface IAuthenticationHandlerCreator public interface IAuthenticationHandlerCreator
{ {
Response<RequestDelegate> CreateIdentityServerAuthenticationHandler(IApplicationBuilder app); Response<RequestDelegate> CreateIdentityServerAuthenticationHandler(IApplicationBuilder app, AuthenticationOptions authOptions);
} }
} }

View File

@ -1,10 +1,11 @@
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Ocelot.Library.Infrastructure.Responses; using Ocelot.Library.Infrastructure.Responses;
using AuthenticationOptions = Ocelot.Library.Infrastructure.Configuration.AuthenticationOptions;
namespace Ocelot.Library.Infrastructure.Authentication namespace Ocelot.Library.Infrastructure.Authentication
{ {
public interface IAuthenticationHandlerFactory public interface IAuthenticationHandlerFactory
{ {
Response<AuthenticationHandler> Get(string provider, IApplicationBuilder app); Response<AuthenticationHandler> Get(IApplicationBuilder app, AuthenticationOptions authOptions);
} }
} }

View File

@ -0,0 +1,7 @@
namespace Ocelot.Library.Infrastructure.Authentication
{
public enum SupportAuthenticationProviders
{
IdentityServer
}
}

View File

@ -1,4 +1,6 @@
namespace Ocelot.Library.Infrastructure.Builder using System.Collections.Generic;
namespace Ocelot.Library.Infrastructure.Builder
{ {
using Configuration; using Configuration;
@ -10,38 +12,84 @@
private string _upstreamHttpMethod; private string _upstreamHttpMethod;
private bool _isAuthenticated; private bool _isAuthenticated;
private string _authenticationProvider; private string _authenticationProvider;
private string _authenticationProviderUrl;
private string _scopeName;
private List<string> _additionalScopes;
private bool _requireHttps;
private string _scopeSecret;
public void WithDownstreamTemplate(string input) public ReRouteBuilder()
{
_additionalScopes = new List<string>();
}
public ReRouteBuilder WithDownstreamTemplate(string input)
{ {
_downstreamTemplate = input; _downstreamTemplate = input;
return this;
} }
public void WithUpstreamTemplate(string input) public ReRouteBuilder WithUpstreamTemplate(string input)
{ {
_upstreamTemplate = input; _upstreamTemplate = input;
return this;
} }
public void WithUpstreamTemplatePattern(string input) public ReRouteBuilder WithUpstreamTemplatePattern(string input)
{ {
_upstreamTemplatePattern = input; _upstreamTemplatePattern = input;
return this;
} }
public void WithUpstreamHttpMethod(string input) public ReRouteBuilder WithUpstreamHttpMethod(string input)
{ {
_upstreamHttpMethod = input; _upstreamHttpMethod = input;
return this;
} }
public void WithIsAuthenticated(bool input) public ReRouteBuilder WithIsAuthenticated(bool input)
{ {
_isAuthenticated = input; _isAuthenticated = input;
return this;
} }
public void WithAuthenticationProvider(string input) public ReRouteBuilder WithAuthenticationProvider(string input)
{ {
_authenticationProvider = input; _authenticationProvider = input;
return this;
}
public ReRouteBuilder WithAuthenticationProviderUrl(string input)
{
_authenticationProviderUrl = input;
return this;
}
public ReRouteBuilder WithAuthenticationProviderScopeName(string input)
{
_scopeName = input;
return this;
}
public ReRouteBuilder WithAuthenticationProviderAdditionalScopes(List<string> input)
{
_additionalScopes = input;
return this;
}
public ReRouteBuilder WithRequireHttps(bool input)
{
_requireHttps = input;
return this;
}
public ReRouteBuilder WithScopeSecret(string input)
{
_scopeSecret = input;
return this;
} }
public ReRoute Build() public ReRoute Build()
{ {
return new ReRoute(_downstreamTemplate, _upstreamTemplate, _upstreamHttpMethod, _upstreamTemplatePattern, _isAuthenticated, _authenticationProvider); return new ReRoute(_downstreamTemplate, _upstreamTemplate, _upstreamHttpMethod, _upstreamTemplatePattern, _isAuthenticated, new AuthenticationOptions(_authenticationProvider, _authenticationProviderUrl, _scopeName, _requireHttps, _additionalScopes, _scopeSecret));
} }
} }
} }

View File

@ -0,0 +1,25 @@
using System.Collections.Generic;
namespace Ocelot.Library.Infrastructure.Configuration
{
public class AuthenticationOptions
{
public AuthenticationOptions(string provider, string providerRootUrl, string scopeName, bool requireHttps, List<string> additionalScopes, string scopeSecret)
{
Provider = provider;
ProviderRootUrl = providerRootUrl;
ScopeName = scopeName;
RequireHttps = requireHttps;
AdditionalScopes = additionalScopes;
ScopeSecret = scopeSecret;
}
public string Provider { get; private set; }
public string ProviderRootUrl { get; private set; }
public string ScopeName { get; private set; }
public string ScopeSecret { get; private set; }
public bool RequireHttps { get; private set; }
public List<string> AdditionalScopes { get; private set; }
}
}

View File

@ -7,22 +7,40 @@ namespace Ocelot.Library.Infrastructure.Configuration
public class OcelotConfiguration : IOcelotConfiguration public class OcelotConfiguration : IOcelotConfiguration
{ {
private readonly IOptions<YamlConfiguration> _options; private readonly IOptions<YamlConfiguration> _options;
private readonly IConfigurationValidator _configurationValidator;
private readonly List<ReRoute> _reRoutes; private readonly List<ReRoute> _reRoutes;
private const string RegExMatchEverything = ".*"; private const string RegExMatchEverything = ".*";
private const string RegExMatchEndString = "$"; private const string RegExMatchEndString = "$";
public OcelotConfiguration(IOptions<YamlConfiguration> options) public OcelotConfiguration(IOptions<YamlConfiguration> options, IConfigurationValidator configurationValidator)
{ {
_options = options; _options = options;
_configurationValidator = configurationValidator;
_reRoutes = new List<ReRoute>(); _reRoutes = new List<ReRoute>();
SetReRoutes(); SetUpConfiguration();
} }
private void SetReRoutes() /// <summary>
/// This method is meant to be tempoary to convert a yaml config to an ocelot config...probably wont keep this but we will see
/// will need a refactor at some point as its crap
/// </summary>
private void SetUpConfiguration()
{ {
foreach(var reRoute in _options.Value.ReRoutes) var response = _configurationValidator.IsValid(_options.Value);
if (!response.IsError && !response.Data.IsError)
{
foreach (var reRoute in _options.Value.ReRoutes)
{
SetUpReRoute(reRoute);
}
}
}
private void SetUpReRoute(YamlReRoute reRoute)
{ {
var upstreamTemplate = reRoute.UpstreamTemplate; var upstreamTemplate = reRoute.UpstreamTemplate;
var placeholders = new List<string>(); var placeholders = new List<string>();
for (int i = 0; i < upstreamTemplate.Length; i++) for (int i = 0; i < upstreamTemplate.Length; i++)
@ -43,13 +61,27 @@ namespace Ocelot.Library.Infrastructure.Configuration
upstreamTemplate = $"{upstreamTemplate}{RegExMatchEndString}"; upstreamTemplate = $"{upstreamTemplate}{RegExMatchEndString}";
var isAuthenticated = !string.IsNullOrEmpty(reRoute.Authentication); var isAuthenticated = !string.IsNullOrEmpty(reRoute.AuthenticationOptions?.Provider);
_reRoutes.Add(new ReRoute(reRoute.DownstreamTemplate, reRoute.UpstreamTemplate, reRoute.UpstreamHttpMethod, upstreamTemplate, isAuthenticated, reRoute.Authentication)); if (isAuthenticated)
{
var authOptionsForRoute = new AuthenticationOptions(reRoute.AuthenticationOptions.Provider,
reRoute.AuthenticationOptions.ProviderRootUrl, reRoute.AuthenticationOptions.ScopeName,
reRoute.AuthenticationOptions.RequireHttps, reRoute.AuthenticationOptions.AdditionalScopes,
reRoute.AuthenticationOptions.ScopeSecret);
_reRoutes.Add(new ReRoute(reRoute.DownstreamTemplate, reRoute.UpstreamTemplate,
reRoute.UpstreamHttpMethod, upstreamTemplate, isAuthenticated, authOptionsForRoute
));
}
else
{
_reRoutes.Add(new ReRoute(reRoute.DownstreamTemplate, reRoute.UpstreamTemplate, reRoute.UpstreamHttpMethod,
upstreamTemplate, isAuthenticated, null));
} }
} }
private static bool IsPlaceHolder(string upstreamTemplate, int i) private bool IsPlaceHolder(string upstreamTemplate, int i)
{ {
return upstreamTemplate[i] == '{'; return upstreamTemplate[i] == '{';
} }

View File

@ -2,14 +2,14 @@
{ {
public class ReRoute public class ReRoute
{ {
public ReRoute(string downstreamTemplate, string upstreamTemplate, string upstreamHttpMethod, string upstreamTemplatePattern, bool isAuthenticated, string authenticationProvider) public ReRoute(string downstreamTemplate, string upstreamTemplate, string upstreamHttpMethod, string upstreamTemplatePattern, bool isAuthenticated, AuthenticationOptions authenticationOptions)
{ {
DownstreamTemplate = downstreamTemplate; DownstreamTemplate = downstreamTemplate;
UpstreamTemplate = upstreamTemplate; UpstreamTemplate = upstreamTemplate;
UpstreamHttpMethod = upstreamHttpMethod; UpstreamHttpMethod = upstreamHttpMethod;
UpstreamTemplatePattern = upstreamTemplatePattern; UpstreamTemplatePattern = upstreamTemplatePattern;
IsAuthenticated = isAuthenticated; IsAuthenticated = isAuthenticated;
AuthenticationProvider = authenticationProvider; AuthenticationOptions = authenticationOptions;
} }
public string DownstreamTemplate { get; private set; } public string DownstreamTemplate { get; private set; }
@ -17,6 +17,6 @@
public string UpstreamTemplatePattern { get; private set; } public string UpstreamTemplatePattern { get; private set; }
public string UpstreamHttpMethod { get; private set; } public string UpstreamHttpMethod { get; private set; }
public bool IsAuthenticated { get; private set; } public bool IsAuthenticated { get; private set; }
public string AuthenticationProvider { get; private set; } public AuthenticationOptions AuthenticationOptions { get; private set; }
} }
} }

View File

@ -1,5 +1,7 @@
using System.Collections.Generic; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using Ocelot.Library.Infrastructure.Authentication;
using Ocelot.Library.Infrastructure.Errors; using Ocelot.Library.Infrastructure.Errors;
using Ocelot.Library.Infrastructure.Responses; using Ocelot.Library.Infrastructure.Responses;
@ -8,6 +10,59 @@ namespace Ocelot.Library.Infrastructure.Configuration.Yaml
public class ConfigurationValidator : IConfigurationValidator public class ConfigurationValidator : IConfigurationValidator
{ {
public Response<ConfigurationValidationResult> IsValid(YamlConfiguration configuration) public Response<ConfigurationValidationResult> IsValid(YamlConfiguration configuration)
{
var result = CheckForDupliateReRoutes(configuration);
if (result.IsError)
{
return new OkResponse<ConfigurationValidationResult>(result);
}
result = CheckForUnsupportedAuthenticationProviders(configuration);
if (result.IsError)
{
return new OkResponse<ConfigurationValidationResult>(result);
}
return new OkResponse<ConfigurationValidationResult>(result);
}
private ConfigurationValidationResult CheckForUnsupportedAuthenticationProviders(YamlConfiguration configuration)
{
var errors = new List<Error>();
foreach (var yamlReRoute in configuration.ReRoutes)
{
var isAuthenticated = !string.IsNullOrEmpty(yamlReRoute.AuthenticationOptions?.Provider);
if (!isAuthenticated)
{
continue;
}
if (IsSupportedAuthenticationProvider(yamlReRoute.AuthenticationOptions?.Provider))
{
continue;
}
var error = new UnsupportedAuthenticationProviderError($"{yamlReRoute.AuthenticationOptions?.Provider} is unsupported authentication provider, upstream template is {yamlReRoute.UpstreamTemplate}, upstream method is {yamlReRoute.UpstreamHttpMethod}");
errors.Add(error);
}
return errors.Count > 0
? new ConfigurationValidationResult(true, errors)
: new ConfigurationValidationResult(false);
}
private bool IsSupportedAuthenticationProvider(string provider)
{
SupportAuthenticationProviders supportedProvider;
return Enum.TryParse(provider, true, out supportedProvider);
}
private ConfigurationValidationResult CheckForDupliateReRoutes(YamlConfiguration configuration)
{ {
var duplicateUpstreamTemplates = configuration.ReRoutes var duplicateUpstreamTemplates = configuration.ReRoutes
.Select(r => r.DownstreamTemplate) .Select(r => r.DownstreamTemplate)
@ -18,19 +73,15 @@ namespace Ocelot.Library.Infrastructure.Configuration.Yaml
if (duplicateUpstreamTemplates.Count <= 0) if (duplicateUpstreamTemplates.Count <= 0)
{ {
return new OkResponse<ConfigurationValidationResult>(new ConfigurationValidationResult(false)); return new ConfigurationValidationResult(false);
} }
var errors = new List<Error>(); var errors = duplicateUpstreamTemplates
.Select(duplicateUpstreamTemplate => new DownstreamTemplateAlreadyUsedError(string.Format("Duplicate DownstreamTemplate: {0}", duplicateUpstreamTemplate)))
.Cast<Error>()
.ToList();
foreach (var duplicateUpstreamTemplate in duplicateUpstreamTemplates) return new ConfigurationValidationResult(true, errors);
{
var error = new DownstreamTemplateAlreadyUsedError(string.Format("Duplicate DownstreamTemplate: {0}",
duplicateUpstreamTemplate));
errors.Add(error);
}
return new OkResponse<ConfigurationValidationResult>(new ConfigurationValidationResult(true, errors));
} }
} }
} }

View File

@ -0,0 +1,12 @@
using Ocelot.Library.Infrastructure.Errors;
namespace Ocelot.Library.Infrastructure.Configuration.Yaml
{
public class UnsupportedAuthenticationProviderError : Error
{
public UnsupportedAuthenticationProviderError(string message)
: base(message, OcelotErrorCode.UnsupportedAuthenticationProviderError)
{
}
}
}

View File

@ -0,0 +1,14 @@
using System.Collections.Generic;
namespace Ocelot.Library.Infrastructure.Configuration.Yaml
{
public class YamlAuthenticationOptions
{
public string Provider { get; set; }
public string ProviderRootUrl { get; set; }
public string ScopeName { get; set; }
public bool RequireHttps { get; set; }
public List<string> AdditionalScopes { get; set; }
public string ScopeSecret { get; set; }
}
}

View File

@ -5,6 +5,6 @@
public string DownstreamTemplate { get; set; } public string DownstreamTemplate { get; set; }
public string UpstreamTemplate { get; set; } public string UpstreamTemplate { get; set; }
public string UpstreamHttpMethod { get; set; } public string UpstreamHttpMethod { get; set; }
public string Authentication { get; set; } public YamlAuthenticationOptions AuthenticationOptions { get; set; }
} }
} }

View File

@ -9,6 +9,7 @@
CannotAddDataError, CannotAddDataError,
CannotFindDataError, CannotFindDataError,
UnableToCompleteRequestError, UnableToCompleteRequestError,
UnableToCreateAuthenticationHandlerError UnableToCreateAuthenticationHandlerError,
UnsupportedAuthenticationProviderError
} }
} }

View File

@ -1,25 +1,18 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Ocelot.Library.Infrastructure.Authentication;
using Ocelot.Library.Infrastructure.Configuration; using Ocelot.Library.Infrastructure.Configuration;
using Ocelot.Library.Infrastructure.DownstreamRouteFinder; using Ocelot.Library.Infrastructure.DownstreamRouteFinder;
using Ocelot.Library.Infrastructure.Errors; using Ocelot.Library.Infrastructure.Errors;
using Ocelot.Library.Infrastructure.Repository; using Ocelot.Library.Infrastructure.Repository;
using Ocelot.Library.Infrastructure.Responses;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Ocelot.Library.Infrastructure.Authentication;
namespace Ocelot.Library.Infrastructure.Middleware namespace Ocelot.Library.Infrastructure.Middleware
{ {
public class AuthenticationMiddleware : OcelotMiddleware public class AuthenticationMiddleware : OcelotMiddleware
{ {
private readonly RequestDelegate _next; private readonly RequestDelegate _next;
private RequestDelegate _authenticationNext;
private readonly IScopedRequestDataRepository _scopedRequestDataRepository; private readonly IScopedRequestDataRepository _scopedRequestDataRepository;
private readonly IApplicationBuilder _app; private readonly IApplicationBuilder _app;
private readonly IAuthenticationHandlerFactory _authHandlerFactory; private readonly IAuthenticationHandlerFactory _authHandlerFactory;
@ -46,7 +39,7 @@ namespace Ocelot.Library.Infrastructure.Middleware
if (IsAuthenticatedRoute(downstreamRoute.Data.ReRoute)) if (IsAuthenticatedRoute(downstreamRoute.Data.ReRoute))
{ {
var authenticationNext = _authHandlerFactory.Get(downstreamRoute.Data.ReRoute.AuthenticationProvider, _app); var authenticationNext = _authHandlerFactory.Get(_app, downstreamRoute.Data.ReRoute.AuthenticationOptions);
if (!authenticationNext.IsError) if (!authenticationNext.IsError)
{ {

View File

@ -43,11 +43,13 @@ namespace Ocelot
services.AddOptions(); services.AddOptions();
services.AddMvc(); services.AddMvc();
services.AddMvcCore().AddAuthorization().AddJsonFormatters(); services.AddMvcCore().AddAuthorization().AddJsonFormatters();
services.AddAuthentication();
services.AddLogging();
services.Configure<YamlConfiguration>(Configuration); services.Configure<YamlConfiguration>(Configuration);
services.AddAuthentication();
// Add framework services. // Add framework services.
services.AddSingleton<IConfigurationValidator, ConfigurationValidator>();
services.AddSingleton<IOcelotConfiguration, OcelotConfiguration>(); services.AddSingleton<IOcelotConfiguration, OcelotConfiguration>();
services.AddSingleton<IUrlPathToUrlTemplateMatcher, RegExUrlMatcher>(); services.AddSingleton<IUrlPathToUrlTemplateMatcher, RegExUrlMatcher>();
services.AddSingleton<ITemplateVariableNameAndValueFinder, TemplateVariableNameAndValueFinder>(); services.AddSingleton<ITemplateVariableNameAndValueFinder, TemplateVariableNameAndValueFinder>();

View File

@ -43,7 +43,7 @@ namespace Ocelot.AcceptanceTests
[Fact] [Fact]
public void should_return_401_using_identity_server_access_token() public void should_return_401_using_identity_server_access_token()
{ {
this.Given(x => x.GivenThereIsAnIdentityServerOn("http://localhost:51888")) this.Given(x => x.GivenThereIsAnIdentityServerOn("http://localhost:51888", "api", AccessTokenType.Jwt))
.And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51876", 201, string.Empty)) .And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51876", 201, string.Empty))
.And(x => x.GivenThereIsAConfiguration(new YamlConfiguration .And(x => x.GivenThereIsAConfiguration(new YamlConfiguration
{ {
@ -54,7 +54,48 @@ namespace Ocelot.AcceptanceTests
DownstreamTemplate = "http://localhost:51876/", DownstreamTemplate = "http://localhost:51876/",
UpstreamTemplate = "/", UpstreamTemplate = "/",
UpstreamHttpMethod = "Post", UpstreamHttpMethod = "Post",
Authentication = "JwtBearerAuthentication" AuthenticationOptions = new YamlAuthenticationOptions
{
AdditionalScopes = new List<string>(),
Provider = "IdentityServer",
ProviderRootUrl = "http://localhost:51888",
RequireHttps = false,
ScopeName = "api",
ScopeSecret = "secret"
}
}
}
}))
.And(x => x.GivenTheApiGatewayIsRunning())
.And(x => x.GivenThePostHasContent("postContent"))
.When(x => x.WhenIPostUrlOnTheApiGateway("/"))
.Then(x => x.ThenTheStatusCodeShouldBe(HttpStatusCode.Unauthorized))
.BDDfy();
}
[Fact]
public void should_return_401_using_identity_server_reference_token()
{
this.Given(x => x.GivenThereIsAnIdentityServerOn("http://localhost:51888", "api", AccessTokenType.Reference))
.And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51876", 201, string.Empty))
.And(x => x.GivenThereIsAConfiguration(new YamlConfiguration
{
ReRoutes = new List<YamlReRoute>
{
new YamlReRoute
{
DownstreamTemplate = "http://localhost:51876/",
UpstreamTemplate = "/",
UpstreamHttpMethod = "Post",
AuthenticationOptions = new YamlAuthenticationOptions
{
AdditionalScopes = new List<string>(),
Provider = "IdentityServer",
ProviderRootUrl = "http://localhost:51888",
RequireHttps = false,
ScopeName = "api",
ScopeSecret = "secret"
}
} }
} }
})) }))
@ -68,7 +109,7 @@ namespace Ocelot.AcceptanceTests
[Fact] [Fact]
public void should_return_201_using_identity_server_access_token() public void should_return_201_using_identity_server_access_token()
{ {
this.Given(x => x.GivenThereIsAnIdentityServerOn("http://localhost:51888")) this.Given(x => x.GivenThereIsAnIdentityServerOn("http://localhost:51888", "api", AccessTokenType.Jwt))
.And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51876", 201, string.Empty)) .And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51876", 201, string.Empty))
.And(x => x.GivenIHaveAToken("http://localhost:51888")) .And(x => x.GivenIHaveAToken("http://localhost:51888"))
.And(x => x.GivenThereIsAConfiguration(new YamlConfiguration .And(x => x.GivenThereIsAConfiguration(new YamlConfiguration
@ -80,7 +121,50 @@ namespace Ocelot.AcceptanceTests
DownstreamTemplate = "http://localhost:51876/", DownstreamTemplate = "http://localhost:51876/",
UpstreamTemplate = "/", UpstreamTemplate = "/",
UpstreamHttpMethod = "Post", UpstreamHttpMethod = "Post",
Authentication = "JwtBearerAuthentication" AuthenticationOptions = new YamlAuthenticationOptions
{
AdditionalScopes = new List<string>(),
Provider = "IdentityServer",
ProviderRootUrl = "http://localhost:51888",
RequireHttps = false,
ScopeName = "api",
ScopeSecret = "secret"
}
}
}
}))
.And(x => x.GivenTheApiGatewayIsRunning())
.And(x => x.GivenIHaveAddedATokenToMyRequest())
.And(x => x.GivenThePostHasContent("postContent"))
.When(x => x.WhenIPostUrlOnTheApiGateway("/"))
.Then(x => x.ThenTheStatusCodeShouldBe(HttpStatusCode.Created))
.BDDfy();
}
[Fact]
public void should_return_201_using_identity_server_reference_token()
{
this.Given(x => x.GivenThereIsAnIdentityServerOn("http://localhost:51888", "api", AccessTokenType.Reference))
.And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51876", 201, string.Empty))
.And(x => x.GivenIHaveAToken("http://localhost:51888"))
.And(x => x.GivenThereIsAConfiguration(new YamlConfiguration
{
ReRoutes = new List<YamlReRoute>
{
new YamlReRoute
{
DownstreamTemplate = "http://localhost:51876/",
UpstreamTemplate = "/",
UpstreamHttpMethod = "Post",
AuthenticationOptions = new YamlAuthenticationOptions
{
AdditionalScopes = new List<string>(),
Provider = "IdentityServer",
ProviderRootUrl = "http://localhost:51888",
RequireHttps = false,
ScopeName = "api",
ScopeSecret = "secret"
}
} }
} }
})) }))
@ -144,7 +228,7 @@ namespace Ocelot.AcceptanceTests
_ocelotBbuilder.Start(); _ocelotBbuilder.Start();
} }
private void GivenThereIsAnIdentityServerOn(string url) private void GivenThereIsAnIdentityServerOn(string url, string scopeName, AccessTokenType tokenType)
{ {
_identityServerBuilder = new WebHostBuilder() _identityServerBuilder = new WebHostBuilder()
.UseUrls(url) .UseUrls(url)
@ -154,13 +238,21 @@ namespace Ocelot.AcceptanceTests
.UseUrls(url) .UseUrls(url)
.ConfigureServices(services => .ConfigureServices(services =>
{ {
services.AddLogging();
services.AddDeveloperIdentityServer() services.AddDeveloperIdentityServer()
.AddInMemoryScopes(new List<Scope> { new Scope .AddInMemoryScopes(new List<Scope> { new Scope
{ {
Name = "api", Name = scopeName,
Description = "My API", Description = "My API",
Enabled = true Enabled = true,
AllowUnrestrictedIntrospection = true,
ScopeSecrets = new List<Secret>()
{
new Secret
{
Value = "secret".Sha256()
}
}
}}) }})
.AddInMemoryClients(new List<Client> { .AddInMemoryClients(new List<Client> {
new Client new Client
@ -168,8 +260,8 @@ namespace Ocelot.AcceptanceTests
ClientId = "client", ClientId = "client",
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
ClientSecrets = new List<Secret> { new Secret("secret".Sha256()) }, ClientSecrets = new List<Secret> { new Secret("secret".Sha256()) },
AllowedScopes = new List<string> { "api" }, AllowedScopes = new List<string> { scopeName },
AccessTokenType = AccessTokenType.Jwt, AccessTokenType = tokenType,
Enabled = true, Enabled = true,
RequireClientSecret = false RequireClientSecret = false
} }) } })
@ -248,6 +340,7 @@ namespace Ocelot.AcceptanceTests
_identityServerBuilder?.Dispose(); _identityServerBuilder?.Dispose();
} }
// ReSharper disable once ClassNeverInstantiated.Local
class BearerToken class BearerToken
{ {
[JsonProperty("access_token")] [JsonProperty("access_token")]

View File

@ -17,8 +17,7 @@ namespace Ocelot.UnitTests.Authentication
private readonly IAuthenticationHandlerFactory _authenticationHandlerFactory; private readonly IAuthenticationHandlerFactory _authenticationHandlerFactory;
private readonly Mock<IApplicationBuilder> _app; private readonly Mock<IApplicationBuilder> _app;
private readonly Mock<IAuthenticationHandlerCreator> _creator; private readonly Mock<IAuthenticationHandlerCreator> _creator;
private Library.Infrastructure.Configuration.AuthenticationOptions _authenticationOptions;
private string _provider;
private Response<AuthenticationHandler> _result; private Response<AuthenticationHandler> _result;
public AuthenticationHandlerFactoryTests() public AuthenticationHandlerFactoryTests()
@ -31,27 +30,32 @@ namespace Ocelot.UnitTests.Authentication
[Fact] [Fact]
public void should_return_identity_server_access_token_handler() public void should_return_identity_server_access_token_handler()
{ {
this.Given(x => x.GivenTheProviderIs("IdentityServer.AccessToken")) this.Given(x => x.GivenTheAuthenticationOptionsAre(new Library.Infrastructure.Configuration.AuthenticationOptions("IdentityServer", "","",false, new List<string>(), "")))
.And(x => x.GivenTheCreatorReturns()) .And(x => x.GivenTheCreatorReturns())
.When(x => x.WhenIGetFromTheFactory()) .When(x => x.WhenIGetFromTheFactory())
.Then(x => x.ThenTheHandlerIsReturned("IdentityServer.AccessToken")) .Then(x => x.ThenTheHandlerIsReturned("IdentityServer"))
.BDDfy(); .BDDfy();
} }
[Fact] [Fact]
public void should_return_error_if_cannot_create_handler() public void should_return_error_if_cannot_create_handler()
{ {
this.Given(x => x.GivenTheProviderIs("IdentityServer.AccessToken")) this.Given(x => x.GivenTheAuthenticationOptionsAre(new Library.Infrastructure.Configuration.AuthenticationOptions("IdentityServer", "", "", false, new List<string>(), "")))
.And(x => x.GivenTheCreatorReturnsAnError()) .And(x => x.GivenTheCreatorReturnsAnError())
.When(x => x.WhenIGetFromTheFactory()) .When(x => x.WhenIGetFromTheFactory())
.Then(x => x.ThenAnErrorResponseIsReturned()) .Then(x => x.ThenAnErrorResponseIsReturned())
.BDDfy(); .BDDfy();
} }
private void GivenTheAuthenticationOptionsAre(Library.Infrastructure.Configuration.AuthenticationOptions authenticationOptions)
{
_authenticationOptions = authenticationOptions;
}
private void GivenTheCreatorReturnsAnError() private void GivenTheCreatorReturnsAnError()
{ {
_creator _creator
.Setup(x => x.CreateIdentityServerAuthenticationHandler(It.IsAny<IApplicationBuilder>())) .Setup(x => x.CreateIdentityServerAuthenticationHandler(It.IsAny<IApplicationBuilder>(), It.IsAny<Library.Infrastructure.Configuration.AuthenticationOptions>()))
.Returns(new ErrorResponse<RequestDelegate>(new List<Error> .Returns(new ErrorResponse<RequestDelegate>(new List<Error>
{ {
new UnableToCreateAuthenticationHandlerError($"Unable to create authentication handler for xxx") new UnableToCreateAuthenticationHandlerError($"Unable to create authentication handler for xxx")
@ -61,18 +65,13 @@ namespace Ocelot.UnitTests.Authentication
private void GivenTheCreatorReturns() private void GivenTheCreatorReturns()
{ {
_creator _creator
.Setup(x => x.CreateIdentityServerAuthenticationHandler(It.IsAny<IApplicationBuilder>())) .Setup(x => x.CreateIdentityServerAuthenticationHandler(It.IsAny<IApplicationBuilder>(), It.IsAny<Library.Infrastructure.Configuration.AuthenticationOptions>()))
.Returns(new OkResponse<RequestDelegate>(x => Task.CompletedTask)); .Returns(new OkResponse<RequestDelegate>(x => Task.CompletedTask));
} }
private void GivenTheProviderIs(string provider)
{
_provider = provider;
}
private void WhenIGetFromTheFactory() private void WhenIGetFromTheFactory()
{ {
_result = _authenticationHandlerFactory.Get(_provider, _app.Object); _result = _authenticationHandlerFactory.Get(_app.Object, _authenticationOptions);
} }
private void ThenTheHandlerIsReturned(string expected) private void ThenTheHandlerIsReturned(string expected)

View File

@ -37,6 +37,53 @@ namespace Ocelot.UnitTests.Configuration
.BDDfy(); .BDDfy();
} }
[Fact]
public void configuration_is_valid_with_valid_authentication_provider()
{
this.Given(x => x.GivenAConfiguration(new YamlConfiguration()
{
ReRoutes = new List<YamlReRoute>
{
new YamlReRoute
{
DownstreamTemplate = "http://www.bbc.co.uk",
UpstreamTemplate = "http://asdf.com",
AuthenticationOptions = new YamlAuthenticationOptions
{
Provider = "IdentityServer"
}
}
}
}))
.When(x => x.WhenIValidateTheConfiguration())
.Then(x => x.ThenTheResultIsValid())
.BDDfy();
}
[Fact]
public void configuration_is_invalid_with_invalid_authentication_provider()
{
this.Given(x => x.GivenAConfiguration(new YamlConfiguration()
{
ReRoutes = new List<YamlReRoute>
{
new YamlReRoute
{
DownstreamTemplate = "http://www.bbc.co.uk",
UpstreamTemplate = "http://asdf.com",
AuthenticationOptions = new YamlAuthenticationOptions
{
Provider = "BootyBootyBottyRockinEverywhere"
}
}
}
}))
.When(x => x.WhenIValidateTheConfiguration())
.Then(x => x.ThenTheResultIsNotValid())
.And(x => x.ThenTheErrorIs<UnsupportedAuthenticationProviderError>())
.BDDfy();
}
[Fact] [Fact]
public void configuration_is_not_valid_with_duplicate_reroutes() public void configuration_is_not_valid_with_duplicate_reroutes()
{ {

View File

@ -1,8 +1,10 @@
using System.Collections.Generic; using System.Collections.Generic;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Moq; using Moq;
using Ocelot.Library.Infrastructure.Builder;
using Ocelot.Library.Infrastructure.Configuration; using Ocelot.Library.Infrastructure.Configuration;
using Ocelot.Library.Infrastructure.Configuration.Yaml; using Ocelot.Library.Infrastructure.Configuration.Yaml;
using Ocelot.Library.Infrastructure.Responses;
using Shouldly; using Shouldly;
using TestStack.BDDfy; using TestStack.BDDfy;
using Xunit; using Xunit;
@ -12,11 +14,13 @@ namespace Ocelot.UnitTests.Configuration
public class OcelotConfigurationTests public class OcelotConfigurationTests
{ {
private readonly Mock<IOptions<YamlConfiguration>> _yamlConfig; private readonly Mock<IOptions<YamlConfiguration>> _yamlConfig;
private readonly Mock<IConfigurationValidator> _validator;
private OcelotConfiguration _config; private OcelotConfiguration _config;
private YamlConfiguration _yamlConfiguration; private YamlConfiguration _yamlConfiguration;
public OcelotConfigurationTests() public OcelotConfigurationTests()
{ {
_validator = new Mock<IConfigurationValidator>();
_yamlConfig = new Mock<IOptions<YamlConfiguration>>(); _yamlConfig = new Mock<IOptions<YamlConfiguration>>();
} }
@ -35,10 +39,16 @@ namespace Ocelot.UnitTests.Configuration
} }
} }
})) }))
.And(x => x.GivenTheYamlConfigIsValid())
.When(x => x.WhenIInstanciateTheOcelotConfig()) .When(x => x.WhenIInstanciateTheOcelotConfig())
.Then(x => x.ThenTheReRoutesAre(new List<ReRoute> .Then(x => x.ThenTheReRoutesAre(new List<ReRoute>
{ {
new ReRoute("/products/{productId}","/api/products/{productId}", "Get", "/api/products/.*$", false, "") new ReRouteBuilder()
.WithDownstreamTemplate("/products/{productId}")
.WithUpstreamTemplate("/api/products/{productId}")
.WithUpstreamHttpMethod("Get")
.WithUpstreamTemplatePattern("/api/products/.*$")
.Build()
})) }))
.BDDfy(); .BDDfy();
} }
@ -58,10 +68,16 @@ namespace Ocelot.UnitTests.Configuration
} }
} }
})) }))
.And(x => x.GivenTheYamlConfigIsValid())
.When(x => x.WhenIInstanciateTheOcelotConfig()) .When(x => x.WhenIInstanciateTheOcelotConfig())
.Then(x => x.ThenTheReRoutesAre(new List<ReRoute> .Then(x => x.ThenTheReRoutesAre(new List<ReRoute>
{ {
new ReRoute("/products/{productId}","/api/products/{productId}/variants/{variantId}", "Get", "/api/products/.*/variants/.*$", false, "") new ReRouteBuilder()
.WithDownstreamTemplate("/products/{productId}")
.WithUpstreamTemplate("/api/products/{productId}/variants/{variantId}")
.WithUpstreamHttpMethod("Get")
.WithUpstreamTemplatePattern("/api/products/.*/variants/.*$")
.Build()
})) }))
.BDDfy(); .BDDfy();
} }
@ -81,10 +97,16 @@ namespace Ocelot.UnitTests.Configuration
} }
} }
})) }))
.And(x => x.GivenTheYamlConfigIsValid())
.When(x => x.WhenIInstanciateTheOcelotConfig()) .When(x => x.WhenIInstanciateTheOcelotConfig())
.Then(x => x.ThenTheReRoutesAre(new List<ReRoute> .Then(x => x.ThenTheReRoutesAre(new List<ReRoute>
{ {
new ReRoute("/products/{productId}","/api/products/{productId}/variants/{variantId}/", "Get", "/api/products/.*/variants/.*/$", false, "") new ReRouteBuilder()
.WithDownstreamTemplate("/products/{productId}")
.WithUpstreamTemplate("/api/products/{productId}/variants/{variantId}/")
.WithUpstreamHttpMethod("Get")
.WithUpstreamTemplatePattern("/api/products/.*/variants/.*/$")
.Build()
})) }))
.BDDfy(); .BDDfy();
} }
@ -104,14 +126,27 @@ namespace Ocelot.UnitTests.Configuration
} }
} }
})) }))
.And(x => x.GivenTheYamlConfigIsValid())
.When(x => x.WhenIInstanciateTheOcelotConfig()) .When(x => x.WhenIInstanciateTheOcelotConfig())
.Then(x => x.ThenTheReRoutesAre(new List<ReRoute> .Then(x => x.ThenTheReRoutesAre(new List<ReRoute>
{ {
new ReRoute("/api/products/","/", "Get", "/$", false, "") new ReRouteBuilder()
.WithDownstreamTemplate("/api/products/")
.WithUpstreamTemplate("/")
.WithUpstreamHttpMethod("Get")
.WithUpstreamTemplatePattern("/$")
.Build()
})) }))
.BDDfy(); .BDDfy();
} }
private void GivenTheYamlConfigIsValid()
{
_validator
.Setup(x => x.IsValid(It.IsAny<YamlConfiguration>()))
.Returns(new OkResponse<ConfigurationValidationResult>(new ConfigurationValidationResult(false)));
}
private void GivenTheYamlConfigIs(YamlConfiguration yamlConfiguration) private void GivenTheYamlConfigIs(YamlConfiguration yamlConfiguration)
{ {
_yamlConfiguration = yamlConfiguration; _yamlConfiguration = yamlConfiguration;
@ -122,7 +157,7 @@ namespace Ocelot.UnitTests.Configuration
private void WhenIInstanciateTheOcelotConfig() private void WhenIInstanciateTheOcelotConfig()
{ {
_config = new OcelotConfiguration(_yamlConfig.Object); _config = new OcelotConfiguration(_yamlConfig.Object, _validator.Object);
} }
private void ThenTheReRoutesAre(List<ReRoute> expectedReRoutes) private void ThenTheReRoutesAre(List<ReRoute> expectedReRoutes)

View File

@ -1,5 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using Moq; using Moq;
using Ocelot.Library.Infrastructure.Builder;
using Ocelot.Library.Infrastructure.Configuration; using Ocelot.Library.Infrastructure.Configuration;
using Ocelot.Library.Infrastructure.DownstreamRouteFinder; using Ocelot.Library.Infrastructure.DownstreamRouteFinder;
using Ocelot.Library.Infrastructure.Responses; using Ocelot.Library.Infrastructure.Responses;
@ -34,17 +35,29 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
public void should_return_route() public void should_return_route()
{ {
this.Given(x => x.GivenThereIsAnUpstreamUrlPath("someUpstreamPath")) this.Given(x => x.GivenThereIsAnUpstreamUrlPath("someUpstreamPath"))
.And(x => x.GivenTheTemplateVariableAndNameFinderReturns(new OkResponse<List<TemplateVariableNameAndValue>>(new List<TemplateVariableNameAndValue>()))) .And(
x =>
x.GivenTheTemplateVariableAndNameFinderReturns(
new OkResponse<List<TemplateVariableNameAndValue>>(new List<TemplateVariableNameAndValue>())))
.And(x => x.GivenTheConfigurationIs(new List<ReRoute> .And(x => x.GivenTheConfigurationIs(new List<ReRoute>
{ {
new ReRoute("someDownstreamPath","someUpstreamPath", "Get", "someUpstreamPath", false, "") new ReRouteBuilder()
.WithDownstreamTemplate("someDownstreamPath")
.WithUpstreamTemplate("someUpstreamPath")
.WithUpstreamHttpMethod("Get")
.WithUpstreamTemplatePattern("someUpstreamPath")
.Build()
} }
)) ))
.And(x => x.GivenTheUrlMatcherReturns(new OkResponse<UrlMatch>(new UrlMatch(true)))) .And(x => x.GivenTheUrlMatcherReturns(new OkResponse<UrlMatch>(new UrlMatch(true))))
.And(x => x.GivenTheUpstreamHttpMethodIs("Get")) .And(x => x.GivenTheUpstreamHttpMethodIs("Get"))
.When(x => x.WhenICallTheFinder()) .When(x => x.WhenICallTheFinder())
.Then( .Then(
x => x.ThenTheFollowingIsReturned(new DownstreamRoute(new List<TemplateVariableNameAndValue>(), new ReRoute("someDownstreamPath","","","",false, "")))) x => x.ThenTheFollowingIsReturned(new DownstreamRoute(new List<TemplateVariableNameAndValue>(),
new ReRouteBuilder()
.WithDownstreamTemplate("someDownstreamPath")
.Build()
)))
.And(x => x.ThenTheUrlMatcherIsCalledCorrectly()) .And(x => x.ThenTheUrlMatcherIsCalledCorrectly())
.BDDfy(); .BDDfy();
} }
@ -53,18 +66,35 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
public void should_return_correct_route_for_http_verb() public void should_return_correct_route_for_http_verb()
{ {
this.Given(x => x.GivenThereIsAnUpstreamUrlPath("someUpstreamPath")) this.Given(x => x.GivenThereIsAnUpstreamUrlPath("someUpstreamPath"))
.And(x => x.GivenTheTemplateVariableAndNameFinderReturns(new OkResponse<List<TemplateVariableNameAndValue>>(new List<TemplateVariableNameAndValue>()))) .And(
x =>
x.GivenTheTemplateVariableAndNameFinderReturns(
new OkResponse<List<TemplateVariableNameAndValue>>(new List<TemplateVariableNameAndValue>())))
.And(x => x.GivenTheConfigurationIs(new List<ReRoute> .And(x => x.GivenTheConfigurationIs(new List<ReRoute>
{ {
new ReRoute("someDownstreamPath", "someUpstreamPath", "Get", string.Empty, false, ""), new ReRouteBuilder()
new ReRoute("someDownstreamPathForAPost", "someUpstreamPath", "Post", string.Empty, false, "") .WithDownstreamTemplate("someDownstreamPath")
.WithUpstreamTemplate("someUpstreamPath")
.WithUpstreamHttpMethod("Get")
.WithUpstreamTemplatePattern("")
.Build(),
new ReRouteBuilder()
.WithDownstreamTemplate("someDownstreamPathForAPost")
.WithUpstreamTemplate("someUpstreamPath")
.WithUpstreamHttpMethod("Post")
.WithUpstreamTemplatePattern("")
.Build()
} }
)) ))
.And(x => x.GivenTheUrlMatcherReturns(new OkResponse<UrlMatch>(new UrlMatch(true)))) .And(x => x.GivenTheUrlMatcherReturns(new OkResponse<UrlMatch>(new UrlMatch(true))))
.And(x => x.GivenTheUpstreamHttpMethodIs("Post")) .And(x => x.GivenTheUpstreamHttpMethodIs("Post"))
.When(x => x.WhenICallTheFinder()) .When(x => x.WhenICallTheFinder())
.Then( .Then(
x => x.ThenTheFollowingIsReturned(new DownstreamRoute(new List<TemplateVariableNameAndValue>(), new ReRoute("someDownstreamPathForAPost", "","","",false, "")))) x => x.ThenTheFollowingIsReturned(new DownstreamRoute(new List<TemplateVariableNameAndValue>(),
new ReRouteBuilder()
.WithDownstreamTemplate("someDownstreamPathForAPost")
.Build()
)))
.BDDfy(); .BDDfy();
} }
@ -74,7 +104,12 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
this.Given(x => x.GivenThereIsAnUpstreamUrlPath("somePath")) this.Given(x => x.GivenThereIsAnUpstreamUrlPath("somePath"))
.And(x => x.GivenTheConfigurationIs(new List<ReRoute> .And(x => x.GivenTheConfigurationIs(new List<ReRoute>
{ {
new ReRoute("somPath", "somePath", "Get", "somePath", false, "") new ReRouteBuilder()
.WithDownstreamTemplate("somPath")
.WithUpstreamTemplate("somePath")
.WithUpstreamHttpMethod("Get")
.WithUpstreamTemplatePattern("somePath")
.Build(),
} }
)) ))
.And(x => x.GivenTheUrlMatcherReturns(new OkResponse<UrlMatch>(new UrlMatch(false)))) .And(x => x.GivenTheUrlMatcherReturns(new OkResponse<UrlMatch>(new UrlMatch(false))))

View File

@ -7,6 +7,7 @@ using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Moq; using Moq;
using Ocelot.Library.Infrastructure.Authentication; using Ocelot.Library.Infrastructure.Authentication;
using Ocelot.Library.Infrastructure.Builder;
using Ocelot.Library.Infrastructure.DownstreamRouteFinder; using Ocelot.Library.Infrastructure.DownstreamRouteFinder;
using Ocelot.Library.Infrastructure.Middleware; using Ocelot.Library.Infrastructure.Middleware;
using Ocelot.Library.Infrastructure.Repository; using Ocelot.Library.Infrastructure.Repository;
@ -57,7 +58,7 @@ namespace Ocelot.UnitTests.Middleware
[Fact] [Fact]
public void happy_path() public void happy_path()
{ {
this.Given(x => x.GivenTheDownStreamRouteIs(new DownstreamRoute(new List<TemplateVariableNameAndValue>(), new ReRoute("","","","",false, "")))) this.Given(x => x.GivenTheDownStreamRouteIs(new DownstreamRoute(new List<TemplateVariableNameAndValue>(), new ReRouteBuilder().Build())))
.When(x => x.WhenICallTheMiddleware()) .When(x => x.WhenICallTheMiddleware())
.Then(x => x.ThenNoExceptionsAreThrown()) .Then(x => x.ThenNoExceptionsAreThrown())
.BDDfy(); .BDDfy();

View File

@ -1,23 +1,22 @@
using Ocelot.Library.Infrastructure.Middleware; using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection;
using Moq;
using Ocelot.Library.Infrastructure.Builder;
using Ocelot.Library.Infrastructure.DownstreamRouteFinder;
using Ocelot.Library.Infrastructure.Middleware;
using Ocelot.Library.Infrastructure.Repository;
using Ocelot.Library.Infrastructure.Responses;
using Ocelot.Library.Infrastructure.UrlMatcher;
using TestStack.BDDfy;
using Xunit;
namespace Ocelot.UnitTests.Middleware namespace Ocelot.UnitTests.Middleware
{ {
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using Library.Infrastructure.Configuration;
using Library.Infrastructure.DownstreamRouteFinder;
using Library.Infrastructure.Repository;
using Library.Infrastructure.Responses;
using Library.Infrastructure.UrlMatcher;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection;
using Moq;
using TestStack.BDDfy;
using Xunit;
public class DownstreamRouteFinderMiddlewareTests : IDisposable public class DownstreamRouteFinderMiddlewareTests : IDisposable
{ {
private readonly Mock<IDownstreamRouteFinder> _downstreamRouteFinder; private readonly Mock<IDownstreamRouteFinder> _downstreamRouteFinder;
@ -57,7 +56,7 @@ namespace Ocelot.UnitTests.Middleware
[Fact] [Fact]
public void happy_path() public void happy_path()
{ {
this.Given(x => x.GivenTheDownStreamRouteFinderReturns(new DownstreamRoute(new List<TemplateVariableNameAndValue>(), new ReRoute("any old string", "", "", "",false, "")))) this.Given(x => x.GivenTheDownStreamRouteFinderReturns(new DownstreamRoute(new List<TemplateVariableNameAndValue>(), new ReRouteBuilder().WithDownstreamTemplate("any old string").Build())))
.When(x => x.WhenICallTheMiddleware()) .When(x => x.WhenICallTheMiddleware())
.Then(x => x.ThenTheScopedDataRepositoryIsCalledCorrectly()) .Then(x => x.ThenTheScopedDataRepositoryIsCalledCorrectly())
.BDDfy(); .BDDfy();

View File

@ -1,4 +1,5 @@
using Ocelot.Library.Infrastructure.Middleware; using Ocelot.Library.Infrastructure.Builder;
using Ocelot.Library.Infrastructure.Middleware;
namespace Ocelot.UnitTests.Middleware namespace Ocelot.UnitTests.Middleware
{ {
@ -59,7 +60,7 @@ namespace Ocelot.UnitTests.Middleware
[Fact] [Fact]
public void happy_path() public void happy_path()
{ {
this.Given(x => x.GivenTheDownStreamRouteIs(new DownstreamRoute(new List<TemplateVariableNameAndValue>(), new ReRoute("any old string", "", "", "", false, "")))) this.Given(x => x.GivenTheDownStreamRouteIs(new DownstreamRoute(new List<TemplateVariableNameAndValue>(), new ReRouteBuilder().WithDownstreamTemplate("any old string").Build())))
.And(x => x.TheUrlReplacerReturns("any old string")) .And(x => x.TheUrlReplacerReturns("any old string"))
.When(x => x.WhenICallTheMiddleware()) .When(x => x.WhenICallTheMiddleware())
.Then(x => x.ThenTheScopedDataRepositoryIsCalledCorrectly()) .Then(x => x.ThenTheScopedDataRepositoryIsCalledCorrectly())

View File

@ -1,4 +1,5 @@
using System.Collections.Generic; using System.Collections.Generic;
using Ocelot.Library.Infrastructure.Builder;
using Ocelot.Library.Infrastructure.DownstreamRouteFinder; using Ocelot.Library.Infrastructure.DownstreamRouteFinder;
using Ocelot.Library.Infrastructure.Responses; using Ocelot.Library.Infrastructure.Responses;
using Ocelot.Library.Infrastructure.UrlMatcher; using Ocelot.Library.Infrastructure.UrlMatcher;
@ -25,7 +26,7 @@ namespace Ocelot.UnitTests.UrlTemplateReplacer
[Fact] [Fact]
public void can_replace_no_template_variables() public void can_replace_no_template_variables()
{ {
this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(new List<TemplateVariableNameAndValue>(), new ReRoute("", "", "", "", false, "")))) this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(new List<TemplateVariableNameAndValue>(), new ReRouteBuilder().Build())))
.When(x => x.WhenIReplaceTheTemplateVariables()) .When(x => x.WhenIReplaceTheTemplateVariables())
.Then(x => x.ThenTheDownstreamUrlPathIsReturned("")) .Then(x => x.ThenTheDownstreamUrlPathIsReturned(""))
.BDDfy(); .BDDfy();
@ -34,7 +35,7 @@ namespace Ocelot.UnitTests.UrlTemplateReplacer
[Fact] [Fact]
public void can_replace_no_template_variables_with_slash() public void can_replace_no_template_variables_with_slash()
{ {
this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(new List<TemplateVariableNameAndValue>(), new ReRoute("/", "", "", "", false, "")))) this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(new List<TemplateVariableNameAndValue>(), new ReRouteBuilder().WithDownstreamTemplate("/").Build())))
.When(x => x.WhenIReplaceTheTemplateVariables()) .When(x => x.WhenIReplaceTheTemplateVariables())
.Then(x => x.ThenTheDownstreamUrlPathIsReturned("/")) .Then(x => x.ThenTheDownstreamUrlPathIsReturned("/"))
.BDDfy(); .BDDfy();
@ -43,7 +44,7 @@ namespace Ocelot.UnitTests.UrlTemplateReplacer
[Fact] [Fact]
public void can_replace_url_no_slash() public void can_replace_url_no_slash()
{ {
this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(new List<TemplateVariableNameAndValue>(), new ReRoute("api", "", "", "", false, "")))) this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(new List<TemplateVariableNameAndValue>(), new ReRouteBuilder().WithDownstreamTemplate("api").Build())))
.When(x => x.WhenIReplaceTheTemplateVariables()) .When(x => x.WhenIReplaceTheTemplateVariables())
.Then(x => x.ThenTheDownstreamUrlPathIsReturned("api")) .Then(x => x.ThenTheDownstreamUrlPathIsReturned("api"))
.BDDfy(); .BDDfy();
@ -52,7 +53,7 @@ namespace Ocelot.UnitTests.UrlTemplateReplacer
[Fact] [Fact]
public void can_replace_url_one_slash() public void can_replace_url_one_slash()
{ {
this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(new List<TemplateVariableNameAndValue>(), new ReRoute("api/", "", "", "", false, "")))) this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(new List<TemplateVariableNameAndValue>(), new ReRouteBuilder().WithDownstreamTemplate("api/").Build())))
.When(x => x.WhenIReplaceTheTemplateVariables()) .When(x => x.WhenIReplaceTheTemplateVariables())
.Then(x => x.ThenTheDownstreamUrlPathIsReturned("api/")) .Then(x => x.ThenTheDownstreamUrlPathIsReturned("api/"))
.BDDfy(); .BDDfy();
@ -61,7 +62,7 @@ namespace Ocelot.UnitTests.UrlTemplateReplacer
[Fact] [Fact]
public void can_replace_url_multiple_slash() public void can_replace_url_multiple_slash()
{ {
this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(new List<TemplateVariableNameAndValue>(), new ReRoute("api/product/products/", "", "", "", false, "")))) this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(new List<TemplateVariableNameAndValue>(), new ReRouteBuilder().WithDownstreamTemplate("api/product/products/").Build())))
.When(x => x.WhenIReplaceTheTemplateVariables()) .When(x => x.WhenIReplaceTheTemplateVariables())
.Then(x => x.ThenTheDownstreamUrlPathIsReturned("api/product/products/")) .Then(x => x.ThenTheDownstreamUrlPathIsReturned("api/product/products/"))
.BDDfy(); .BDDfy();
@ -75,7 +76,7 @@ namespace Ocelot.UnitTests.UrlTemplateReplacer
new TemplateVariableNameAndValue("{productId}", "1") new TemplateVariableNameAndValue("{productId}", "1")
}; };
this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(templateVariables, new ReRoute("productservice/products/{productId}/", "", "", "", false, "")))) this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(templateVariables, new ReRouteBuilder().WithDownstreamTemplate("productservice/products/{productId}/").Build())))
.When(x => x.WhenIReplaceTheTemplateVariables()) .When(x => x.WhenIReplaceTheTemplateVariables())
.Then(x => x.ThenTheDownstreamUrlPathIsReturned("productservice/products/1/")) .Then(x => x.ThenTheDownstreamUrlPathIsReturned("productservice/products/1/"))
.BDDfy(); .BDDfy();
@ -89,7 +90,7 @@ namespace Ocelot.UnitTests.UrlTemplateReplacer
new TemplateVariableNameAndValue("{productId}", "1") new TemplateVariableNameAndValue("{productId}", "1")
}; };
this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(templateVariables, new ReRoute("productservice/products/{productId}/variants", "", "", "", false, "")))) this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(templateVariables, new ReRouteBuilder().WithDownstreamTemplate("productservice/products/{productId}/variants").Build())))
.When(x => x.WhenIReplaceTheTemplateVariables()) .When(x => x.WhenIReplaceTheTemplateVariables())
.Then(x => x.ThenTheDownstreamUrlPathIsReturned("productservice/products/1/variants")) .Then(x => x.ThenTheDownstreamUrlPathIsReturned("productservice/products/1/variants"))
.BDDfy(); .BDDfy();
@ -104,7 +105,7 @@ namespace Ocelot.UnitTests.UrlTemplateReplacer
new TemplateVariableNameAndValue("{variantId}", "12") new TemplateVariableNameAndValue("{variantId}", "12")
}; };
this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(templateVariables, new ReRoute("productservice/products/{productId}/variants/{variantId}", "", "", "", false, "")))) this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(templateVariables, new ReRouteBuilder().WithDownstreamTemplate("productservice/products/{productId}/variants/{variantId}").Build())))
.When(x => x.WhenIReplaceTheTemplateVariables()) .When(x => x.WhenIReplaceTheTemplateVariables())
.Then(x => x.ThenTheDownstreamUrlPathIsReturned("productservice/products/1/variants/12")) .Then(x => x.ThenTheDownstreamUrlPathIsReturned("productservice/products/1/variants/12"))
.BDDfy(); .BDDfy();
@ -120,7 +121,7 @@ namespace Ocelot.UnitTests.UrlTemplateReplacer
new TemplateVariableNameAndValue("{categoryId}", "34") new TemplateVariableNameAndValue("{categoryId}", "34")
}; };
this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(templateVariables, new ReRoute("productservice/category/{categoryId}/products/{productId}/variants/{variantId}", "", "", "", false, "")))) this.Given(x => x.GivenThereIsAUrlMatch(new DownstreamRoute(templateVariables, new ReRouteBuilder().WithDownstreamTemplate("productservice/category/{categoryId}/products/{productId}/variants/{variantId}").Build())))
.When(x => x.WhenIReplaceTheTemplateVariables()) .When(x => x.WhenIReplaceTheTemplateVariables())
.Then(x => x.ThenTheDownstreamUrlPathIsReturned("productservice/category/34/products/1/variants/12")) .Then(x => x.ThenTheDownstreamUrlPathIsReturned("productservice/category/34/products/1/variants/12"))
.BDDfy(); .BDDfy();