Merge pull request #110 from sharpn/develop

Adding JWT authentication for use with auth0
This commit is contained in:
Tom Pallister 2017-06-30 09:32:55 +01:00 committed by GitHub
commit 50ee9e20d8
18 changed files with 934 additions and 630 deletions

View File

@ -6,6 +6,8 @@ using Ocelot.Responses;
namespace Ocelot.Authentication.Handler.Creator namespace Ocelot.Authentication.Handler.Creator
{ {
using Ocelot.Configuration;
using AuthenticationOptions = Configuration.AuthenticationOptions; using AuthenticationOptions = Configuration.AuthenticationOptions;
/// <summary> /// <summary>
@ -17,15 +19,31 @@ namespace Ocelot.Authentication.Handler.Creator
{ {
var builder = app.New(); var builder = app.New();
builder.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions if (authOptions.Provider.ToLower() == "jwt")
{ {
Authority = authOptions.ProviderRootUrl, var authenticationConfig = authOptions.Config as JwtConfig;
ApiName = authOptions.ApiName,
RequireHttpsMetadata = authOptions.RequireHttps, builder.UseJwtBearerAuthentication(
AllowedScopes = authOptions.AllowedScopes, new JwtBearerOptions()
SupportedTokens = SupportedTokens.Both, {
ApiSecret = authOptions.ApiSecret Authority = authenticationConfig.Authority,
}); Audience = authenticationConfig.Audience
});
}
else
{
var authenticationConfig = authOptions.Config as IdentityServerConfig;
builder.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
{
Authority = authenticationConfig.ProviderRootUrl,
ApiName = authenticationConfig.ApiName,
RequireHttpsMetadata = authenticationConfig.RequireHttps,
AllowedScopes = authOptions.AllowedScopes,
SupportedTokens = SupportedTokens.Both,
ApiSecret = authenticationConfig.ApiSecret
});
}
var authenticationNext = builder.Build(); var authenticationNext = builder.Build();

View File

@ -2,6 +2,7 @@
{ {
public enum SupportedAuthenticationProviders public enum SupportedAuthenticationProviders
{ {
IdentityServer IdentityServer,
Jwt
} }
} }

View File

@ -2,24 +2,52 @@
namespace Ocelot.Configuration namespace Ocelot.Configuration
{ {
using Newtonsoft.Json;
public class AuthenticationOptions public class AuthenticationOptions
{ {
public AuthenticationOptions(string provider, string providerRootUrl, string apiName, bool requireHttps, List<string> allowedScopes, string apiSecret) public AuthenticationOptions(string provider, List<string> allowedScopes, IAuthenticationConfig config)
{ {
Provider = provider; Provider = provider;
ProviderRootUrl = providerRootUrl; AllowedScopes = allowedScopes;
ApiName = apiName; Config = config;
RequireHttps = requireHttps;
AllowedScopes = allowedScopes;
ApiSecret = apiSecret;
} }
public string Provider { get; private set; } public string Provider { get; private set; }
public List<string> AllowedScopes { get; private set; }
public IAuthenticationConfig Config { get; private set; }
}
public class IdentityServerConfig : IAuthenticationConfig
{
public IdentityServerConfig(string providerRootUrl, string apiName, bool requireHttps, string apiSecret)
{
ProviderRootUrl = providerRootUrl;
ApiName = apiName;
RequireHttps = requireHttps;
ApiSecret = apiSecret;
}
public string ProviderRootUrl { get; private set; } public string ProviderRootUrl { get; private set; }
public string ApiName { get; private set; } public string ApiName { get; private set; }
public string ApiSecret { get; private set; } public string ApiSecret { get; private set; }
public bool RequireHttps { get; private set; } public bool RequireHttps { get; private set; }
public List<string> AllowedScopes { get; private set; }
} }
public class JwtConfig : IAuthenticationConfig
{
public JwtConfig(string authority, string audience)
{
Audience = audience;
Authority = authority;
}
public string Audience { get; }
public string Authority { get; }
}
public interface IAuthenticationConfig {}
} }

View File

@ -6,51 +6,93 @@ namespace Ocelot.Configuration.Builder
{ {
private string _provider; private string _provider;
private string _providerRootUrl;
private string _apiName;
private string _apiSecret;
private bool _requireHttps;
private List<string> _allowedScopes; private List<string> _allowedScopes;
private IAuthenticationConfig _identityServerConfig;
public AuthenticationOptionsBuilder WithProvider(string provider) public AuthenticationOptionsBuilder WithProvider(string provider)
{ {
_provider = provider; _provider = provider;
return this; return this;
} }
public AuthenticationOptionsBuilder WithProviderRootUrl(string providerRootUrl)
{
_providerRootUrl = providerRootUrl;
return this;
}
public AuthenticationOptionsBuilder WithApiName(string apiName)
{
_apiName = apiName;
return this;
}
public AuthenticationOptionsBuilder WithApiSecret(string apiSecret)
{
_apiSecret = apiSecret;
return this;
}
public AuthenticationOptionsBuilder WithRequireHttps(bool requireHttps)
{
_requireHttps = requireHttps;
return this;
}
public AuthenticationOptionsBuilder WithAllowedScopes(List<string> allowedScopes) public AuthenticationOptionsBuilder WithAllowedScopes(List<string> allowedScopes)
{ {
_allowedScopes = allowedScopes; _allowedScopes = allowedScopes;
return this; return this;
} }
public AuthenticationOptionsBuilder WithConfig(IAuthenticationConfig config)
{
_identityServerConfig = config;
return this;
}
public AuthenticationOptions Build() public AuthenticationOptions Build()
{ {
return new AuthenticationOptions(_provider, _providerRootUrl, _apiName, _requireHttps, _allowedScopes, _apiSecret); return new AuthenticationOptions(_provider, _allowedScopes, _identityServerConfig);
}
}
public class IdentityServerConfigBuilder
{
private string _providerRootUrl;
private string _apiName;
private string _apiSecret;
private bool _requireHttps;
public IdentityServerConfigBuilder WithProviderRootUrl(string providerRootUrl)
{
_providerRootUrl = providerRootUrl;
return this;
}
public IdentityServerConfigBuilder WithApiName(string apiName)
{
_apiName = apiName;
return this;
}
public IdentityServerConfigBuilder WithApiSecret(string apiSecret)
{
_apiSecret = apiSecret;
return this;
}
public IdentityServerConfigBuilder WithRequireHttps(bool requireHttps)
{
_requireHttps = requireHttps;
return this;
}
public IdentityServerConfig Build()
{
return new IdentityServerConfig(_providerRootUrl, _apiName, _requireHttps, _apiSecret);
}
}
public class JwtConfigBuilder
{
public string _authority;
public string _audience;
public JwtConfigBuilder WithAuthority(string authority)
{
_authority = authority;
return this;
}
public JwtConfigBuilder WithAudience(string audience)
{
_audience = audience;
return this;
}
public JwtConfig Build()
{
return new JwtConfig(_authority, _audience);
} }
} }
} }

View File

@ -7,14 +7,13 @@ namespace Ocelot.Configuration.Creator
{ {
public AuthenticationOptions Create(FileReRoute fileReRoute) public AuthenticationOptions Create(FileReRoute fileReRoute)
{ {
var authenticationConfig = new ConfigCreator().Create(fileReRoute.AuthenticationOptions);
return new AuthenticationOptionsBuilder() return new AuthenticationOptionsBuilder()
.WithProvider(fileReRoute.AuthenticationOptions?.Provider) .WithProvider(fileReRoute.AuthenticationOptions?.Provider)
.WithProviderRootUrl(fileReRoute.AuthenticationOptions?.ProviderRootUrl) .WithAllowedScopes(fileReRoute.AuthenticationOptions?.AllowedScopes)
.WithApiName(fileReRoute.AuthenticationOptions?.ApiName) .WithConfig(authenticationConfig)
.WithRequireHttps(fileReRoute.AuthenticationOptions.RequireHttps) .Build();
.WithAllowedScopes(fileReRoute.AuthenticationOptions?.AllowedScopes)
.WithApiSecret(fileReRoute.AuthenticationOptions?.ApiSecret)
.Build();
} }
} }
} }

