diff --git a/src/Ocelot/Authorisation/ClaimsAuthoriser.cs b/src/Ocelot/Authorisation/ClaimsAuthoriser.cs index 29acb685..4db1045c 100644 --- a/src/Ocelot/Authorisation/ClaimsAuthoriser.cs +++ b/src/Ocelot/Authorisation/ClaimsAuthoriser.cs @@ -20,22 +20,22 @@ namespace Ocelot.Authorisation { foreach (var required in routeClaimsRequirement) { - var value = _claimsParser.GetValue(claimsPrincipal.Claims, required.Key, string.Empty, 0); + var values = _claimsParser.GetValuesByClaimType(claimsPrincipal.Claims, required.Key); - if (value.IsError) + if (values.IsError) { - return new ErrorResponse(value.Errors); + return new ErrorResponse(values.Errors); } - if (value.Data != null) + if (values.Data != null) { - var authorised = value.Data == required.Value; + var authorised = values.Data.Contains(required.Value); if (!authorised) { return new ErrorResponse(new List { new ClaimValueNotAuthorisedError( - $"claim value: {value.Data} is not the same as required value: {required.Value} for type: {required.Key}") + $"claim value: {values.Data} is not the same as required value: {required.Value} for type: {required.Key}") }); } } diff --git a/test/Ocelot.AcceptanceTests/AuthorisationTests.cs b/test/Ocelot.AcceptanceTests/AuthorisationTests.cs index b30f1db0..aaa4faff 100644 --- a/test/Ocelot.AcceptanceTests/AuthorisationTests.cs +++ b/test/Ocelot.AcceptanceTests/AuthorisationTests.cs @@ -235,6 +235,66 @@ namespace Ocelot.AcceptanceTests .BDDfy(); } + [Fact] + public void should_fix_issue_240() + { + var configuration = new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamPathTemplate = "/", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 51876, + } + }, + DownstreamScheme = "http", + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Get" }, + AuthenticationOptions = new FileAuthenticationOptions + { + AuthenticationProviderKey = "Test" + }, + RouteClaimsRequirement = + { + {"Role", "User"} + } + } + } + }; + + var users = new List + { + new TestUser + { + Username = "test", + Password = "test", + SubjectId = "registered|1231231", + Claims = new List + { + new Claim("Role", "AdminUser"), + new Claim("Role", "User") + }, + } + }; + + this.Given(x => x.GivenThereIsAnIdentityServerOn("http://localhost:51888", "api", AccessTokenType.Jwt, users)) + .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(_options, "Test")) + .And(x => _steps.GivenIHaveAddedATokenToMyRequest()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) + .BDDfy(); + } + private void GivenThereIsAServiceRunningOn(string url, int statusCode, string responseBody) { _servicebuilder = new WebHostBuilder() @@ -335,7 +395,75 @@ namespace Ocelot.AcceptanceTests _identityServerBuilder.Start(); _steps.VerifyIdentiryServerStarted(url); + } + private void GivenThereIsAnIdentityServerOn(string url, string apiName, AccessTokenType tokenType, List users) + { + _identityServerBuilder = new WebHostBuilder() + .UseUrls(url) + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseIISIntegration() + .UseUrls(url) + .ConfigureServices(services => + { + services.AddLogging(); + services.AddIdentityServer() + .AddDeveloperSigningCredential() + .AddInMemoryApiResources(new List + { + new ApiResource + { + Name = apiName, + Description = "My API", + Enabled = true, + DisplayName = "test", + 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", "UserType", "UserId", "Role" + } + }, + + }) + .AddInMemoryClients(new List + { + new Client + { + ClientId = "client", + AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, + ClientSecrets = new List {new Secret("secret".Sha256())}, + AllowedScopes = new List { apiName, "api.readOnly", "openid", "offline_access" }, + AccessTokenType = tokenType, + Enabled = true, + RequireClientSecret = false, + + } + }) + .AddTestUsers(users); + }) + .Configure(app => + { + app.UseIdentityServer(); + }) + .Build(); + + _identityServerBuilder.Start(); + + _steps.VerifyIdentiryServerStarted(url); } public void Dispose() diff --git a/test/Ocelot.UnitTests/Authorization/ClaimsAuthoriserTests.cs b/test/Ocelot.UnitTests/Authorization/ClaimsAuthoriserTests.cs index b435f7e7..0333268b 100644 --- a/test/Ocelot.UnitTests/Authorization/ClaimsAuthoriserTests.cs +++ b/test/Ocelot.UnitTests/Authorization/ClaimsAuthoriserTests.cs @@ -27,7 +27,25 @@ namespace Ocelot.UnitTests.Authorization { this.Given(x => x.GivenAClaimsPrincipal(new ClaimsPrincipal(new ClaimsIdentity(new List { - new Claim("UserType", "registered") + new Claim("UserType", "registered"), + + })))) + .And(x => x.GivenARouteClaimsRequirement(new Dictionary + { + {"UserType", "registered"} + })) + .When(x => x.WhenICallTheAuthoriser()) + .Then(x => x.ThenTheUserIsAuthorised()) + .BDDfy(); + } + + [Fact] + public void should_authorise_user_multiple_claims_of_same_type() + { + this.Given(x => x.GivenAClaimsPrincipal(new ClaimsPrincipal(new ClaimsIdentity(new List + { + new Claim("UserType", "guest"), + new Claim("UserType", "registered"), })))) .And(x => x.GivenARouteClaimsRequirement(new Dictionary {