diff --git a/src/Ocelot/Authentication/Handler/Creator/AuthenticationHandlerCreator.cs b/src/Ocelot/Authentication/Handler/Creator/AuthenticationHandlerCreator.cs index 1fcc50fbf..b4c1503a0 100644 --- a/src/Ocelot/Authentication/Handler/Creator/AuthenticationHandlerCreator.cs +++ b/src/Ocelot/Authentication/Handler/Creator/AuthenticationHandlerCreator.cs @@ -6,6 +6,8 @@ namespace Ocelot.Authentication.Handler.Creator { + using Ocelot.Configuration; + using AuthenticationOptions = Configuration.AuthenticationOptions; /// @@ -17,15 +19,31 @@ public Response Create(IApplicationBuilder app, AuthenticationO { var builder = app.New(); - builder.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions + if (authOptions.Provider.ToLower() == "jwt") + { + var authenticationConfig = authOptions.Config as JwtConfig; + + builder.UseJwtBearerAuthentication( + new JwtBearerOptions() + { + Authority = authenticationConfig.Authority, + Audience = authenticationConfig.Audience + }); + } + else { - Authority = authOptions.ProviderRootUrl, - ApiName = authOptions.ApiName, - RequireHttpsMetadata = authOptions.RequireHttps, - AllowedScopes = authOptions.AllowedScopes, - SupportedTokens = SupportedTokens.Both, - ApiSecret = authOptions.ApiSecret - }); + 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(); diff --git a/src/Ocelot/Authentication/Handler/SupportedAuthenticationProviders.cs b/src/Ocelot/Authentication/Handler/SupportedAuthenticationProviders.cs index 2a815ee02..5662fe401 100644 --- a/src/Ocelot/Authentication/Handler/SupportedAuthenticationProviders.cs +++ b/src/Ocelot/Authentication/Handler/SupportedAuthenticationProviders.cs @@ -2,6 +2,7 @@ { public enum SupportedAuthenticationProviders { - IdentityServer + IdentityServer, + Jwt } } diff --git a/src/Ocelot/Configuration/AuthenticationOptions.cs b/src/Ocelot/Configuration/AuthenticationOptions.cs index 223491b27..1c71d68b3 100644 --- a/src/Ocelot/Configuration/AuthenticationOptions.cs +++ b/src/Ocelot/Configuration/AuthenticationOptions.cs @@ -2,24 +2,52 @@ namespace Ocelot.Configuration { + using Newtonsoft.Json; + public class AuthenticationOptions { - public AuthenticationOptions(string provider, string providerRootUrl, string apiName, bool requireHttps, List allowedScopes, string apiSecret) + public AuthenticationOptions(string provider, List allowedScopes, IAuthenticationConfig config) { Provider = provider; + AllowedScopes = allowedScopes; + Config = config; + } + + public string Provider { get; private set; } + + public List 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; + ApiName = apiName; RequireHttps = requireHttps; - AllowedScopes = allowedScopes; ApiSecret = apiSecret; } - public string Provider { get; private set; } public string ProviderRootUrl { get; private set; } public string ApiName { get; private set; } public string ApiSecret { get; private set; } public bool RequireHttps { get; private set; } - public List 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 {} } diff --git a/src/Ocelot/Configuration/Builder/AuthenticationOptionsBuilder.cs b/src/Ocelot/Configuration/Builder/AuthenticationOptionsBuilder.cs index 0c6484894..ea43a23ea 100644 --- a/src/Ocelot/Configuration/Builder/AuthenticationOptionsBuilder.cs +++ b/src/Ocelot/Configuration/Builder/AuthenticationOptionsBuilder.cs @@ -6,51 +6,93 @@ public class AuthenticationOptionsBuilder { private string _provider; - private string _providerRootUrl; - private string _apiName; - private string _apiSecret; - private bool _requireHttps; + private List _allowedScopes; + private IAuthenticationConfig _identityServerConfig; + public AuthenticationOptionsBuilder WithProvider(string provider) { _provider = provider; return this; } - public AuthenticationOptionsBuilder WithProviderRootUrl(string providerRootUrl) + public AuthenticationOptionsBuilder WithAllowedScopes(List allowedScopes) + { + _allowedScopes = allowedScopes; + return this; + } + + public AuthenticationOptionsBuilder WithConfig(IAuthenticationConfig config) + { + _identityServerConfig = config; + return this; + } + + public AuthenticationOptions Build() + { + 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 AuthenticationOptionsBuilder WithApiName(string apiName) + public IdentityServerConfigBuilder WithApiName(string apiName) { _apiName = apiName; return this; } - public AuthenticationOptionsBuilder WithApiSecret(string apiSecret) + public IdentityServerConfigBuilder WithApiSecret(string apiSecret) { _apiSecret = apiSecret; return this; } - public AuthenticationOptionsBuilder WithRequireHttps(bool requireHttps) + public IdentityServerConfigBuilder WithRequireHttps(bool requireHttps) { _requireHttps = requireHttps; return this; } - public AuthenticationOptionsBuilder WithAllowedScopes(List allowedScopes) + public IdentityServerConfig Build() { - _allowedScopes = allowedScopes; + 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 AuthenticationOptions Build() + public JwtConfigBuilder WithAudience(string audience) + { + _audience = audience; + return this; + } + + public JwtConfig Build() { - return new AuthenticationOptions(_provider, _providerRootUrl, _apiName, _requireHttps, _allowedScopes, _apiSecret); + return new JwtConfig(_authority, _audience); } } } \ No newline at end of file diff --git a/src/Ocelot/Configuration/Creator/AuthenticationOptionsCreator.cs b/src/Ocelot/Configuration/Creator/AuthenticationOptionsCreator.cs index 583748e14..d5be4eee3 100644 --- a/src/Ocelot/Configuration/Creator/AuthenticationOptionsCreator.cs +++ b/src/Ocelot/Configuration/Creator/AuthenticationOptionsCreator.cs @@ -7,14 +7,13 @@ public class AuthenticationOptionsCreator : IAuthenticationOptionsCreator { public AuthenticationOptions Create(FileReRoute fileReRoute) { + var authenticationConfig = new ConfigCreator().Create(fileReRoute.AuthenticationOptions); + return new AuthenticationOptionsBuilder() - .WithProvider(fileReRoute.AuthenticationOptions?.Provider) - .WithProviderRootUrl(fileReRoute.AuthenticationOptions?.ProviderRootUrl) - .WithApiName(fileReRoute.AuthenticationOptions?.ApiName) - .WithRequireHttps(fileReRoute.AuthenticationOptions.RequireHttps) - .WithAllowedScopes(fileReRoute.AuthenticationOptions?.AllowedScopes) - .WithApiSecret(fileReRoute.AuthenticationOptions?.ApiSecret) - .Build(); - } + .WithProvider(fileReRoute.AuthenticationOptions?.Provider) + .WithAllowedScopes(fileReRoute.AuthenticationOptions?.AllowedScopes) + .WithConfig(authenticationConfig) + .Build(); + } } } \ No newline at end of file diff --git a/src/Ocelot/Configuration/Creator/ConfigCreator.cs b/src/Ocelot/Configuration/Creator/ConfigCreator.cs new file mode 100644 index 000000000..09c7ab16c --- /dev/null +++ b/src/Ocelot/Configuration/Creator/ConfigCreator.cs @@ -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(); + } + } +} \ No newline at end of file diff --git a/src/Ocelot/Configuration/File/FileAuthenticationOptions.cs b/src/Ocelot/Configuration/File/FileAuthenticationOptions.cs index 63e6347cc..31be2307e 100644 --- a/src/Ocelot/Configuration/File/FileAuthenticationOptions.cs +++ b/src/Ocelot/Configuration/File/FileAuthenticationOptions.cs @@ -6,14 +6,29 @@ public class FileAuthenticationOptions { public FileAuthenticationOptions() { - AllowedScopes = new List(); + AllowedScopes = new List(); + IdentityServerConfig = new FileIdentityServerConfig(); + JwtConfig = new FileJwtConfig(); } public string Provider { get; set; } + public List AllowedScopes { get; set; } + public FileIdentityServerConfig IdentityServerConfig { get; set; } + public FileJwtConfig JwtConfig { get; set; } + } + + public class FileIdentityServerConfig + { public string ProviderRootUrl { get; set; } public string ApiName { get; set; } public bool RequireHttps { get; set; } - public List AllowedScopes { get; set; } public string ApiSecret { get; set; } } + + public class FileJwtConfig + { + public string Authority { get; set; } + + public string Audience { get; set; } + } } diff --git a/src/Ocelot/Configuration/Repository/ConsulOcelotConfigurationRepository.cs b/src/Ocelot/Configuration/Repository/ConsulOcelotConfigurationRepository.cs index c54132742..9fd26f875 100644 --- a/src/Ocelot/Configuration/Repository/ConsulOcelotConfigurationRepository.cs +++ b/src/Ocelot/Configuration/Repository/ConsulOcelotConfigurationRepository.cs @@ -9,6 +9,8 @@ namespace Ocelot.Configuration.Repository { + using Ocelot.AcceptanceTests; + public class ConsulOcelotConfigurationRepository : IOcelotConfigurationRepository { private readonly ConsulClient _consul; @@ -48,7 +50,9 @@ public async Task> Get() var json = Encoding.UTF8.GetString(bytes); - var consulConfig = JsonConvert.DeserializeObject(json); + var settings = new JsonSerializerSettings(); + settings.Converters.Add(new AuthenticationConfigConverter()); + var consulConfig = JsonConvert.DeserializeObject(json, settings); return new OkResponse(consulConfig); } diff --git a/src/Ocelot/JsonConverters/AuthenticationConfigConverter.cs b/src/Ocelot/JsonConverters/AuthenticationConfigConverter.cs new file mode 100644 index 000000000..06699c28a --- /dev/null +++ b/src/Ocelot/JsonConverters/AuthenticationConfigConverter.cs @@ -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()) + { + case "Jwt": + setting = new JwtConfig( + jsonObject["Authority"].Value(), + jsonObject["Audience"].Value()); + break; + + default: + setting = new IdentityServerConfig( + jsonObject["ProviderRootUrl"].Value(), + jsonObject["ApiName"].Value(), + jsonObject["RequireHttps"].Value(), + jsonObject["ApiSecret"].Value()); + 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); + } + } + + +} diff --git a/test/Ocelot.AcceptanceTests/AuthenticationTests.cs b/test/Ocelot.AcceptanceTests/AuthenticationTests.cs index c0d143b91..641160343 100644 --- a/test/Ocelot.AcceptanceTests/AuthenticationTests.cs +++ b/test/Ocelot.AcceptanceTests/AuthenticationTests.cs @@ -37,237 +37,209 @@ public AuthenticationTests() [Fact] public void should_return_401_using_identity_server_access_token() { - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = _downstreamServicePath, - DownstreamPort = _downstreamServicePort, - DownstreamHost = _downstreamServiceHost, - DownstreamScheme = _downstreamServiceScheme, - UpstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "Post" }, - AuthenticationOptions = new FileAuthenticationOptions - { - AllowedScopes = new List(), - Provider = "IdentityServer", - ProviderRootUrl = _identityServerRootUrl, - RequireHttps = false, - ApiName = "api", - ApiSecret = "secret" - } - } - } - }; + var configuration = new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamPathTemplate = _downstreamServicePath, + DownstreamPort = _downstreamServicePort, + DownstreamHost = _downstreamServiceHost, + DownstreamScheme = _downstreamServiceScheme, + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Post" }, + AuthenticationOptions = new FileAuthenticationOptions + { + AllowedScopes = new List(), + Provider = "IdentityServer", + IdentityServerConfig = new FileIdentityServerConfig{ + ProviderRootUrl = _identityServerRootUrl, + RequireHttps = false, + ApiName = "api", + ApiSecret = "secret" + } + } + } + } + }; - this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", "api2", AccessTokenType.Jwt)) - .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] - public void should_return_401_using_identity_server_reference_token() - { - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = _downstreamServicePath, - DownstreamPort = _downstreamServicePort, - DownstreamHost = _downstreamServiceHost, - DownstreamScheme = _downstreamServiceScheme, - UpstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "Post" }, - AuthenticationOptions = new FileAuthenticationOptions - { - AllowedScopes = new List(), - 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(); + this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", "api2", AccessTokenType.Jwt)) + .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] public void should_return_response_200_using_identity_server() { - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = _downstreamServicePath, - DownstreamPort = _downstreamServicePort, - DownstreamHost = _downstreamServiceHost, - DownstreamScheme = _downstreamServiceScheme, - UpstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "Get" }, - AuthenticationOptions = new FileAuthenticationOptions - { - AllowedScopes = new List(), - Provider = "IdentityServer", - ProviderRootUrl = _identityServerRootUrl, - RequireHttps = false, - ApiName = "api", - ApiSecret = "secret" - } - } - } - }; + var configuration = new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamPathTemplate = _downstreamServicePath, + DownstreamPort = _downstreamServicePort, + DownstreamHost = _downstreamServiceHost, + DownstreamScheme = _downstreamServiceScheme, + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Get" }, + AuthenticationOptions = new FileAuthenticationOptions + { + AllowedScopes = new List(), + Provider = "IdentityServer", + IdentityServerConfig = new FileIdentityServerConfig{ + ProviderRootUrl = _identityServerRootUrl, + RequireHttps = false, + ApiName = "api", + ApiSecret = "secret" + } + } + } + } + }; - this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", "api2", AccessTokenType.Jwt)) - .And(x => x.GivenThereIsAServiceRunningOn(_downstreamServiceUrl, 200, "Hello from Laura")) - .And(x => _steps.GivenIHaveAToken(_identityServerRootUrl)) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunning()) - .And(x => _steps.GivenIHaveAddedATokenToMyRequest()) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) - .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) - .BDDfy(); + this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", "api2", AccessTokenType.Jwt)) + .And(x => x.GivenThereIsAServiceRunningOn(_downstreamServiceUrl, 200, "Hello from Laura")) + .And(x => _steps.GivenIHaveAToken(_identityServerRootUrl)) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning()) + .And(x => _steps.GivenIHaveAddedATokenToMyRequest()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) + .BDDfy(); } [Fact] public void should_return_response_401_using_identity_server_with_token_requested_for_other_api() { - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = _downstreamServicePath, - DownstreamPort = _downstreamServicePort, - DownstreamHost = _downstreamServiceHost, - DownstreamScheme = _downstreamServiceScheme, - UpstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "Get" }, - AuthenticationOptions = new FileAuthenticationOptions - { - AllowedScopes = new List(), - Provider = "IdentityServer", - ProviderRootUrl = _identityServerRootUrl, - RequireHttps = false, - ApiName = "api", - ApiSecret = "secret" - } - } - } - }; + var configuration = new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamPathTemplate = _downstreamServicePath, + DownstreamPort = _downstreamServicePort, + DownstreamHost = _downstreamServiceHost, + DownstreamScheme = _downstreamServiceScheme, + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Get" }, + AuthenticationOptions = new FileAuthenticationOptions + { + AllowedScopes = new List(), + Provider = "IdentityServer", + IdentityServerConfig = new FileIdentityServerConfig{ + ProviderRootUrl = _identityServerRootUrl, + RequireHttps = false, + ApiName = "api", + ApiSecret = "secret" + } + } + } + } + }; - this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", "api2", AccessTokenType.Jwt)) - .And(x => x.GivenThereIsAServiceRunningOn(_downstreamServiceUrl, 200, "Hello from Laura")) - .And(x => _steps.GivenIHaveATokenForApi2(_identityServerRootUrl)) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunning()) - .And(x => _steps.GivenIHaveAddedATokenToMyRequest()) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) - .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Unauthorized)) - .BDDfy(); + this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", "api2", AccessTokenType.Jwt)) + .And(x => x.GivenThereIsAServiceRunningOn(_downstreamServiceUrl, 200, "Hello from Laura")) + .And(x => _steps.GivenIHaveATokenForApi2(_identityServerRootUrl)) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning()) + .And(x => _steps.GivenIHaveAddedATokenToMyRequest()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Unauthorized)) + .BDDfy(); } [Fact] public void should_return_201_using_identity_server_access_token() { - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = _downstreamServicePath, - DownstreamPort = _downstreamServicePort, - DownstreamHost = _downstreamServiceHost, - DownstreamScheme = _downstreamServiceScheme, - UpstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "Post" }, + var configuration = new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamPathTemplate = _downstreamServicePath, + DownstreamPort = _downstreamServicePort, + DownstreamHost = _downstreamServiceHost, + DownstreamScheme = _downstreamServiceScheme, + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Post" }, - AuthenticationOptions = new FileAuthenticationOptions - { - AllowedScopes = new List(), - Provider = "IdentityServer", - ProviderRootUrl = _identityServerRootUrl, - RequireHttps = false, - ApiName = "api", - ApiSecret = "secret" - } - } - } - }; + AuthenticationOptions = new FileAuthenticationOptions + { + AllowedScopes = new List(), + Provider = "IdentityServer", + IdentityServerConfig = new FileIdentityServerConfig{ + ProviderRootUrl = _identityServerRootUrl, + RequireHttps = false, + ApiName = "api", + ApiSecret = "secret" + } + } + } + } + }; - this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", "api2", AccessTokenType.Jwt)) - .And(x => x.GivenThereIsAServiceRunningOn(_downstreamServiceUrl, 201, string.Empty)) - .And(x => _steps.GivenIHaveAToken(_identityServerRootUrl)) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunning()) - .And(x => _steps.GivenIHaveAddedATokenToMyRequest()) - .And(x => _steps.GivenThePostHasContent("postContent")) - .When(x => _steps.WhenIPostUrlOnTheApiGateway("/")) - .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Created)) - .BDDfy(); + this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", "api2", AccessTokenType.Jwt)) + .And(x => x.GivenThereIsAServiceRunningOn(_downstreamServiceUrl, 201, string.Empty)) + .And(x => _steps.GivenIHaveAToken(_identityServerRootUrl)) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning()) + .And(x => _steps.GivenIHaveAddedATokenToMyRequest()) + .And(x => _steps.GivenThePostHasContent("postContent")) + .When(x => _steps.WhenIPostUrlOnTheApiGateway("/")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Created)) + .BDDfy(); } [Fact] public void should_return_201_using_identity_server_reference_token() { - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = _downstreamServicePath, - DownstreamPort = _downstreamServicePort, - DownstreamHost = _downstreamServiceHost, - DownstreamScheme = _downstreamServiceScheme, - UpstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "Post" }, - AuthenticationOptions = new FileAuthenticationOptions - { - AllowedScopes = new List(), - Provider = "IdentityServer", - ProviderRootUrl = _identityServerRootUrl, - RequireHttps = false, - ApiName = "api", - ApiSecret = "secret" - } - } - } - }; + var configuration = new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamPathTemplate = _downstreamServicePath, + DownstreamPort = _downstreamServicePort, + DownstreamHost = _downstreamServiceHost, + DownstreamScheme = _downstreamServiceScheme, + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Post" }, + AuthenticationOptions = new FileAuthenticationOptions + { + AllowedScopes = new List(), + Provider = "IdentityServer", + IdentityServerConfig = new FileIdentityServerConfig{ + 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.GivenIHaveAToken(_identityServerRootUrl)) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunning()) - .And(x => _steps.GivenIHaveAddedATokenToMyRequest()) - .And(x => _steps.GivenThePostHasContent("postContent")) - .When(x => _steps.WhenIPostUrlOnTheApiGateway("/")) - .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Created)) - .BDDfy(); + this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", "api2", AccessTokenType.Reference)) + .And(x => x.GivenThereIsAServiceRunningOn(_downstreamServiceUrl, 201, string.Empty)) + .And(x => _steps.GivenIHaveAToken(_identityServerRootUrl)) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning()) + .And(x => _steps.GivenIHaveAddedATokenToMyRequest()) + .And(x => _steps.GivenThePostHasContent("postContent")) + .When(x => _steps.WhenIPostUrlOnTheApiGateway("/")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Created)) + .BDDfy(); } private void GivenThereIsAServiceRunningOn(string url, int statusCode, string responseBody) diff --git a/test/Ocelot.AcceptanceTests/AuthorisationTests.cs b/test/Ocelot.AcceptanceTests/AuthorisationTests.cs index 011bb679b..3eebd0002 100644 --- a/test/Ocelot.AcceptanceTests/AuthorisationTests.cs +++ b/test/Ocelot.AcceptanceTests/AuthorisationTests.cs @@ -31,191 +31,199 @@ public AuthorisationTests() [Fact] public void should_return_response_200_authorising_route() { - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/", - DownstreamPort = 51876, - DownstreamScheme = "http", - DownstreamHost = "localhost", - UpstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "Get" }, - AuthenticationOptions = new FileAuthenticationOptions - { - AllowedScopes = new List(), - Provider = "IdentityServer", - ProviderRootUrl = "http://localhost:51888", - RequireHttps = false, - ApiName = "api", - ApiSecret = "secret" - }, - AddHeadersToRequest = - { - {"CustomerId", "Claims[CustomerId] > value"}, - {"LocationId", "Claims[LocationId] > value"}, - {"UserType", "Claims[sub] > value[0] > |"}, - {"UserId", "Claims[sub] > value[1] > |"} - }, - AddClaimsToRequest = - { - {"CustomerId", "Claims[CustomerId] > value"}, - {"UserType", "Claims[sub] > value[0] > |"}, - {"UserId", "Claims[sub] > value[1] > |"} - }, - RouteClaimsRequirement = - { - {"UserType", "registered"} - } - } - } - }; + var configuration = new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamPathTemplate = "/", + DownstreamPort = 51876, + DownstreamScheme = "http", + DownstreamHost = "localhost", + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Get" }, + AuthenticationOptions = new FileAuthenticationOptions + { + AllowedScopes = new List(), + Provider = "IdentityServer", + IdentityServerConfig = new FileIdentityServerConfig{ + ProviderRootUrl = "http://localhost:51888", + RequireHttps = false, + ApiName = "api", + ApiSecret = "secret" + } + }, + AddHeadersToRequest = + { + {"CustomerId", "Claims[CustomerId] > value"}, + {"LocationId", "Claims[LocationId] > value"}, + {"UserType", "Claims[sub] > value[0] > |"}, + {"UserId", "Claims[sub] > value[1] > |"} + }, + AddClaimsToRequest = + { + {"CustomerId", "Claims[CustomerId] > value"}, + {"UserType", "Claims[sub] > value[0] > |"}, + {"UserId", "Claims[sub] > value[1] > |"} + }, + RouteClaimsRequirement = + { + {"UserType", "registered"} + } + } + } + }; - this.Given(x => x.GivenThereIsAnIdentityServerOn("http://localhost:51888", "api", AccessTokenType.Jwt)) - .And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51876", 200, "Hello from Laura")) - .And(x => _steps.GivenIHaveAToken("http://localhost:51888")) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunning()) - .And(x => _steps.GivenIHaveAddedATokenToMyRequest()) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) - .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) - .BDDfy(); + this.Given(x => x.GivenThereIsAnIdentityServerOn("http://localhost:51888", "api", AccessTokenType.Jwt)) + .And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51876", 200, "Hello from Laura")) + .And(x => _steps.GivenIHaveAToken("http://localhost:51888")) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning()) + .And(x => _steps.GivenIHaveAddedATokenToMyRequest()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) + .BDDfy(); } [Fact] public void should_return_response_403_authorising_route() { - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/", - DownstreamPort = 51876, - DownstreamScheme = "http", - DownstreamHost = "localhost", - UpstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "Get" }, - AuthenticationOptions = new FileAuthenticationOptions - { - AllowedScopes = new List(), + var configuration = new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamPathTemplate = "/", + DownstreamPort = 51876, + DownstreamScheme = "http", + DownstreamHost = "localhost", + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Get" }, + AuthenticationOptions = new FileAuthenticationOptions + { + AllowedScopes = new List(), Provider = "IdentityServer", - ProviderRootUrl = "http://localhost:51888", - RequireHttps = false, - ApiName = "api", - ApiSecret = "secret" - }, - AddHeadersToRequest = - { - {"CustomerId", "Claims[CustomerId] > value"}, - {"LocationId", "Claims[LocationId] > value"}, - {"UserType", "Claims[sub] > value[0] > |"}, - {"UserId", "Claims[sub] > value[1] > |"} - }, - AddClaimsToRequest = - { - {"CustomerId", "Claims[CustomerId] > value"}, - {"UserId", "Claims[sub] > value[1] > |"} - }, - RouteClaimsRequirement = - { - {"UserType", "registered"} - } - } - } - }; + IdentityServerConfig = new FileIdentityServerConfig{ + ProviderRootUrl = "http://localhost:51888", + RequireHttps = false, + ApiName = "api", + ApiSecret = "secret" + } + }, + AddHeadersToRequest = + { + {"CustomerId", "Claims[CustomerId] > value"}, + {"LocationId", "Claims[LocationId] > value"}, + {"UserType", "Claims[sub] > value[0] > |"}, + {"UserId", "Claims[sub] > value[1] > |"} + }, + AddClaimsToRequest = + { + {"CustomerId", "Claims[CustomerId] > value"}, + {"UserId", "Claims[sub] > value[1] > |"} + }, + RouteClaimsRequirement = + { + {"UserType", "registered"} + } + } + } + }; - this.Given(x => x.GivenThereIsAnIdentityServerOn("http://localhost:51888", "api", AccessTokenType.Jwt)) - .And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51876", 200, "Hello from Laura")) - .And(x => _steps.GivenIHaveAToken("http://localhost:51888")) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunning()) - .And(x => _steps.GivenIHaveAddedATokenToMyRequest()) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) - .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Forbidden)) - .BDDfy(); + this.Given(x => x.GivenThereIsAnIdentityServerOn("http://localhost:51888", "api", AccessTokenType.Jwt)) + .And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51876", 200, "Hello from Laura")) + .And(x => _steps.GivenIHaveAToken("http://localhost:51888")) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning()) + .And(x => _steps.GivenIHaveAddedATokenToMyRequest()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Forbidden)) + .BDDfy(); } [Fact] public void should_return_response_200_using_identity_server_with_allowed_scope() { - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/", - DownstreamPort = 51876, - DownstreamHost = "localhost", - DownstreamScheme = "http", - UpstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "Get" }, - AuthenticationOptions = new FileAuthenticationOptions - { - AllowedScopes = new List{ "api", "api.readOnly", "openid", "offline_access" }, - Provider = "IdentityServer", - ProviderRootUrl = "http://localhost:51888", - RequireHttps = false, - ApiName = "api", - ApiSecret = "secret" - } - } - } - }; + var configuration = new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamPathTemplate = "/", + DownstreamPort = 51876, + DownstreamHost = "localhost", + DownstreamScheme = "http", + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Get" }, + AuthenticationOptions = new FileAuthenticationOptions + { + AllowedScopes = new List{ "api", "api.readOnly", "openid", "offline_access" }, + Provider = "IdentityServer", + IdentityServerConfig = new FileIdentityServerConfig{ + ProviderRootUrl = "http://localhost:51888", + RequireHttps = false, + ApiName = "api", + ApiSecret = "secret" + } + } + } + } + }; - this.Given(x => x.GivenThereIsAnIdentityServerOn("http://localhost:51888", "api", AccessTokenType.Jwt)) - .And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51876", 200, "Hello from Laura")) - .And(x => _steps.GivenIHaveATokenForApiReadOnlyScope("http://localhost:51888")) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunning()) - .And(x => _steps.GivenIHaveAddedATokenToMyRequest()) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) - .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .BDDfy(); + this.Given(x => x.GivenThereIsAnIdentityServerOn("http://localhost:51888", "api", AccessTokenType.Jwt)) + .And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51876", 200, "Hello from Laura")) + .And(x => _steps.GivenIHaveATokenForApiReadOnlyScope("http://localhost:51888")) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning()) + .And(x => _steps.GivenIHaveAddedATokenToMyRequest()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .BDDfy(); } [Fact] public void should_return_response_403_using_identity_server_with_scope_not_allowed() { - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/", - DownstreamPort = 51876, - DownstreamHost = "localhost", - DownstreamScheme = "http", - UpstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "Get" }, - AuthenticationOptions = new FileAuthenticationOptions - { - AllowedScopes = new List{ "api", "openid", "offline_access" }, - Provider = "IdentityServer", - ProviderRootUrl = "http://localhost:51888", - RequireHttps = false, - ApiName = "api", - ApiSecret = "secret" - } - } - } - }; + var configuration = new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamPathTemplate = "/", + DownstreamPort = 51876, + DownstreamHost = "localhost", + DownstreamScheme = "http", + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Get" }, + AuthenticationOptions = new FileAuthenticationOptions + { + AllowedScopes = new List{ "api", "openid", "offline_access" }, + Provider = "IdentityServer", + IdentityServerConfig = new FileIdentityServerConfig{ + ProviderRootUrl = "http://localhost:51888", + RequireHttps = false, + ApiName = "api", + ApiSecret = "secret" + } + } + } + } + }; - this.Given(x => x.GivenThereIsAnIdentityServerOn("http://localhost:51888", "api", AccessTokenType.Jwt)) - .And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51876", 200, "Hello from Laura")) - .And(x => _steps.GivenIHaveATokenForApiReadOnlyScope("http://localhost:51888")) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunning()) - .And(x => _steps.GivenIHaveAddedATokenToMyRequest()) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) - .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Forbidden)) - .BDDfy(); + this.Given(x => x.GivenThereIsAnIdentityServerOn("http://localhost:51888", "api", AccessTokenType.Jwt)) + .And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51876", 200, "Hello from Laura")) + .And(x => _steps.GivenIHaveATokenForApiReadOnlyScope("http://localhost:51888")) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning()) + .And(x => _steps.GivenIHaveAddedATokenToMyRequest()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Forbidden)) + .BDDfy(); } private void GivenThereIsAServiceRunningOn(string url, int statusCode, string responseBody) diff --git a/test/Ocelot.AcceptanceTests/ClaimsToHeadersForwardingTests.cs b/test/Ocelot.AcceptanceTests/ClaimsToHeadersForwardingTests.cs index 88a294a08..babb520e6 100644 --- a/test/Ocelot.AcceptanceTests/ClaimsToHeadersForwardingTests.cs +++ b/test/Ocelot.AcceptanceTests/ClaimsToHeadersForwardingTests.cs @@ -33,63 +33,65 @@ public ClaimsToHeadersForwardingTests() [Fact] public void should_return_response_200_and_foward_claim_as_header() { - var user = new TestUser() - { - Username = "test", - Password = "test", - SubjectId = "registered|1231231", - Claims = new List - { - new Claim("CustomerId", "123"), - new Claim("LocationId", "1") - } - }; + var user = new TestUser() + { + Username = "test", + Password = "test", + SubjectId = "registered|1231231", + Claims = new List + { + new Claim("CustomerId", "123"), + new Claim("LocationId", "1") + } + }; - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/", - DownstreamPort = 52876, - DownstreamScheme = "http", - DownstreamHost = "localhost", - UpstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "Get" }, - AuthenticationOptions = new FileAuthenticationOptions - { + var configuration = new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamPathTemplate = "/", + DownstreamPort = 52876, + DownstreamScheme = "http", + DownstreamHost = "localhost", + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Get" }, + AuthenticationOptions = new FileAuthenticationOptions + { AllowedScopes = new List - { - "openid", "offline_access", "api" - }, - Provider = "IdentityServer", - ProviderRootUrl = "http://localhost:52888", - RequireHttps = false, - ApiName = "api", - ApiSecret = "secret", - }, - AddHeadersToRequest = - { - {"CustomerId", "Claims[CustomerId] > value"}, - {"LocationId", "Claims[LocationId] > value"}, - {"UserType", "Claims[sub] > value[0] > |"}, - {"UserId", "Claims[sub] > value[1] > |"} - } - } - } - }; + { + "openid", "offline_access", "api" + }, + Provider = "IdentityServer", + IdentityServerConfig = new FileIdentityServerConfig{ + ProviderRootUrl = "http://localhost:52888", + RequireHttps = false, + ApiName = "api", + ApiSecret = "secret" + } + }, + AddHeadersToRequest = + { + {"CustomerId", "Claims[CustomerId] > value"}, + {"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)) - .And(x => x.GivenThereIsAServiceRunningOn("http://localhost:52876", 200)) - .And(x => _steps.GivenIHaveAToken("http://localhost:52888")) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunning()) - .And(x => _steps.GivenIHaveAddedATokenToMyRequest()) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) - .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .And(x => _steps.ThenTheResponseBodyShouldBe("CustomerId: 123 LocationId: 1 UserType: registered UserId: 1231231")) - .BDDfy(); + this.Given(x => x.GivenThereIsAnIdentityServerOn("http://localhost:52888", "api", AccessTokenType.Jwt, user)) + .And(x => x.GivenThereIsAServiceRunningOn("http://localhost:52876", 200)) + .And(x => _steps.GivenIHaveAToken("http://localhost:52888")) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning()) + .And(x => _steps.GivenIHaveAddedATokenToMyRequest()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => _steps.ThenTheResponseBodyShouldBe("CustomerId: 123 LocationId: 1 UserType: registered UserId: 1231231")) + .BDDfy(); } private void GivenThereIsAServiceRunningOn(string url, int statusCode) diff --git a/test/Ocelot.AcceptanceTests/ClaimsToQueryStringForwardingTests.cs b/test/Ocelot.AcceptanceTests/ClaimsToQueryStringForwardingTests.cs index a6162c5f7..744cfab21 100644 --- a/test/Ocelot.AcceptanceTests/ClaimsToQueryStringForwardingTests.cs +++ b/test/Ocelot.AcceptanceTests/ClaimsToQueryStringForwardingTests.cs @@ -33,63 +33,65 @@ public ClaimsToQueryStringForwardingTests() [Fact] public void should_return_response_200_and_foward_claim_as_query_string() { - var user = new TestUser() - { - Username = "test", - Password = "test", - SubjectId = "registered|1231231", - Claims = new List - { - new Claim("CustomerId", "123"), - new Claim("LocationId", "1") - } - }; - - var configuration = new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - DownstreamPathTemplate = "/", - DownstreamPort = 57876, - DownstreamScheme = "http", - DownstreamHost = "localhost", - UpstreamPathTemplate = "/", - UpstreamHttpMethod = new List { "Get" }, - AuthenticationOptions = new FileAuthenticationOptions - { + var user = new TestUser() + { + Username = "test", + Password = "test", + SubjectId = "registered|1231231", + Claims = new List + { + new Claim("CustomerId", "123"), + new Claim("LocationId", "1") + } + }; + + var configuration = new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamPathTemplate = "/", + DownstreamPort = 57876, + DownstreamScheme = "http", + DownstreamHost = "localhost", + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Get" }, + AuthenticationOptions = new FileAuthenticationOptions + { AllowedScopes = new List - { - "openid", "offline_access", "api" - }, - Provider = "IdentityServer", - ProviderRootUrl = "http://localhost:57888", - RequireHttps = false, - ApiName = "api", - ApiSecret = "secret", - }, - AddQueriesToRequest = - { - {"CustomerId", "Claims[CustomerId] > value"}, - {"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)) - .And(x => x.GivenThereIsAServiceRunningOn("http://localhost:57876", 200)) - .And(x => _steps.GivenIHaveAToken("http://localhost:57888")) - .And(x => _steps.GivenThereIsAConfiguration(configuration)) - .And(x => _steps.GivenOcelotIsRunning()) - .And(x => _steps.GivenIHaveAddedATokenToMyRequest()) - .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) - .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) - .And(x => _steps.ThenTheResponseBodyShouldBe("CustomerId: 123 LocationId: 1 UserType: registered UserId: 1231231")) - .BDDfy(); + { + "openid", "offline_access", "api" + }, + Provider = "IdentityServer", + IdentityServerConfig = new FileIdentityServerConfig{ + ProviderRootUrl = "http://localhost:57888", + RequireHttps = false, + ApiName = "api", + ApiSecret = "secret" + } + }, + AddQueriesToRequest = + { + {"CustomerId", "Claims[CustomerId] > value"}, + {"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)) + .And(x => x.GivenThereIsAServiceRunningOn("http://localhost:57876", 200)) + .And(x => _steps.GivenIHaveAToken("http://localhost:57888")) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning()) + .And(x => _steps.GivenIHaveAddedATokenToMyRequest()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => _steps.ThenTheResponseBodyShouldBe("CustomerId: 123 LocationId: 1 UserType: registered UserId: 1231231")) + .BDDfy(); } private void GivenThereIsAServiceRunningOn(string url, int statusCode) diff --git a/test/Ocelot.AcceptanceTests/ConfigurationInConsulTests.cs b/test/Ocelot.AcceptanceTests/ConfigurationInConsulTests.cs index 0ab4cc6a4..afed11641 100644 --- a/test/Ocelot.AcceptanceTests/ConfigurationInConsulTests.cs +++ b/test/Ocelot.AcceptanceTests/ConfigurationInConsulTests.cs @@ -29,7 +29,6 @@ public ConfigurationInConsul() _steps = new Steps(); } - [Fact] public void should_return_response_200_with_simple_url() { @@ -105,7 +104,9 @@ private void GivenThereIsAFakeConsulServiceDiscoveryProvider(string url) var json = reader.ReadToEnd(); - _config = JsonConvert.DeserializeObject(json); + var settings = new JsonSerializerSettings(); + settings.Converters.Add(new AuthenticationConfigConverter()); + _config = JsonConvert.DeserializeObject(json, settings); var response = JsonConvert.SerializeObject(true); @@ -167,4 +168,4 @@ public void Dispose() _steps.Dispose(); } } -} +} \ No newline at end of file diff --git a/test/Ocelot.UnitTests/Authentication/AuthenticationHandlerFactoryTests.cs b/test/Ocelot.UnitTests/Authentication/AuthenticationHandlerFactoryTests.cs index 8bf53607b..55e1a05c4 100644 --- a/test/Ocelot.UnitTests/Authentication/AuthenticationHandlerFactoryTests.cs +++ b/test/Ocelot.UnitTests/Authentication/AuthenticationHandlerFactoryTests.cs @@ -31,17 +31,19 @@ public AuthenticationHandlerFactoryTests() _authenticationHandlerFactory = new AuthenticationHandlerFactory(_creator.Object); } - [Fact] - public void should_return_identity_server_access_token_handler() + [Theory] + [InlineData("IdentityServer")] + [InlineData("Jwt")] + public void should_return_access_token_handler(string provider) { var authenticationOptions = new AuthenticationOptionsBuilder() - .WithProvider("IdentityServer") + .WithProvider(provider) .Build(); this.Given(x => x.GivenTheAuthenticationOptionsAre(authenticationOptions)) .And(x => x.GivenTheCreatorReturns()) .When(x => x.WhenIGetFromTheFactory()) - .Then(x => x.ThenTheHandlerIsReturned("IdentityServer")) + .Then(x => x.ThenTheHandlerIsReturned(provider)) .BDDfy(); } diff --git a/test/Ocelot.UnitTests/Configuration/AuthenticationOptionsCreatorTests.cs b/test/Ocelot.UnitTests/Configuration/AuthenticationOptionsCreatorTests.cs index ee1292aa9..852452071 100644 --- a/test/Ocelot.UnitTests/Configuration/AuthenticationOptionsCreatorTests.cs +++ b/test/Ocelot.UnitTests/Configuration/AuthenticationOptionsCreatorTests.cs @@ -28,26 +28,68 @@ public void should_return_auth_options() AuthenticationOptions = new FileAuthenticationOptions { Provider = "Geoff", - ProviderRootUrl = "http://www.bbc.co.uk/", - ApiName = "Laura", - RequireHttps = true, - AllowedScopes = new List {"cheese"}, - ApiSecret = "secret" + IdentityServerConfig = new FileIdentityServerConfig() + { + ProviderRootUrl = "http://www.bbc.co.uk/", + ApiName = "Laura", + RequireHttps = true, + ApiSecret = "secret" + }, + AllowedScopes = new List { "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() .WithProvider(fileReRoute.AuthenticationOptions?.Provider) - .WithProviderRootUrl(fileReRoute.AuthenticationOptions?.ProviderRootUrl) - .WithApiName(fileReRoute.AuthenticationOptions?.ApiName) - .WithRequireHttps(fileReRoute.AuthenticationOptions.RequireHttps) .WithAllowedScopes(fileReRoute.AuthenticationOptions?.AllowedScopes) - .WithApiSecret(fileReRoute.AuthenticationOptions?.ApiSecret) + .WithConfig(authenticationConfig) .Build(); this.Given(x => x.GivenTheFollowing(fileReRoute)) .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 { "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(); } @@ -61,14 +103,31 @@ private void WhenICreateTheAuthenticationOptions() _result = _authOptionsCreator.Create(_fileReRoute); } - private void ThenTheFollowingIsReturned(AuthenticationOptions expected) + private void ThenTheFollowingJwtConfigIsReturned(AuthenticationOptions expected) + { + _result.AllowedScopes.ShouldBe(expected.AllowedScopes); + _result.Provider.ShouldBe(expected.Provider); + + var _resultSettings = _result.Config as JwtConfig; + var expectedSettngs = expected.Config as JwtConfig; + + _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); - _result.ProviderRootUrl.ShouldBe(expected.ProviderRootUrl); - _result.RequireHttps.ShouldBe(expected.RequireHttps); - _result.ApiName.ShouldBe(expected.ApiName); - _result.ApiSecret.ShouldBe(expected.ApiSecret); + + 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); } } } \ No newline at end of file diff --git a/test/Ocelot.UnitTests/Configuration/FileConfigurationCreatorTests.cs b/test/Ocelot.UnitTests/Configuration/FileConfigurationCreatorTests.cs index 386958c92..4cb53118b 100644 --- a/test/Ocelot.UnitTests/Configuration/FileConfigurationCreatorTests.cs +++ b/test/Ocelot.UnitTests/Configuration/FileConfigurationCreatorTests.cs @@ -18,6 +18,10 @@ namespace Ocelot.UnitTests.Configuration { + using System.Collections; + + using Ocelot.UnitTests.TestData; + public class FileConfigurationCreatorTests { private readonly Mock> _fileConfig; @@ -440,20 +444,18 @@ public void should_call_request_id_creator() .BDDfy(); } - [Fact] - public void should_create_with_headers_to_extract() + [Theory] + [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() .WithIsAuthenticated(true) .Build(); var authenticationOptions = new AuthenticationOptionsBuilder() - .WithProvider("IdentityServer") - .WithProviderRootUrl("http://localhost:51888") - .WithRequireHttps(false) - .WithApiSecret("secret") - .WithApiName("api") + .WithProvider(provider) .WithAllowedScopes(new List()) + .WithConfig(config) .Build(); var expected = new List @@ -470,59 +472,32 @@ public void should_create_with_headers_to_extract() .Build() }; - this.Given(x => x.GivenTheConfigIs(new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - UpstreamPathTemplate = "/api/products/{productId}", - DownstreamPathTemplate = "/products/{productId}", - UpstreamHttpMethod = new List { "Get" }, - ReRouteIsCaseSensitive = true, - AuthenticationOptions = new FileAuthenticationOptions - { - AllowedScopes= new List(), - Provider = "IdentityServer", - ProviderRootUrl = "http://localhost:51888", - RequireHttps = false, - ApiName= "api", - ApiSecret = "secret" - }, - AddHeadersToRequest = - { - {"CustomerId", "Claims[CustomerId] > value"}, - } - } - } - })) + this.Given(x => x.GivenTheConfigIs(fileConfig)) .And(x => x.GivenTheConfigIsValid()) .And(x => x.GivenTheAuthOptionsCreatorReturns(authenticationOptions)) .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions)) - .And(x => x.GivenTheClaimsToThingCreatorReturns(new List{new ClaimToThing("CustomerId", "CustomerId", "", 0)})) + .And(x => x.GivenTheClaimsToThingCreatorReturns(new List { new ClaimToThing("CustomerId", "CustomerId", "", 0) })) .And(x => x.GivenTheLoadBalancerFactoryReturns()) .When(x => x.WhenICreateTheConfig()) .Then(x => x.ThenTheReRoutesAre(expected)) - .And(x => x.ThenTheAuthenticationOptionsAre(expected)) + .And(x => x.ThenTheAuthenticationOptionsAre(provider, expected)) .And(x => x.ThenTheAuthOptionsCreatorIsCalledCorrectly()) .BDDfy(); } - [Fact] - public void should_create_with_authentication_properties() + [Theory] + [MemberData(nameof(AuthenticationConfigTestData.GetAuthenticationData), MemberType = typeof(AuthenticationConfigTestData))] + public void should_create_with_authentication_properties(string provider, IAuthenticationConfig config, FileConfiguration fileConfig) { var reRouteOptions = new ReRouteOptionsBuilder() .WithIsAuthenticated(true) .Build(); - var authenticationOptions = new AuthenticationOptionsBuilder() - .WithProvider("IdentityServer") - .WithProviderRootUrl("http://localhost:51888") - .WithRequireHttps(false) - .WithApiSecret("secret") - .WithApiName("api") - .WithAllowedScopes(new List()) - .Build(); + var authenticationOptions = new AuthenticationOptionsBuilder() + .WithProvider(provider) + .WithAllowedScopes(new List()) + .WithConfig(config) + .Build(); var expected = new List { @@ -534,35 +509,14 @@ public void should_create_with_authentication_properties() .Build() }; - this.Given(x => x.GivenTheConfigIs(new FileConfiguration - { - ReRoutes = new List - { - new FileReRoute - { - UpstreamPathTemplate = "/api/products/{productId}", - DownstreamPathTemplate = "/products/{productId}", - UpstreamHttpMethod = new List { "Get" }, - ReRouteIsCaseSensitive = true, - AuthenticationOptions = new FileAuthenticationOptions - { - AllowedScopes = new List(), - Provider = "IdentityServer", - ProviderRootUrl = "http://localhost:51888", - RequireHttps = false, - ApiName= "api", - ApiSecret = "secret" - } - } - } - })) + this.Given(x => x.GivenTheConfigIs(fileConfig)) .And(x => x.GivenTheConfigIsValid()) .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions)) .And(x => x.GivenTheAuthOptionsCreatorReturns(authenticationOptions)) .And(x => x.GivenTheLoadBalancerFactoryReturns()) .When(x => x.WhenICreateTheConfig()) .Then(x => x.ThenTheReRoutesAre(expected)) - .And(x => x.ThenTheAuthenticationOptionsAre(expected)) + .And(x => x.ThenTheAuthenticationOptionsAre(provider, expected)) .And(x => x.ThenTheAuthOptionsCreatorIsCalledCorrectly()) .BDDfy(); } @@ -633,7 +587,7 @@ private void ThenTheServiceConfigurationIs(ServiceProviderConfiguration expected } } - private void ThenTheAuthenticationOptionsAre(List expectedReRoutes) + private void ThenTheAuthenticationOptionsAre(string provider, List expectedReRoutes) { for (int i = 0; i < _config.Data.ReRoutes.Count; i++) { @@ -642,11 +596,25 @@ private void ThenTheAuthenticationOptionsAre(List expectedReRoutes) result.AllowedScopes.ShouldBe(expected.AllowedScopes); 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); + } } } diff --git a/test/Ocelot.UnitTests/TestData/AuthenticationConfigTestData.cs b/test/Ocelot.UnitTests/TestData/AuthenticationConfigTestData.cs new file mode 100644 index 000000000..5392a58dd --- /dev/null +++ b/test/Ocelot.UnitTests/TestData/AuthenticationConfigTestData.cs @@ -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 GetAuthenticationData() + { + yield return new object[] + { + "IdentityServer", + new IdentityServerConfigBuilder() + .WithRequireHttps(true) + .WithApiName("test") + .WithApiSecret("test") + .WithProviderRootUrl("test") + .Build(), + new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + UpstreamPathTemplate = "/api/products/{productId}", + DownstreamPathTemplate = "/products/{productId}", + UpstreamHttpMethod = new List { "Get" }, + ReRouteIsCaseSensitive = true, + AuthenticationOptions = new FileAuthenticationOptions + { + AllowedScopes = new List(), + 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 + { + new FileReRoute + { + UpstreamPathTemplate = "/api/products/{productId}", + DownstreamPathTemplate = "/products/{productId}", + UpstreamHttpMethod = new List { "Get" }, + ReRouteIsCaseSensitive = true, + AuthenticationOptions = new FileAuthenticationOptions + { + AllowedScopes = new List(), + Provider = "IdentityServer", + JwtConfig = new FileJwtConfig + { + Audience = "a", + Authority = "au" + } + }, + AddHeadersToRequest = + { + { "CustomerId", "Claims[CustomerId] > value" }, + } + } + } + } + }; + } + } +}