View File

@ -0,0 +1,35 @@
namespace Ocelot.Configuration.Creator
{
using Ocelot.Configuration.Builder;
using Ocelot.Configuration.File;
public class ConfigCreator
{
public IAuthenticationConfig Create(FileAuthenticationOptions authenticationOptions)
{
if (authenticationOptions.Provider == "Jwt")
{
return CreateJwt(authenticationOptions);
}
return CreateIdentityServer(authenticationOptions);
}
private JwtConfig CreateJwt(FileAuthenticationOptions authenticationOptions)
{
return new JwtConfigBuilder()
.WithAudience(authenticationOptions.JwtConfig?.Audience)
.WithAuthority(authenticationOptions.JwtConfig?.Authority)
.Build();
}
private IdentityServerConfig CreateIdentityServer(FileAuthenticationOptions authenticationOptions)
{
return new IdentityServerConfigBuilder()
.WithApiName(authenticationOptions.IdentityServerConfig?.ApiName)
.WithApiSecret(authenticationOptions.IdentityServerConfig?.ApiSecret)
.WithProviderRootUrl(authenticationOptions.IdentityServerConfig?.ProviderRootUrl)
.WithRequireHttps(authenticationOptions.IdentityServerConfig.RequireHttps).Build();
}
}
}

View File

@ -6,14 +6,29 @@ namespace Ocelot.Configuration.File
{ {
public FileAuthenticationOptions() public FileAuthenticationOptions()
{ {
AllowedScopes = new List<string>(); AllowedScopes = new List<string>();
IdentityServerConfig = new FileIdentityServerConfig();
JwtConfig = new FileJwtConfig();
} }
public string Provider { get; set; } public string Provider { get; set; }
public List<string> AllowedScopes { get; set; }
public FileIdentityServerConfig IdentityServerConfig { get; set; }
public FileJwtConfig JwtConfig { get; set; }
}
public class FileIdentityServerConfig
{
public string ProviderRootUrl { get; set; } public string ProviderRootUrl { get; set; }
public string ApiName { get; set; } public string ApiName { get; set; }
public bool RequireHttps { get; set; } public bool RequireHttps { get; set; }
public List<string> AllowedScopes { get; set; }
public string ApiSecret { get; set; } public string ApiSecret { get; set; }
} }
public class FileJwtConfig
{
public string Authority { get; set; }
public string Audience { get; set; }
}
} }

View File

