diff --git a/test/Ocelot.AcceptanceTests/AuthenticationTests.cs b/test/Ocelot.AcceptanceTests/AuthenticationTests.cs index 32e72a18..40ff7047 100644 --- a/test/Ocelot.AcceptanceTests/AuthenticationTests.cs +++ b/test/Ocelot.AcceptanceTests/AuthenticationTests.cs @@ -51,18 +51,18 @@ namespace Ocelot.AcceptanceTests UpstreamHttpMethod = new List { "Post" }, AuthenticationOptions = new FileAuthenticationOptions { - AllowedScopes = new List(), + AllowedScopes = new List(), Provider = "IdentityServer", ProviderRootUrl = _identityServerRootUrl, RequireHttps = false, - ApiName = "api", + ApiName = "api", ApiSecret = "secret" } } } }; - this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", AccessTokenType.Jwt)) + 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()) @@ -89,18 +89,18 @@ namespace Ocelot.AcceptanceTests UpstreamHttpMethod = new List { "Post" }, AuthenticationOptions = new FileAuthenticationOptions { - AllowedScopes = new List(), + AllowedScopes = new List(), Provider = "IdentityServer", ProviderRootUrl = _identityServerRootUrl, RequireHttps = false, - ApiName = "api", + ApiName = "api", ApiSecret = "secret" } } } }; - this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", AccessTokenType.Reference)) + 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()) @@ -127,18 +127,18 @@ namespace Ocelot.AcceptanceTests UpstreamHttpMethod = new List { "Get" }, AuthenticationOptions = new FileAuthenticationOptions { - AllowedScopes = new List(), + AllowedScopes = new List(), Provider = "IdentityServer", ProviderRootUrl = _identityServerRootUrl, RequireHttps = false, - ApiName = "api", + ApiName = "api", ApiSecret = "secret" } } } }; - this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", AccessTokenType.Jwt)) + 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)) @@ -150,6 +150,84 @@ namespace Ocelot.AcceptanceTests .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" + } + } + } + }; + + 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_response_403_using_identity_server_with_scope_not_allowed() + { + 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{ "api", "openid", "offline_access" }, + Provider = "IdentityServer", + 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.GivenIHaveATokenForApiReadOnlyScope(_identityServerRootUrl)) + .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_201_using_identity_server_access_token() { @@ -168,18 +246,18 @@ namespace Ocelot.AcceptanceTests AuthenticationOptions = new FileAuthenticationOptions { - AllowedScopes = new List(), + AllowedScopes = new List(), Provider = "IdentityServer", ProviderRootUrl = _identityServerRootUrl, RequireHttps = false, - ApiName = "api", + ApiName = "api", ApiSecret = "secret" } } } }; - this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", AccessTokenType.Jwt)) + 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)) @@ -208,18 +286,18 @@ namespace Ocelot.AcceptanceTests UpstreamHttpMethod = new List { "Post" }, AuthenticationOptions = new FileAuthenticationOptions { - AllowedScopes = new List(), + AllowedScopes = new List(), Provider = "IdentityServer", ProviderRootUrl = _identityServerRootUrl, RequireHttps = false, - ApiName = "api", + ApiName = "api", ApiSecret = "secret" } } } }; - this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", AccessTokenType.Reference)) + 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)) @@ -252,7 +330,7 @@ namespace Ocelot.AcceptanceTests _servicebuilder.Start(); } - private void GivenThereIsAnIdentityServerOn(string url, string apiName, AccessTokenType tokenType) + private void GivenThereIsAnIdentityServerOn(string url, string apiName, string api2Name, AccessTokenType tokenType) { _identityServerBuilder = new WebHostBuilder() .UseUrls(url) @@ -276,6 +354,32 @@ namespace Ocelot.AcceptanceTests Scopes = new List() { new Scope("api"), + new Scope("api.readOnly"), + new Scope("openid"), + new Scope("offline_access") + }, + ApiSecrets = new List() + { + new Secret + { + Value = "secret".Sha256() + } + }, + UserClaims = new List() + { + "CustomerId", "LocationId" + } + }, + new ApiResource + { + Name = api2Name, + Description = "My second API", + Enabled = true, + DisplayName = "second test", + Scopes = new List() + { + new Scope("api2"), + new Scope("api2.readOnly"), new Scope("openid"), new Scope("offline_access") }, @@ -299,7 +403,7 @@ namespace Ocelot.AcceptanceTests ClientId = "client", AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, ClientSecrets = new List {new Secret("secret".Sha256())}, - AllowedScopes = new List { apiName, "openid", "offline_access" }, + AllowedScopes = new List { apiName, api2Name, "api.readOnly", "openid", "offline_access" }, AccessTokenType = tokenType, Enabled = true, RequireClientSecret = false diff --git a/test/Ocelot.AcceptanceTests/Steps.cs b/test/Ocelot.AcceptanceTests/Steps.cs index 79a586ac..cb408037 100644 --- a/test/Ocelot.AcceptanceTests/Steps.cs +++ b/test/Ocelot.AcceptanceTests/Steps.cs @@ -199,6 +199,52 @@ namespace Ocelot.AcceptanceTests } } + public void GivenIHaveATokenForApiReadOnlyScope(string url) + { + var tokenUrl = $"{url}/connect/token"; + var formData = new List> + { + new KeyValuePair("client_id", "client"), + new KeyValuePair("client_secret", "secret"), + new KeyValuePair("scope", "api.readOnly"), + new KeyValuePair("username", "test"), + new KeyValuePair("password", "test"), + new KeyValuePair("grant_type", "password") + }; + var content = new FormUrlEncodedContent(formData); + + using (var httpClient = new HttpClient()) + { + var response = httpClient.PostAsync(tokenUrl, content).Result; + var responseContent = response.Content.ReadAsStringAsync().Result; + response.EnsureSuccessStatusCode(); + _token = JsonConvert.DeserializeObject(responseContent); + } + } + + public void GivenIHaveATokenForApi2(string url) + { + var tokenUrl = $"{url}/connect/token"; + var formData = new List> + { + new KeyValuePair("client_id", "client"), + new KeyValuePair("client_secret", "secret"), + new KeyValuePair("scope", "api2"), + new KeyValuePair("username", "test"), + new KeyValuePair("password", "test"), + new KeyValuePair("grant_type", "password") + }; + var content = new FormUrlEncodedContent(formData); + + using (var httpClient = new HttpClient()) + { + var response = httpClient.PostAsync(tokenUrl, content).Result; + var responseContent = response.Content.ReadAsStringAsync().Result; + response.EnsureSuccessStatusCode(); + _token = JsonConvert.DeserializeObject(responseContent); + } + } + public void GivenIHaveAnOcelotToken(string adminPath) { var tokenUrl = $"{adminPath}/connect/token";