@ -9,6 +9,8 @@ using Ocelot.ServiceDiscovery;
namespace Ocelot.Configuration.Repository namespace Ocelot.Configuration.Repository
{ {
using Ocelot.AcceptanceTests;
public class ConsulOcelotConfigurationRepository : IOcelotConfigurationRepository public class ConsulOcelotConfigurationRepository : IOcelotConfigurationRepository
{ {
private readonly ConsulClient _consul; private readonly ConsulClient _consul;
@ -48,7 +50,9 @@ namespace Ocelot.Configuration.Repository
var json = Encoding.UTF8.GetString(bytes); var json = Encoding.UTF8.GetString(bytes);
var consulConfig = JsonConvert.DeserializeObject<OcelotConfiguration>(json); var settings = new JsonSerializerSettings();
settings.Converters.Add(new AuthenticationConfigConverter());
var consulConfig = JsonConvert.DeserializeObject<OcelotConfiguration>(json, settings);
return new OkResponse<IOcelotConfiguration>(consulConfig); return new OkResponse<IOcelotConfiguration>(consulConfig);
} }

View File

@ -0,0 +1,59 @@
using System;
using Newtonsoft.Json;
using Ocelot.Configuration;
namespace Ocelot.AcceptanceTests
{
using Newtonsoft.Json.Linq;
public class AuthenticationConfigConverter : JsonConverter
{
public override bool CanWrite => false;
public override bool CanRead => true;
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new InvalidOperationException("Use default serialization.");
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var jsonObject = JObject.Load(reader);
var setting = default(IAuthenticationConfig);
if (jsonObject["Provider"] != null)
{
switch (jsonObject["Provider"].Value<string>())
{
case "Jwt":
setting = new JwtConfig(
jsonObject["Authority"].Value<string>(),
jsonObject["Audience"].Value<string>());
break;
default:
setting = new IdentityServerConfig(
jsonObject["ProviderRootUrl"].Value<string>(),
jsonObject["ApiName"].Value<string>(),
jsonObject["RequireHttps"].Value<bool>(),
jsonObject["ApiSecret"].Value<string>());
break;
}
}
else
{
setting = new IdentityServerConfig(string.Empty, string.Empty, false, string.Empty);
}
serializer.Populate(jsonObject.CreateReader(), setting);
return setting;
}
public override bool CanConvert(Type objectType)
{
return objectType == typeof(IAuthenticationConfig);
}
}
}

View File

@ -37,237 +37,209 @@ 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()
{ {
var configuration = new FileConfiguration var configuration = new FileConfiguration
{ {
ReRoutes = new List<FileReRoute> ReRoutes = new List<FileReRoute>
{ {
new FileReRoute new FileReRoute
{ {
DownstreamPathTemplate = _downstreamServicePath, DownstreamPathTemplate = _downstreamServicePath,
DownstreamPort = _downstreamServicePort, DownstreamPort = _downstreamServicePort,
DownstreamHost = _downstreamServiceHost, DownstreamHost = _downstreamServiceHost,
DownstreamScheme = _downstreamServiceScheme, DownstreamScheme = _downstreamServiceScheme,
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Post" }, UpstreamHttpMethod = new List<string> { "Post" },
AuthenticationOptions = new FileAuthenticationOptions AuthenticationOptions = new FileAuthenticationOptions
{ {
AllowedScopes = new List<string>(), AllowedScopes = new List<string>(),
Provider = "IdentityServer", Provider = "IdentityServer",
ProviderRootUrl = _identityServerRootUrl, IdentityServerConfig = new FileIdentityServerConfig{
RequireHttps = false, ProviderRootUrl = _identityServerRootUrl,
ApiName = "api", RequireHttps = false,
ApiSecret = "secret" ApiName = "api",
} ApiSecret = "secret"
} }
} }
}; }
}
};
this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", "api2", AccessTokenType.Jwt)) this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", "api2", AccessTokenType.Jwt))
.And(x => x.GivenThereIsAServiceRunningOn(_downstreamServiceUrl, 201, string.Empty)) .And(x => x.GivenThereIsAServiceRunningOn(_downstreamServiceUrl, 201, string.Empty))
.And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenOcelotIsRunning())
.And(x => _steps.GivenThePostHasContent("postContent")) .And(x => _steps.GivenThePostHasContent("postContent"))
.When(x => _steps.WhenIPostUrlOnTheApiGateway("/")) .When(x => _steps.WhenIPostUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Unauthorized)) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Unauthorized))
.BDDfy(); .BDDfy();
}
[Fact]
public void should_return_401_using_identity_server_reference_token()
{
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = _downstreamServicePath,
DownstreamPort = _downstreamServicePort,
DownstreamHost = _downstreamServiceHost,
DownstreamScheme = _downstreamServiceScheme,
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Post" },
AuthenticationOptions = new FileAuthenticationOptions
{
AllowedScopes = new List<string>(),
Provider = "IdentityServer",
ProviderRootUrl = _identityServerRootUrl,
RequireHttps = false,
ApiName = "api",
ApiSecret = "secret"
}
}
}
};
this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", "api2", AccessTokenType.Reference))
.And(x => x.GivenThereIsAServiceRunningOn(_downstreamServiceUrl, 201, string.Empty))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.And(x => _steps.GivenThePostHasContent("postContent"))
.When(x => _steps.WhenIPostUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Unauthorized))
.BDDfy();
} }
[Fact] [Fact]
public void should_return_response_200_using_identity_server() public void should_return_response_200_using_identity_server()
{ {
var configuration = new FileConfiguration var configuration = new FileConfiguration
{ {
ReRoutes = new List<FileReRoute> ReRoutes = new List<FileReRoute>
{ {
new FileReRoute new FileReRoute
{ {
DownstreamPathTemplate = _downstreamServicePath, DownstreamPathTemplate = _downstreamServicePath,
DownstreamPort = _downstreamServicePort, DownstreamPort = _downstreamServicePort,
DownstreamHost = _downstreamServiceHost, DownstreamHost = _downstreamServiceHost,
DownstreamScheme = _downstreamServiceScheme, DownstreamScheme = _downstreamServiceScheme,
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
AuthenticationOptions = new FileAuthenticationOptions AuthenticationOptions = new FileAuthenticationOptions
{ {
AllowedScopes = new List<string>(), AllowedScopes = new List<string>(),
Provider = "IdentityServer", Provider = "IdentityServer",
ProviderRootUrl = _identityServerRootUrl, IdentityServerConfig = new FileIdentityServerConfig{
RequireHttps = false, ProviderRootUrl = _identityServerRootUrl,
ApiName = "api", RequireHttps = false,
ApiSecret = "secret" ApiName = "api",
} ApiSecret = "secret"
} }
} }
}; }
}
};
this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", "api2", AccessTokenType.Jwt)) this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", "api2", AccessTokenType.Jwt))
.And(x => x.GivenThereIsAServiceRunningOn(_downstreamServiceUrl, 200, "Hello from Laura")) .And(x => x.GivenThereIsAServiceRunningOn(_downstreamServiceUrl, 200, "Hello from Laura"))
.And(x => _steps.GivenIHaveAToken(_identityServerRootUrl)) .And(x => _steps.GivenIHaveAToken(_identityServerRootUrl))
.And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenOcelotIsRunning())
.And(x => _steps.GivenIHaveAddedATokenToMyRequest()) .And(x => _steps.GivenIHaveAddedATokenToMyRequest())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
.BDDfy(); .BDDfy();
} }
[Fact] [Fact]
public void should_return_response_401_using_identity_server_with_token_requested_for_other_api() public void should_return_response_401_using_identity_server_with_token_requested_for_other_api()
{ {
var configuration = new FileConfiguration var configuration = new FileConfiguration
{ {
ReRoutes = new List<FileReRoute> ReRoutes = new List<FileReRoute>
{ {
new FileReRoute new FileReRoute
{ {
DownstreamPathTemplate = _downstreamServicePath, DownstreamPathTemplate = _downstreamServicePath,
DownstreamPort = _downstreamServicePort, DownstreamPort = _downstreamServicePort,
DownstreamHost = _downstreamServiceHost, DownstreamHost = _downstreamServiceHost,
DownstreamScheme = _downstreamServiceScheme, DownstreamScheme = _downstreamServiceScheme,
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
AuthenticationOptions = new FileAuthenticationOptions AuthenticationOptions = new FileAuthenticationOptions
{ {
AllowedScopes = new List<string>(), AllowedScopes = new List<string>(),
Provider = "IdentityServer", Provider = "IdentityServer",
ProviderRootUrl = _identityServerRootUrl, IdentityServerConfig = new FileIdentityServerConfig{
RequireHttps = false, ProviderRootUrl = _identityServerRootUrl,
ApiName = "api", RequireHttps = false,
ApiSecret = "secret" ApiName = "api",
} ApiSecret = "secret"
} }
} }
}; }
}
};
this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", "api2", AccessTokenType.Jwt)) this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", "api2", AccessTokenType.Jwt))
.And(x => x.GivenThereIsAServiceRunningOn(_downstreamServiceUrl, 200, "Hello from Laura")) .And(x => x.GivenThereIsAServiceRunningOn(_downstreamServiceUrl, 200, "Hello from Laura"))
.And(x => _steps.GivenIHaveATokenForApi2(_identityServerRootUrl)) .And(x => _steps.GivenIHaveATokenForApi2(_identityServerRootUrl))
.And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenOcelotIsRunning())
.And(x => _steps.GivenIHaveAddedATokenToMyRequest()) .And(x => _steps.GivenIHaveAddedATokenToMyRequest())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Unauthorized)) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Unauthorized))
.BDDfy(); .BDDfy();
} }
[Fact] [Fact]
public void should_return_201_using_identity_server_access_token() public void should_return_201_using_identity_server_access_token()
{ {
var configuration = new FileConfiguration var configuration = new FileConfiguration
{ {
ReRoutes = new List<FileReRoute> ReRoutes = new List<FileReRoute>
{ {
new FileReRoute new FileReRoute
{ {
DownstreamPathTemplate = _downstreamServicePath, DownstreamPathTemplate = _downstreamServicePath,
DownstreamPort = _downstreamServicePort, DownstreamPort = _downstreamServicePort,
DownstreamHost = _downstreamServiceHost, DownstreamHost = _downstreamServiceHost,
DownstreamScheme = _downstreamServiceScheme, DownstreamScheme = _downstreamServiceScheme,
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Post" }, UpstreamHttpMethod = new List<string> { "Post" },
AuthenticationOptions = new FileAuthenticationOptions AuthenticationOptions = new FileAuthenticationOptions
{ {
AllowedScopes = new List<string>(), AllowedScopes = new List<string>(),
Provider = "IdentityServer", Provider = "IdentityServer",
ProviderRootUrl = _identityServerRootUrl, IdentityServerConfig = new FileIdentityServerConfig{
RequireHttps = false, ProviderRootUrl = _identityServerRootUrl,
ApiName = "api", RequireHttps = false,
ApiSecret = "secret" ApiName = "api",
} ApiSecret = "secret"
} }
} }
}; }
}
};
this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", "api2", AccessTokenType.Jwt)) this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", "api2", AccessTokenType.Jwt))
.And(x => x.GivenThereIsAServiceRunningOn(_downstreamServiceUrl, 201, string.Empty)) .And(x => x.GivenThereIsAServiceRunningOn(_downstreamServiceUrl, 201, string.Empty))
.And(x => _steps.GivenIHaveAToken(_identityServerRootUrl)) .And(x => _steps.GivenIHaveAToken(_identityServerRootUrl))
.And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenOcelotIsRunning())
.And(x => _steps.GivenIHaveAddedATokenToMyRequest()) .And(x => _steps.GivenIHaveAddedATokenToMyRequest())
.And(x => _steps.GivenThePostHasContent("postContent")) .And(x => _steps.GivenThePostHasContent("postContent"))
.When(x => _steps.WhenIPostUrlOnTheApiGateway("/")) .When(x => _steps.WhenIPostUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Created)) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Created))
.BDDfy(); .BDDfy();
} }
[Fact] [Fact]
public void should_return_201_using_identity_server_reference_token() public void should_return_201_using_identity_server_reference_token()
{ {
var configuration = new FileConfiguration var configuration = new FileConfiguration
{ {
ReRoutes = new List<FileReRoute> ReRoutes = new List<FileReRoute>
{ {
new FileReRoute new FileReRoute
{ {
DownstreamPathTemplate = _downstreamServicePath, DownstreamPathTemplate = _downstreamServicePath,
DownstreamPort = _downstreamServicePort, DownstreamPort = _downstreamServicePort,
DownstreamHost = _downstreamServiceHost, DownstreamHost = _downstreamServiceHost,
DownstreamScheme = _downstreamServiceScheme, DownstreamScheme = _downstreamServiceScheme,
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Post" }, UpstreamHttpMethod = new List<string> { "Post" },
AuthenticationOptions = new FileAuthenticationOptions AuthenticationOptions = new FileAuthenticationOptions
{ {
AllowedScopes = new List<string>(), AllowedScopes = new List<string>(),
Provider = "IdentityServer", Provider = "IdentityServer",
ProviderRootUrl = _identityServerRootUrl, IdentityServerConfig = new FileIdentityServerConfig{
RequireHttps = false, ProviderRootUrl = _identityServerRootUrl,
ApiName = "api", RequireHttps = false,
ApiSecret = "secret" ApiName = "api",
} ApiSecret = "secret"
} }
} }
}; }
}
};
this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", "api2", AccessTokenType.Reference)) this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", "api2", AccessTokenType.Reference))
.And(x => x.GivenThereIsAServiceRunningOn(_downstreamServiceUrl, 201, string.Empty)) .And(x => x.GivenThereIsAServiceRunningOn(_downstreamServiceUrl, 201, string.Empty))
.And(x => _steps.GivenIHaveAToken(_identityServerRootUrl)) .And(x => _steps.GivenIHaveAToken(_identityServerRootUrl))
.And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenOcelotIsRunning())
.And(x => _steps.GivenIHaveAddedATokenToMyRequest()) .And(x => _steps.GivenIHaveAddedATokenToMyRequest())
.And(x => _steps.GivenThePostHasContent("postContent")) .And(x => _steps.GivenThePostHasContent("postContent"))
.When(x => _steps.WhenIPostUrlOnTheApiGateway("/")) .When(x => _steps.WhenIPostUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Created)) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Created))
.BDDfy(); .BDDfy();
} }
private void GivenThereIsAServiceRunningOn(string url, int statusCode, string responseBody) private void GivenThereIsAServiceRunningOn(string url, int statusCode, string responseBody)

View File

@ -31,191 +31,199 @@ namespace Ocelot.AcceptanceTests
[Fact] [Fact]
public void should_return_response_200_authorising_route() public void should_return_response_200_authorising_route()
{ {
var configuration = new FileConfiguration var configuration = new FileConfiguration
{ {
ReRoutes = new List<FileReRoute> ReRoutes = new List<FileReRoute>
{ {
new FileReRoute new FileReRoute
{ {
DownstreamPathTemplate = "/", DownstreamPathTemplate = "/",
DownstreamPort = 51876, DownstreamPort = 51876,
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHost = "localhost", DownstreamHost = "localhost",
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
AuthenticationOptions = new FileAuthenticationOptions AuthenticationOptions = new FileAuthenticationOptions
{ {
AllowedScopes = new List<string>(), AllowedScopes = new List<string>(),
Provider = "IdentityServer", Provider = "IdentityServer",
ProviderRootUrl = "http://localhost:51888", IdentityServerConfig = new FileIdentityServerConfig{
RequireHttps = false, ProviderRootUrl = "http://localhost:51888",
ApiName = "api", RequireHttps = false,
ApiSecret = "secret" ApiName = "api",
}, ApiSecret = "secret"
AddHeadersToRequest = }
{ },
{"CustomerId", "Claims[CustomerId] > value"}, AddHeadersToRequest =
{"LocationId", "Claims[LocationId] > value"}, {
{"UserType", "Claims[sub] > value[0] > |"}, {"CustomerId", "Claims[CustomerId] > value"},
{"UserId", "Claims[sub] > value[1] > |"} {"LocationId", "Claims[LocationId] > value"},
}, {"UserType", "Claims[sub] > value[0] > |"},
AddClaimsToRequest = {"UserId", "Claims[sub] > value[1] > |"}
{ },
{"CustomerId", "Claims[CustomerId] > value"}, AddClaimsToRequest =
{"UserType", "Claims[sub] > value[0] > |"}, {
{"UserId", "Claims[sub] > value[1] > |"} {"CustomerId", "Claims[CustomerId] > value"},
}, {"UserType", "Claims[sub] > value[0] > |"},
RouteClaimsRequirement = {"UserId", "Claims[sub] > value[1] > |"}
{ },
{"UserType", "registered"} RouteClaimsRequirement =
} {
} {"UserType", "registered"}
} }
}; }
}
};
this.Given(x => x.GivenThereIsAnIdentityServerOn("http://localhost:51888", "api", AccessTokenType.Jwt)) this.Given(x => x.GivenThereIsAnIdentityServerOn("http://localhost:51888", "api", AccessTokenType.Jwt))
.And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51876", 200, "Hello from Laura")) .And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51876", 200, "Hello from Laura"))
.And(x => _steps.GivenIHaveAToken("http://localhost:51888")) .And(x => _steps.GivenIHaveAToken("http://localhost:51888"))
.And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenOcelotIsRunning())
.And(x => _steps.GivenIHaveAddedATokenToMyRequest()) .And(x => _steps.GivenIHaveAddedATokenToMyRequest())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
.BDDfy(); .BDDfy();
} }
[Fact] [Fact]
public void should_return_response_403_authorising_route() public void should_return_response_403_authorising_route()
{ {
var configuration = new FileConfiguration var configuration = new FileConfiguration
{ {
ReRoutes = new List<FileReRoute> ReRoutes = new List<FileReRoute>
{ {
new FileReRoute new FileReRoute
{ {
DownstreamPathTemplate = "/", DownstreamPathTemplate = "/",
DownstreamPort = 51876, DownstreamPort = 51876,
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHost = "localhost", DownstreamHost = "localhost",
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
AuthenticationOptions = new FileAuthenticationOptions AuthenticationOptions = new FileAuthenticationOptions
{ {
AllowedScopes = new List<string>(), AllowedScopes = new List<string>(),
Provider = "IdentityServer", Provider = "IdentityServer",
ProviderRootUrl = "http://localhost:51888", IdentityServerConfig = new FileIdentityServerConfig{
RequireHttps = false, ProviderRootUrl = "http://localhost:51888",
ApiName = "api", RequireHttps = false,
ApiSecret = "secret" ApiName = "api",
}, ApiSecret = "secret"
AddHeadersToRequest = }
{ },
{"CustomerId", "Claims[CustomerId] > value"}, AddHeadersToRequest =
{"LocationId", "Claims[LocationId] > value"}, {
{"UserType", "Claims[sub] > value[0] > |"}, {"CustomerId", "Claims[CustomerId] > value"},
{"UserId", "Claims[sub] > value[1] > |"} {"LocationId", "Claims[LocationId] > value"},
}, {"UserType", "Claims[sub] > value[0] > |"},
AddClaimsToRequest = {"UserId", "Claims[sub] > value[1] > |"}
{ },
{"CustomerId", "Claims[CustomerId] > value"}, AddClaimsToRequest =
{"UserId", "Claims[sub] > value[1] > |"} {
}, {"CustomerId", "Claims[CustomerId] > value"},
RouteClaimsRequirement = {"UserId", "Claims[sub] > value[1] > |"}
{ },
{"UserType", "registered"} RouteClaimsRequirement =
} {
} {"UserType", "registered"}
} }
}; }
}
};
this.Given(x => x.GivenThereIsAnIdentityServerOn("http://localhost:51888", "api", AccessTokenType.Jwt)) this.Given(x => x.GivenThereIsAnIdentityServerOn("http://localhost:51888", "api", AccessTokenType.Jwt))
.And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51876", 200, "Hello from Laura")) .And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51876", 200, "Hello from Laura"))
.And(x => _steps.GivenIHaveAToken("http://localhost:51888")) .And(x => _steps.GivenIHaveAToken("http://localhost:51888"))
.And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenOcelotIsRunning())
.And(x => _steps.GivenIHaveAddedATokenToMyRequest()) .And(x => _steps.GivenIHaveAddedATokenToMyRequest())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Forbidden)) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Forbidden))
.BDDfy(); .BDDfy();
} }
[Fact] [Fact]
public void should_return_response_200_using_identity_server_with_allowed_scope() public void should_return_response_200_using_identity_server_with_allowed_scope()
{ {
var configuration = new FileConfiguration var configuration = new FileConfiguration
{ {
ReRoutes = new List<FileReRoute> ReRoutes = new List<FileReRoute>
{ {
new FileReRoute new FileReRoute
{ {
DownstreamPathTemplate = "/", DownstreamPathTemplate = "/",
DownstreamPort = 51876, DownstreamPort = 51876,
DownstreamHost = "localhost", DownstreamHost = "localhost",
DownstreamScheme = "http", DownstreamScheme = "http",
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
AuthenticationOptions = new FileAuthenticationOptions AuthenticationOptions = new FileAuthenticationOptions
{ {
AllowedScopes = new List<string>{ "api", "api.readOnly", "openid", "offline_access" }, AllowedScopes = new List<string>{ "api", "api.readOnly", "openid", "offline_access" },
Provider = "IdentityServer", Provider = "IdentityServer",
ProviderRootUrl = "http://localhost:51888", IdentityServerConfig = new FileIdentityServerConfig{
RequireHttps = false, ProviderRootUrl = "http://localhost:51888",
ApiName = "api", RequireHttps = false,
ApiSecret = "secret" ApiName = "api",
} ApiSecret = "secret"
} }
} }
}; }
}
};
this.Given(x => x.GivenThereIsAnIdentityServerOn("http://localhost:51888", "api", AccessTokenType.Jwt)) this.Given(x => x.GivenThereIsAnIdentityServerOn("http://localhost:51888", "api", AccessTokenType.Jwt))
.And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51876", 200, "Hello from Laura")) .And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51876", 200, "Hello from Laura"))
.And(x => _steps.GivenIHaveATokenForApiReadOnlyScope("http://localhost:51888")) .And(x => _steps.GivenIHaveATokenForApiReadOnlyScope("http://localhost:51888"))
.And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenOcelotIsRunning())
.And(x => _steps.GivenIHaveAddedATokenToMyRequest()) .And(x => _steps.GivenIHaveAddedATokenToMyRequest())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.BDDfy(); .BDDfy();
} }
[Fact] [Fact]
public void should_return_response_403_using_identity_server_with_scope_not_allowed() public void should_return_response_403_using_identity_server_with_scope_not_allowed()
{ {
var configuration = new FileConfiguration var configuration = new FileConfiguration
{ {
ReRoutes = new List<FileReRoute> ReRoutes = new List<FileReRoute>
{ {
new FileReRoute new FileReRoute
{ {
DownstreamPathTemplate = "/", DownstreamPathTemplate = "/",
DownstreamPort = 51876, DownstreamPort = 51876,
DownstreamHost = "localhost", DownstreamHost = "localhost",
DownstreamScheme = "http", DownstreamScheme = "http",
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
AuthenticationOptions = new FileAuthenticationOptions AuthenticationOptions = new FileAuthenticationOptions
{ {
AllowedScopes = new List<string>{ "api", "openid", "offline_access" }, AllowedScopes = new List<string>{ "api", "openid", "offline_access" },
Provider = "IdentityServer", Provider = "IdentityServer",
ProviderRootUrl = "http://localhost:51888", IdentityServerConfig = new FileIdentityServerConfig{
RequireHttps = false, ProviderRootUrl = "http://localhost:51888",
ApiName = "api", RequireHttps = false,
ApiSecret = "secret" ApiName = "api",
} ApiSecret = "secret"
} }
} }
}; }
}
};
this.Given(x => x.GivenThereIsAnIdentityServerOn("http://localhost:51888", "api", AccessTokenType.Jwt)) this.Given(x => x.GivenThereIsAnIdentityServerOn("http://localhost:51888", "api", AccessTokenType.Jwt))
.And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51876", 200, "Hello from Laura")) .And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51876", 200, "Hello from Laura"))
.And(x => _steps.GivenIHaveATokenForApiReadOnlyScope("http://localhost:51888")) .And(x => _steps.GivenIHaveATokenForApiReadOnlyScope("http://localhost:51888"))
.And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenOcelotIsRunning())
.And(x => _steps.GivenIHaveAddedATokenToMyRequest()) .And(x => _steps.GivenIHaveAddedATokenToMyRequest())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Forbidden)) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Forbidden))
.BDDfy(); .BDDfy();
} }
private void GivenThereIsAServiceRunningOn(string url, int statusCode, string responseBody) private void GivenThereIsAServiceRunningOn(string url, int statusCode, string responseBody)

View File

@ -33,63 +33,65 @@ namespace Ocelot.AcceptanceTests
[Fact] [Fact]
public void should_return_response_200_and_foward_claim_as_header() public void should_return_response_200_and_foward_claim_as_header()
{ {
var user = new TestUser() var user = new TestUser()
{ {
Username = "test", Username = "test",
Password = "test", Password = "test",
SubjectId = "registered|1231231", SubjectId = "registered|1231231",
Claims = new List<Claim> Claims = new List<Claim>
{ {
new Claim("CustomerId", "123"), new Claim("CustomerId", "123"),
new Claim("LocationId", "1") new Claim("LocationId", "1")
} }
}; };
var configuration = new FileConfiguration var configuration = new FileConfiguration
{ {
ReRoutes = new List<FileReRoute> ReRoutes = new List<FileReRoute>
{ {
new FileReRoute new FileReRoute
{ {
DownstreamPathTemplate = "/", DownstreamPathTemplate = "/",
DownstreamPort = 52876, DownstreamPort = 52876,
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHost = "localhost", DownstreamHost = "localhost",
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
AuthenticationOptions = new FileAuthenticationOptions AuthenticationOptions = new FileAuthenticationOptions
{ {
AllowedScopes = new List<string> AllowedScopes = new List<string>
{ {
"openid", "offline_access", "api" "openid", "offline_access", "api"
}, },
Provider = "IdentityServer", Provider = "IdentityServer",
ProviderRootUrl = "http://localhost:52888", IdentityServerConfig = new FileIdentityServerConfig{
RequireHttps = false, ProviderRootUrl = "http://localhost:52888",
ApiName = "api", RequireHttps = false,
ApiSecret = "secret", ApiName = "api",
}, ApiSecret = "secret"
AddHeadersToRequest = }
{ },
{"CustomerId", "Claims[CustomerId] > value"}, AddHeadersToRequest =
{"LocationId", "Claims[LocationId] > value"}, {
{"UserType", "Claims[sub] > value[0] > |"}, {"CustomerId", "Claims[CustomerId] > value"},
{"UserId", "Claims[sub] > value[1] > |"} {"LocationId", "Claims[LocationId] > value"},
} {"UserType", "Claims[sub] > value[0] > |"},
} {"UserId", "Claims[sub] > value[1] > |"}
} }
}; }
}
};
this.Given(x => x.GivenThereIsAnIdentityServerOn("http://localhost:52888", "api", AccessTokenType.Jwt, user)) this.Given(x => x.GivenThereIsAnIdentityServerOn("http://localhost:52888", "api", AccessTokenType.Jwt, user))
.And(x => x.GivenThereIsAServiceRunningOn("http://localhost:52876", 200)) .And(x => x.GivenThereIsAServiceRunningOn("http://localhost:52876", 200))
.And(x => _steps.GivenIHaveAToken("http://localhost:52888")) .And(x => _steps.GivenIHaveAToken("http://localhost:52888"))
.And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenOcelotIsRunning())
.And(x => _steps.GivenIHaveAddedATokenToMyRequest()) .And(x => _steps.GivenIHaveAddedATokenToMyRequest())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("CustomerId: 123 LocationId: 1 UserType: registered UserId: 1231231")) .And(x => _steps.ThenTheResponseBodyShouldBe("CustomerId: 123 LocationId: 1 UserType: registered UserId: 1231231"))
.BDDfy(); .BDDfy();
} }
private void GivenThereIsAServiceRunningOn(string url, int statusCode) private void GivenThereIsAServiceRunningOn(string url, int statusCode)

View File

@ -33,63 +33,65 @@ namespace Ocelot.AcceptanceTests
[Fact] [Fact]
public void should_return_response_200_and_foward_claim_as_query_string() public void should_return_response_200_and_foward_claim_as_query_string()
{ {
var user = new TestUser() var user = new TestUser()
{ {
Username = "test", Username = "test",
Password = "test", Password = "test",
SubjectId = "registered|1231231", SubjectId = "registered|1231231",
Claims = new List<Claim> Claims = new List<Claim>
{ {
new Claim("CustomerId", "123"), new Claim("CustomerId", "123"),
new Claim("LocationId", "1") new Claim("LocationId", "1")
} }
}; };
var configuration = new FileConfiguration var configuration = new FileConfiguration
{ {
ReRoutes = new List<FileReRoute> ReRoutes = new List<FileReRoute>
{ {
new FileReRoute new FileReRoute
{ {
DownstreamPathTemplate = "/", DownstreamPathTemplate = "/",
DownstreamPort = 57876, DownstreamPort = 57876,
DownstreamScheme = "http", DownstreamScheme = "http",
DownstreamHost = "localhost", DownstreamHost = "localhost",
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
AuthenticationOptions = new FileAuthenticationOptions AuthenticationOptions = new FileAuthenticationOptions
{ {
AllowedScopes = new List<string> AllowedScopes = new List<string>
{ {
"openid", "offline_access", "api" "openid", "offline_access", "api"
}, },
Provider = "IdentityServer", Provider = "IdentityServer",
ProviderRootUrl = "http://localhost:57888", IdentityServerConfig = new FileIdentityServerConfig{
RequireHttps = false, ProviderRootUrl = "http://localhost:57888",
ApiName = "api", RequireHttps = false,
ApiSecret = "secret", ApiName = "api",
}, ApiSecret = "secret"
AddQueriesToRequest = }
{ },
{"CustomerId", "Claims[CustomerId] > value"}, AddQueriesToRequest =
{"LocationId", "Claims[LocationId] > value"}, {
{"UserType", "Claims[sub] > value[0] > |"}, {"CustomerId", "Claims[CustomerId] > value"},
{"UserId", "Claims[sub] > value[1] > |"} {"LocationId", "Claims[LocationId] > value"},
} {"UserType", "Claims[sub] > value[0] > |"},
} {"UserId", "Claims[sub] > value[1] > |"}
} }
}; }
}
};
this.Given(x => x.GivenThereIsAnIdentityServerOn("http://localhost:57888", "api", AccessTokenType.Jwt, user)) this.Given(x => x.GivenThereIsAnIdentityServerOn("http://localhost:57888", "api", AccessTokenType.Jwt, user))
.And(x => x.GivenThereIsAServiceRunningOn("http://localhost:57876", 200)) .And(x => x.GivenThereIsAServiceRunningOn("http://localhost:57876", 200))
.And(x => _steps.GivenIHaveAToken("http://localhost:57888")) .And(x => _steps.GivenIHaveAToken("http://localhost:57888"))
.And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning()) .And(x => _steps.GivenOcelotIsRunning())
.And(x => _steps.GivenIHaveAddedATokenToMyRequest()) .And(x => _steps.GivenIHaveAddedATokenToMyRequest())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("CustomerId: 123 LocationId: 1 UserType: registered UserId: 1231231")) .And(x => _steps.ThenTheResponseBodyShouldBe("CustomerId: 123 LocationId: 1 UserType: registered UserId: 1231231"))
.BDDfy(); .BDDfy();
} }
private void GivenThereIsAServiceRunningOn(string url, int statusCode) private void GivenThereIsAServiceRunningOn(string url, int statusCode)

View File

@ -29,7 +29,6 @@ namespace Ocelot.AcceptanceTests
_steps = new Steps(); _steps = new Steps();
} }
[Fact] [Fact]
public void should_return_response_200_with_simple_url() public void should_return_response_200_with_simple_url()
{ {
@ -105,7 +104,9 @@ namespace Ocelot.AcceptanceTests
var json = reader.ReadToEnd(); var json = reader.ReadToEnd();
_config = JsonConvert.DeserializeObject<OcelotConfiguration>(json); var settings = new JsonSerializerSettings();
settings.Converters.Add(new AuthenticationConfigConverter());
_config = JsonConvert.DeserializeObject<OcelotConfiguration>(json, settings);
var response = JsonConvert.SerializeObject(true); var response = JsonConvert.SerializeObject(true);

View File

@ -31,17 +31,19 @@ namespace Ocelot.UnitTests.Authentication
_authenticationHandlerFactory = new AuthenticationHandlerFactory(_creator.Object); _authenticationHandlerFactory = new AuthenticationHandlerFactory(_creator.Object);
} }
[Fact] [Theory]
public void should_return_identity_server_access_token_handler() [InlineData("IdentityServer")]
[InlineData("Jwt")]
public void should_return_access_token_handler(string provider)
{ {
var authenticationOptions = new AuthenticationOptionsBuilder() var authenticationOptions = new AuthenticationOptionsBuilder()
.WithProvider("IdentityServer") .WithProvider(provider)
.Build(); .Build();
this.Given(x => x.GivenTheAuthenticationOptionsAre(authenticationOptions)) this.Given(x => x.GivenTheAuthenticationOptionsAre(authenticationOptions))
.And(x => x.GivenTheCreatorReturns()) .And(x => x.GivenTheCreatorReturns())
.When(x => x.WhenIGetFromTheFactory()) .When(x => x.WhenIGetFromTheFactory())
.Then(x => x.ThenTheHandlerIsReturned("IdentityServer")) .Then(x => x.ThenTheHandlerIsReturned(provider))
.BDDfy(); .BDDfy();
} }

View File

@ -28,26 +28,68 @@ namespace Ocelot.UnitTests.Configuration
AuthenticationOptions = new FileAuthenticationOptions AuthenticationOptions = new FileAuthenticationOptions
{ {
Provider = "Geoff", Provider = "Geoff",
ProviderRootUrl = "http://www.bbc.co.uk/", IdentityServerConfig = new FileIdentityServerConfig()
ApiName = "Laura", {
RequireHttps = true, ProviderRootUrl = "http://www.bbc.co.uk/",
AllowedScopes = new List<string> {"cheese"}, ApiName = "Laura",
ApiSecret = "secret" RequireHttps = true,
ApiSecret = "secret"
},
AllowedScopes = new List<string> { "cheese" },
} }
}; };
var authenticationConfig = new IdentityServerConfigBuilder()
.WithProviderRootUrl(fileReRoute.AuthenticationOptions?.IdentityServerConfig?.ProviderRootUrl)
.WithApiName(fileReRoute.AuthenticationOptions?.IdentityServerConfig?.ApiName)
.WithRequireHttps(fileReRoute.AuthenticationOptions.IdentityServerConfig.RequireHttps)
.WithApiSecret(fileReRoute.AuthenticationOptions?.IdentityServerConfig?.ApiSecret)
.Build();
var expected = new AuthenticationOptionsBuilder() var expected = new AuthenticationOptionsBuilder()
.WithProvider(fileReRoute.AuthenticationOptions?.Provider) .WithProvider(fileReRoute.AuthenticationOptions?.Provider)
.WithProviderRootUrl(fileReRoute.AuthenticationOptions?.ProviderRootUrl)
.WithApiName(fileReRoute.AuthenticationOptions?.ApiName)
.WithRequireHttps(fileReRoute.AuthenticationOptions.RequireHttps)
.WithAllowedScopes(fileReRoute.AuthenticationOptions?.AllowedScopes) .WithAllowedScopes(fileReRoute.AuthenticationOptions?.AllowedScopes)
.WithApiSecret(fileReRoute.AuthenticationOptions?.ApiSecret) .WithConfig(authenticationConfig)
.Build(); .Build();
this.Given(x => x.GivenTheFollowing(fileReRoute)) this.Given(x => x.GivenTheFollowing(fileReRoute))
.When(x => x.WhenICreateTheAuthenticationOptions()) .When(x => x.WhenICreateTheAuthenticationOptions())
.Then(x => x.ThenTheFollowingIsReturned(expected)) .Then(x => x.ThenTheFollowingIdentityServerConfigIsReturned(expected))
.BDDfy();
}
[Fact]
public void should_return_Jwt_auth_options()
{
var fileReRoute = new FileReRoute()
{
AuthenticationOptions = new FileAuthenticationOptions
{
Provider = "Jwt",
JwtConfig = new FileJwtConfig()
{
Audience = "Audience",
Authority = "Authority"
},
AllowedScopes = new List<string> { "cheese" }
}
};
var authenticationConfig = new JwtConfigBuilder()
.WithAudience(fileReRoute.AuthenticationOptions?.JwtConfig?.Audience)
.WithAuthority(fileReRoute.AuthenticationOptions?.JwtConfig?.Authority)
.Build();
var expected = new AuthenticationOptionsBuilder()
.WithProvider(fileReRoute.AuthenticationOptions?.Provider)
.WithAllowedScopes(fileReRoute.AuthenticationOptions?.AllowedScopes)
.WithConfig(authenticationConfig)
.Build();
this.Given(x => x.GivenTheFollowing(fileReRoute))
.When(x => x.WhenICreateTheAuthenticationOptions())
.Then(x => x.ThenTheFollowingJwtConfigIsReturned(expected))
.BDDfy(); .BDDfy();
} }
@ -61,14 +103,31 @@ namespace Ocelot.UnitTests.Configuration
_result = _authOptionsCreator.Create(_fileReRoute); _result = _authOptionsCreator.Create(_fileReRoute);
} }
private void ThenTheFollowingIsReturned(AuthenticationOptions expected) private void ThenTheFollowingJwtConfigIsReturned(AuthenticationOptions expected)
{ {
_result.AllowedScopes.ShouldBe(expected.AllowedScopes); _result.AllowedScopes.ShouldBe(expected.AllowedScopes);
_result.Provider.ShouldBe(expected.Provider); _result.Provider.ShouldBe(expected.Provider);
_result.ProviderRootUrl.ShouldBe(expected.ProviderRootUrl);
_result.RequireHttps.ShouldBe(expected.RequireHttps); var _resultSettings = _result.Config as JwtConfig;
_result.ApiName.ShouldBe(expected.ApiName); var expectedSettngs = expected.Config as JwtConfig;
_result.ApiSecret.ShouldBe(expected.ApiSecret);
_resultSettings.Audience.ShouldBe(expectedSettngs.Audience);
_resultSettings.Authority.ShouldBe(expectedSettngs.Authority);
}
private void ThenTheFollowingIdentityServerConfigIsReturned(AuthenticationOptions expected)
{
_result.AllowedScopes.ShouldBe(expected.AllowedScopes);
_result.Provider.ShouldBe(expected.Provider);
var _resultSettings = _result.Config as IdentityServerConfig;
var expectedSettngs = expected.Config as IdentityServerConfig;
_resultSettings.ProviderRootUrl.ShouldBe(expectedSettngs.ProviderRootUrl);
_resultSettings.RequireHttps.ShouldBe(expectedSettngs.RequireHttps);
_resultSettings.ApiName.ShouldBe(expectedSettngs.ApiName);
_resultSettings.ApiSecret.ShouldBe(expectedSettngs.ApiSecret);
} }
} }
} }

View File

@ -18,6 +18,10 @@ using Xunit;
namespace Ocelot.UnitTests.Configuration namespace Ocelot.UnitTests.Configuration
{ {
using System.Collections;
using Ocelot.UnitTests.TestData;
public class FileConfigurationCreatorTests public class FileConfigurationCreatorTests
{ {
private readonly Mock<IOptions<FileConfiguration>> _fileConfig; private readonly Mock<IOptions<FileConfiguration>> _fileConfig;
@ -440,20 +444,18 @@ namespace Ocelot.UnitTests.Configuration
.BDDfy(); .BDDfy();
} }
[Fact] [Theory]
public void should_create_with_headers_to_extract() [MemberData(nameof(AuthenticationConfigTestData.GetAuthenticationData), MemberType = typeof(AuthenticationConfigTestData))]
public void should_create_with_headers_to_extract(string provider, IAuthenticationConfig config, FileConfiguration fileConfig)
{ {
var reRouteOptions = new ReRouteOptionsBuilder() var reRouteOptions = new ReRouteOptionsBuilder()
.WithIsAuthenticated(true) .WithIsAuthenticated(true)
.Build(); .Build();
var authenticationOptions = new AuthenticationOptionsBuilder() var authenticationOptions = new AuthenticationOptionsBuilder()
.WithProvider("IdentityServer") .WithProvider(provider)
.WithProviderRootUrl("http://localhost:51888")
.WithRequireHttps(false)
.WithApiSecret("secret")
.WithApiName("api")
.WithAllowedScopes(new List<string>()) .WithAllowedScopes(new List<string>())
.WithConfig(config)
.Build(); .Build();
var expected = new List<ReRoute> var expected = new List<ReRoute>
@ -470,59 +472,32 @@ namespace Ocelot.UnitTests.Configuration
.Build() .Build()
}; };
this.Given(x => x.GivenTheConfigIs(new FileConfiguration this.Given(x => x.GivenTheConfigIs(fileConfig))
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
UpstreamPathTemplate = "/api/products/{productId}",
DownstreamPathTemplate = "/products/{productId}",
UpstreamHttpMethod = new List<string> { "Get" },
ReRouteIsCaseSensitive = true,
AuthenticationOptions = new FileAuthenticationOptions
{
AllowedScopes= new List<string>(),
Provider = "IdentityServer",
ProviderRootUrl = "http://localhost:51888",
RequireHttps = false,
ApiName= "api",
ApiSecret = "secret"
},
AddHeadersToRequest =
{
{"CustomerId", "Claims[CustomerId] > value"},
}
}
}
}))
.And(x => x.GivenTheConfigIsValid()) .And(x => x.GivenTheConfigIsValid())
.And(x => x.GivenTheAuthOptionsCreatorReturns(authenticationOptions)) .And(x => x.GivenTheAuthOptionsCreatorReturns(authenticationOptions))
.And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions)) .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions))
.And(x => x.GivenTheClaimsToThingCreatorReturns(new List<ClaimToThing>{new ClaimToThing("CustomerId", "CustomerId", "", 0)})) .And(x => x.GivenTheClaimsToThingCreatorReturns(new List<ClaimToThing> { new ClaimToThing("CustomerId", "CustomerId", "", 0) }))
.And(x => x.GivenTheLoadBalancerFactoryReturns()) .And(x => x.GivenTheLoadBalancerFactoryReturns())
.When(x => x.WhenICreateTheConfig()) .When(x => x.WhenICreateTheConfig())
.Then(x => x.ThenTheReRoutesAre(expected)) .Then(x => x.ThenTheReRoutesAre(expected))
.And(x => x.ThenTheAuthenticationOptionsAre(expected)) .And(x => x.ThenTheAuthenticationOptionsAre(provider, expected))
.And(x => x.ThenTheAuthOptionsCreatorIsCalledCorrectly()) .And(x => x.ThenTheAuthOptionsCreatorIsCalledCorrectly())
.BDDfy(); .BDDfy();
} }
[Fact] [Theory]
public void should_create_with_authentication_properties() [MemberData(nameof(AuthenticationConfigTestData.GetAuthenticationData), MemberType = typeof(AuthenticationConfigTestData))]
public void should_create_with_authentication_properties(string provider, IAuthenticationConfig config, FileConfiguration fileConfig)
{ {
var reRouteOptions = new ReRouteOptionsBuilder() var reRouteOptions = new ReRouteOptionsBuilder()
.WithIsAuthenticated(true) .WithIsAuthenticated(true)
.Build(); .Build();
var authenticationOptions = new AuthenticationOptionsBuilder() var authenticationOptions = new AuthenticationOptionsBuilder()
.WithProvider("IdentityServer") .WithProvider(provider)
.WithProviderRootUrl("http://localhost:51888") .WithAllowedScopes(new List<string>())
.WithRequireHttps(false) .WithConfig(config)
.WithApiSecret("secret") .Build();
.WithApiName("api")
.WithAllowedScopes(new List<string>())
.Build();
var expected = new List<ReRoute> var expected = new List<ReRoute>
{ {
@ -534,35 +509,14 @@ namespace Ocelot.UnitTests.Configuration
.Build() .Build()
}; };
this.Given(x => x.GivenTheConfigIs(new FileConfiguration this.Given(x => x.GivenTheConfigIs(fileConfig))
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
UpstreamPathTemplate = "/api/products/{productId}",
DownstreamPathTemplate = "/products/{productId}",
UpstreamHttpMethod = new List<string> { "Get" },
ReRouteIsCaseSensitive = true,
AuthenticationOptions = new FileAuthenticationOptions
{
AllowedScopes = new List<string>(),
Provider = "IdentityServer",
ProviderRootUrl = "http://localhost:51888",
RequireHttps = false,
ApiName= "api",
ApiSecret = "secret"
}
}
}
}))
.And(x => x.GivenTheConfigIsValid()) .And(x => x.GivenTheConfigIsValid())
.And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions)) .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions))
.And(x => x.GivenTheAuthOptionsCreatorReturns(authenticationOptions)) .And(x => x.GivenTheAuthOptionsCreatorReturns(authenticationOptions))
.And(x => x.GivenTheLoadBalancerFactoryReturns()) .And(x => x.GivenTheLoadBalancerFactoryReturns())
.When(x => x.WhenICreateTheConfig()) .When(x => x.WhenICreateTheConfig())
.Then(x => x.ThenTheReRoutesAre(expected)) .Then(x => x.ThenTheReRoutesAre(expected))
.And(x => x.ThenTheAuthenticationOptionsAre(expected)) .And(x => x.ThenTheAuthenticationOptionsAre(provider, expected))
.And(x => x.ThenTheAuthOptionsCreatorIsCalledCorrectly()) .And(x => x.ThenTheAuthOptionsCreatorIsCalledCorrectly())
.BDDfy(); .BDDfy();
} }
@ -633,7 +587,7 @@ namespace Ocelot.UnitTests.Configuration
} }
} }
private void ThenTheAuthenticationOptionsAre(List<ReRoute> expectedReRoutes) private void ThenTheAuthenticationOptionsAre(string provider, List<ReRoute> expectedReRoutes)
{ {
for (int i = 0; i < _config.Data.ReRoutes.Count; i++) for (int i = 0; i < _config.Data.ReRoutes.Count; i++)
{ {
@ -642,11 +596,25 @@ namespace Ocelot.UnitTests.Configuration
result.AllowedScopes.ShouldBe(expected.AllowedScopes); result.AllowedScopes.ShouldBe(expected.AllowedScopes);
result.Provider.ShouldBe(expected.Provider); result.Provider.ShouldBe(expected.Provider);
result.ProviderRootUrl.ShouldBe(expected.ProviderRootUrl);
result.RequireHttps.ShouldBe(expected.RequireHttps);
result.ApiName.ShouldBe(expected.ApiName);
result.ApiSecret.ShouldBe(expected.ApiSecret);
if (provider.ToLower() == "identityserver")
{
var config = result.Config as IdentityServerConfig;
var expectedConfig = expected.Config as IdentityServerConfig;
config.ProviderRootUrl.ShouldBe(expectedConfig.ProviderRootUrl);
config.RequireHttps.ShouldBe(expectedConfig.RequireHttps);
config.ApiName.ShouldBe(expectedConfig.ApiName);
config.ApiSecret.ShouldBe(expectedConfig.ApiSecret);
}
else
{
var config = result.Config as JwtConfig;
var expectedConfig = expected.Config as JwtConfig;
config.Audience.ShouldBe(expectedConfig.Audience);
config.Authority.ShouldBe(expectedConfig.Authority);
}
} }
} }

View File

@ -0,0 +1,89 @@
namespace Ocelot.UnitTests.TestData
{
using System.Collections.Generic;
using Ocelot.Configuration.Builder;
using Ocelot.Configuration.File;
public class AuthenticationConfigTestData
{
public static IEnumerable<object[]> GetAuthenticationData()
{
yield return new object[]
{
"IdentityServer",
new IdentityServerConfigBuilder()
.WithRequireHttps(true)
.WithApiName("test")
.WithApiSecret("test")
.WithProviderRootUrl("test")
.Build(),
new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
UpstreamPathTemplate = "/api/products/{productId}",
DownstreamPathTemplate = "/products/{productId}",
UpstreamHttpMethod = new List<string> { "Get" },
ReRouteIsCaseSensitive = true,
AuthenticationOptions = new FileAuthenticationOptions
{
AllowedScopes = new List<string>(),
Provider = "IdentityServer",
IdentityServerConfig = new FileIdentityServerConfig
{
ProviderRootUrl = "http://localhost:51888",
RequireHttps = false,
ApiName = "api",
ApiSecret = "secret"
}
},
AddHeadersToRequest =
{
{ "CustomerId", "Claims[CustomerId] > value" },
}
}
}
}
};
yield return new object[]
{
"Jwt",
new JwtConfigBuilder()
.WithAudience("a")
.WithAuthority("au")
.Build(),
new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
UpstreamPathTemplate = "/api/products/{productId}",
DownstreamPathTemplate = "/products/{productId}",
UpstreamHttpMethod = new List<string> { "Get" },
ReRouteIsCaseSensitive = true,
AuthenticationOptions = new FileAuthenticationOptions
{
AllowedScopes = new List<string>(),
Provider = "IdentityServer",
JwtConfig = new FileJwtConfig
{
Audience = "a",
Authority = "au"
}
},
AddHeadersToRequest =
{
{ "CustomerId", "Claims[CustomerId] > value" },
}
}
}
}
};
}
}
}