diff --git a/services/src/main/java/org/keycloak/authorization/common/KeycloakIdentity.java b/services/src/main/java/org/keycloak/authorization/common/KeycloakIdentity.java index 654193d0b8..58962b51d4 100644 --- a/services/src/main/java/org/keycloak/authorization/common/KeycloakIdentity.java +++ b/services/src/main/java/org/keycloak/authorization/common/KeycloakIdentity.java @@ -212,7 +212,7 @@ public class KeycloakIdentity implements Identity { return client==null ? null : client.getId(); } - return this.accessToken.getSubject(); + return this.getUserFromSessionState().getId(); } @Override @@ -237,7 +237,7 @@ public class KeycloakIdentity implements Identity { return false; } - return this.accessToken.getSubject().equals(clientUser.getId()); + return this.getUserFromSessionState().getId().equals(clientUser.getId()); } private ClientModel getTargetClient() { @@ -252,4 +252,9 @@ public class KeycloakIdentity implements Identity { return null; } + + private UserModel getUserFromSessionState() { + UserSessionModel userSession = keycloakSession.sessions().getUserSession(realm, accessToken.getSessionState()); + return userSession.getUser(); + } } diff --git a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java index e18b444218..803e778115 100755 --- a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java +++ b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java @@ -1082,24 +1082,28 @@ public class AuthenticationManager { } } - UserModel user = session.users().getUserById(token.getSubject(), realm); - if (user == null || !user.isEnabled() ) { - logger.debug("Unknown user in identity token"); - return null; - } - - int userNotBefore = session.users().getNotBeforeOfUser(realm, user); - if (token.getIssuedAt() < userNotBefore) { - logger.debug("User notBefore newer than token"); - return null; - } - UserSessionModel userSession = session.sessions().getUserSession(realm, token.getSessionState()); + UserModel user = null; + if (userSession != null) { + user = userSession.getUser(); + if (user == null || !user.isEnabled()) { + logger.debug("Unknown user in identity token"); + return null; + } + + int userNotBefore = session.users().getNotBeforeOfUser(realm, user); + if (token.getIssuedAt() < userNotBefore) { + logger.debug("User notBefore newer than token"); + return null; + } + } + if (!isSessionValid(realm, userSession)) { // Check if accessToken was for the offline session. if (!isCookie) { UserSessionModel offlineUserSession = session.sessions().getOfflineUserSession(realm, token.getSessionState()); if (isOfflineSessionValid(realm, offlineUserSession)) { + user = offlineUserSession.getUser(); return new AuthResult(user, offlineUserSession, token); } } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/AuthorizationAPITest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/AuthorizationAPITest.java index 61d27735c9..37dd1ffe3a 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/AuthorizationAPITest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/AuthorizationAPITest.java @@ -53,29 +53,53 @@ import org.keycloak.util.JsonSerialization; */ public class AuthorizationAPITest extends AbstractAuthzTest { + private static final String RESOURCE_SERVER_TEST = "resource-server-test"; + private static final String TEST_CLIENT = "test-client"; + private static final String AUTHZ_CLIENT_CONFIG = "default-keycloak.json"; + private static final String PAIRWISE_RESOURCE_SERVER_TEST = "pairwise-resource-server-test"; + private static final String PAIRWISE_TEST_CLIENT = "test-client-pairwise"; + private static final String PAIRWISE_AUTHZ_CLIENT_CONFIG = "default-keycloak-pairwise.json"; + @Override public void addTestRealms(List testRealms) { testRealms.add(RealmBuilder.create().name("authz-test") .roles(RolesBuilder.create().realmRole(RoleBuilder.create().name("uma_authorization").build())) .user(UserBuilder.create().username("marta").password("password").addRoles("uma_authorization")) .user(UserBuilder.create().username("kolo").password("password")) - .client(ClientBuilder.create().clientId("resource-server-test") + .client(ClientBuilder.create().clientId(RESOURCE_SERVER_TEST) .secret("secret") .authorizationServicesEnabled(true) .redirectUris("http://localhost/resource-server-test") .defaultRoles("uma_protection") .directAccessGrants()) - .client(ClientBuilder.create().clientId("test-client") + .client(ClientBuilder.create().clientId(PAIRWISE_RESOURCE_SERVER_TEST) + .secret("secret") + .authorizationServicesEnabled(true) + .redirectUris("http://localhost/resource-server-test") + .defaultRoles("uma_protection") + .directAccessGrants() + .pairwise("http://pairwise.com")) + .client(ClientBuilder.create().clientId(TEST_CLIENT) .secret("secret") .authorizationServicesEnabled(true) .redirectUris("http://localhost/test-client") .directAccessGrants()) + .client(ClientBuilder.create().clientId(PAIRWISE_TEST_CLIENT) + .secret("secret") + .authorizationServicesEnabled(true) + .redirectUris("http://localhost/test-client") + .directAccessGrants()) .build()); } @Before public void configureAuthorization() throws Exception { - ClientResource client = getClient(getRealm()); + configureAuthorization(RESOURCE_SERVER_TEST); + configureAuthorization(PAIRWISE_RESOURCE_SERVER_TEST); + } + + private void configureAuthorization(String clientId) throws Exception { + ClientResource client = getClient(getRealm(), clientId); AuthorizationResource authorization = client.authorization(); ResourceRepresentation resource = new ResourceRepresentation("Resource A"); @@ -102,7 +126,16 @@ public class AuthorizationAPITest extends AbstractAuthzTest { @Test public void testAccessTokenWithUmaAuthorization() { - AuthzClient authzClient = getAuthzClient(); + testAccessTokenWithUmaAuthorization(AUTHZ_CLIENT_CONFIG); + } + + @Test + public void testAccessTokenWithUmaAuthorizationPairwise() { + testAccessTokenWithUmaAuthorization(PAIRWISE_AUTHZ_CLIENT_CONFIG); + } + + public void testAccessTokenWithUmaAuthorization(String authzConfigFile) { + AuthzClient authzClient = getAuthzClient(authzConfigFile); PermissionRequest request = new PermissionRequest("Resource A"); String ticket = authzClient.protection().permission().create(request).getTicket(); @@ -113,32 +146,63 @@ public class AuthorizationAPITest extends AbstractAuthzTest { @Test public void testResourceServerAsAudience() throws Exception { - AuthzClient authzClient = getAuthzClient(); + testResourceServerAsAudience( + TEST_CLIENT, + RESOURCE_SERVER_TEST, + AUTHZ_CLIENT_CONFIG); + } + + @Test + public void testResourceServerAsAudienceWithPairwiseClient() throws Exception { + testResourceServerAsAudience( + PAIRWISE_TEST_CLIENT, + RESOURCE_SERVER_TEST, + AUTHZ_CLIENT_CONFIG); + } + + @Test + public void testPairwiseResourceServerAsAudience() throws Exception { + testResourceServerAsAudience( + TEST_CLIENT, + PAIRWISE_RESOURCE_SERVER_TEST, + PAIRWISE_AUTHZ_CLIENT_CONFIG); + } + + @Test + public void testPairwiseResourceServerAsAudienceWithPairwiseClient() throws Exception { + testResourceServerAsAudience( + PAIRWISE_TEST_CLIENT, + PAIRWISE_RESOURCE_SERVER_TEST, + PAIRWISE_AUTHZ_CLIENT_CONFIG); + } + + public void testResourceServerAsAudience(String clientId, String resourceServerClientId, String authzConfigFile) throws Exception { + AuthzClient authzClient = getAuthzClient(authzConfigFile); PermissionRequest request = new PermissionRequest(); request.setResourceId("Resource A"); - String accessToken = new OAuthClient().realm("authz-test").clientId("test-client").doGrantAccessTokenRequest("secret", "marta", "password").getAccessToken(); + String accessToken = new OAuthClient().realm("authz-test").clientId(clientId).doGrantAccessTokenRequest("secret", "marta", "password").getAccessToken(); String ticket = authzClient.protection().permission().create(request).getTicket(); AuthorizationResponse response = authzClient.authorization(accessToken).authorize(new AuthorizationRequest(ticket)); assertNotNull(response.getToken()); AccessToken rpt = toAccessToken(response.getToken()); - assertEquals("resource-server-test", rpt.getAudience()[0]); + assertEquals(resourceServerClientId, rpt.getAudience()[0]); } private RealmResource getRealm() throws Exception { return adminClient.realm("authz-test"); } - private ClientResource getClient(RealmResource realm) { + private ClientResource getClient(RealmResource realm, String clientId) { ClientsResource clients = realm.clients(); - return clients.findByClientId("resource-server-test").stream().map(representation -> clients.get(representation.getId())).findFirst().orElseThrow(() -> new RuntimeException("Expected client [resource-server-test]")); + return clients.findByClientId(clientId).stream().map(representation -> clients.get(representation.getId())).findFirst().orElseThrow(() -> new RuntimeException("Expected client [resource-server-test]")); } - private AuthzClient getAuthzClient() { + private AuthzClient getAuthzClient(String configFile) { try { - return AuthzClient.create(JsonSerialization.readValue(getClass().getResourceAsStream("/authorization-test/default-keycloak.json"), Configuration.class)); + return AuthzClient.create(JsonSerialization.readValue(getClass().getResourceAsStream("/authorization-test/" + configFile), Configuration.class)); } catch (IOException cause) { throw new RuntimeException("Failed to create authz client", cause); } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/EntitlementAPITest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/EntitlementAPITest.java index 54bcd2e5bb..a7fab87278 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/EntitlementAPITest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/EntitlementAPITest.java @@ -55,6 +55,13 @@ import org.keycloak.util.JsonSerialization; */ public class EntitlementAPITest extends AbstractAuthzTest { + private static final String RESOURCE_SERVER_TEST = "resource-server-test"; + private static final String TEST_CLIENT = "test-client"; + private static final String AUTHZ_CLIENT_CONFIG = "default-keycloak.json"; + private static final String PAIRWISE_RESOURCE_SERVER_TEST = "pairwise-resource-server-test"; + private static final String PAIRWISE_TEST_CLIENT = "test-client-pairwise"; + private static final String PAIRWISE_AUTHZ_CLIENT_CONFIG = "default-keycloak-pairwise.json"; + private AuthzClient authzClient; @Override @@ -63,23 +70,41 @@ public class EntitlementAPITest extends AbstractAuthzTest { .roles(RolesBuilder.create().realmRole(RoleBuilder.create().name("uma_authorization").build())) .user(UserBuilder.create().username("marta").password("password").addRoles("uma_authorization")) .user(UserBuilder.create().username("kolo").password("password")) - .client(ClientBuilder.create().clientId("resource-server-test") - .secret("secret") - .authorizationServicesEnabled(true) - .redirectUris("http://localhost/resource-server-test") - .defaultRoles("uma_protection") - .directAccessGrants()) - .client(ClientBuilder.create().clientId("test-client") - .secret("secret") - .authorizationServicesEnabled(true) - .redirectUris("http://localhost/test-client") - .directAccessGrants()) + .client(ClientBuilder.create().clientId(RESOURCE_SERVER_TEST) + .secret("secret") + .authorizationServicesEnabled(true) + .redirectUris("http://localhost/resource-server-test") + .defaultRoles("uma_protection") + .directAccessGrants()) + .client(ClientBuilder.create().clientId(PAIRWISE_RESOURCE_SERVER_TEST) + .secret("secret") + .authorizationServicesEnabled(true) + .redirectUris("http://localhost/resource-server-test") + .defaultRoles("uma_protection") + .pairwise("http://pairwise.com") + .directAccessGrants()) + .client(ClientBuilder.create().clientId(TEST_CLIENT) + .secret("secret") + .authorizationServicesEnabled(true) + .redirectUris("http://localhost/test-client") + .directAccessGrants()) + .client(ClientBuilder.create().clientId(PAIRWISE_TEST_CLIENT) + .secret("secret") + .authorizationServicesEnabled(true) + .redirectUris("http://localhost/test-client") + .pairwise("http://pairwise.com") + .directAccessGrants()) .build()); } @Before public void configureAuthorization() throws Exception { - ClientResource client = getClient(getRealm()); + configureAuthorization(RESOURCE_SERVER_TEST); + configureAuthorization(PAIRWISE_RESOURCE_SERVER_TEST); + } + + public void configureAuthorization(String clientId) throws Exception { + ClientResource client = getClient(getRealm(), clientId); AuthorizationResource authorization = client.authorization(); JSPolicyRepresentation policy = new JSPolicyRepresentation(); @@ -106,6 +131,15 @@ public class EntitlementAPITest extends AbstractAuthzTest { @Test public void testRptRequestWithoutResourceName() { + testRptRequestWithoutResourceName(AUTHZ_CLIENT_CONFIG); + } + + @Test + public void testRptRequestWithoutResourceNamePairwise() { + testRptRequestWithoutResourceName(PAIRWISE_AUTHZ_CLIENT_CONFIG); + } + + public void testRptRequestWithoutResourceName(String configFile) { Metadata metadata = new Metadata(); metadata.setIncludeResourceName(false); @@ -116,32 +150,51 @@ public class EntitlementAPITest extends AbstractAuthzTest { request.setMetadata(metadata); request.addPermission("Resource 1"); - return getAuthzClient().authorization("marta", "password").authorize(request); + return getAuthzClient(configFile).authorization("marta", "password").authorize(request); }); } @Test public void testRptRequestWithResourceName() { + testRptRequestWithResourceName(AUTHZ_CLIENT_CONFIG); + } + + @Test + public void testRptRequestWithResourceNamePairwise() { + testRptRequestWithResourceName(PAIRWISE_AUTHZ_CLIENT_CONFIG); + } + + + public void testRptRequestWithResourceName(String configFile) { Metadata metadata = new Metadata(); metadata.setIncludeResourceName(true); - assertResponse(metadata, () -> getAuthzClient().authorization("marta", "password").authorize()); + assertResponse(metadata, () -> getAuthzClient(configFile).authorization("marta", "password").authorize()); AuthorizationRequest request = new AuthorizationRequest(); request.setMetadata(metadata); request.addPermission("Resource 13"); - assertResponse(metadata, () -> getAuthzClient().authorization("marta", "password").authorize(request)); + assertResponse(metadata, () -> getAuthzClient(configFile).authorization("marta", "password").authorize(request)); request.setMetadata(null); - assertResponse(metadata, () -> getAuthzClient().authorization("marta", "password").authorize(request)); + assertResponse(metadata, () -> getAuthzClient(configFile).authorization("marta", "password").authorize(request)); } @Test public void testPermissionLimit() { + testPermissionLimit(AUTHZ_CLIENT_CONFIG); + } + + @Test + public void testPermissionLimitPairwise() { + testPermissionLimit(PAIRWISE_AUTHZ_CLIENT_CONFIG); + } + + public void testPermissionLimit(String configFile) { AuthorizationRequest request = new AuthorizationRequest(); for (int i = 1; i <= 10; i++) { @@ -154,7 +207,7 @@ public class EntitlementAPITest extends AbstractAuthzTest { request.setMetadata(metadata); - AuthorizationResponse response = getAuthzClient().authorization("marta", "password").authorize(request); + AuthorizationResponse response = getAuthzClient(configFile).authorization("marta", "password").authorize(request); AccessToken rpt = toAccessToken(response.getToken()); List permissions = rpt.getAuthorization().getPermissions(); @@ -174,7 +227,7 @@ public class EntitlementAPITest extends AbstractAuthzTest { request.setMetadata(metadata); request.setRpt(response.getToken()); - response = getAuthzClient().authorization("marta", "password").authorize(request); + response = getAuthzClient(configFile).authorization("marta", "password").authorize(request); rpt = toAccessToken(response.getToken()); permissions = rpt.getAuthorization().getPermissions(); @@ -198,7 +251,7 @@ public class EntitlementAPITest extends AbstractAuthzTest { request.setMetadata(metadata); request.setRpt(response.getToken()); - response = getAuthzClient().authorization("marta", "password").authorize(request); + response = getAuthzClient(configFile).authorization("marta", "password").authorize(request); rpt = toAccessToken(response.getToken()); permissions = rpt.getAuthorization().getPermissions(); @@ -221,7 +274,7 @@ public class EntitlementAPITest extends AbstractAuthzTest { request.setMetadata(metadata); request.setRpt(response.getToken()); - response = getAuthzClient().authorization("marta", "password").authorize(request); + response = getAuthzClient(configFile).authorization("marta", "password").authorize(request); rpt = toAccessToken(response.getToken()); permissions = rpt.getAuthorization().getPermissions(); @@ -236,15 +289,46 @@ public class EntitlementAPITest extends AbstractAuthzTest { @Test public void testResourceServerAsAudience() throws Exception { + testResourceServerAsAudience( + TEST_CLIENT, + RESOURCE_SERVER_TEST, + AUTHZ_CLIENT_CONFIG); + } + + @Test + public void testResourceServerAsAudienceWithPairwiseClient() throws Exception { + testResourceServerAsAudience( + PAIRWISE_TEST_CLIENT, + RESOURCE_SERVER_TEST, + AUTHZ_CLIENT_CONFIG); + } + + @Test + public void testPairwiseResourceServerAsAudience() throws Exception { + testResourceServerAsAudience( + TEST_CLIENT, + PAIRWISE_RESOURCE_SERVER_TEST, + PAIRWISE_AUTHZ_CLIENT_CONFIG); + } + + @Test + public void testPairwiseResourceServerAsAudienceWithPairwiseClient() throws Exception { + testResourceServerAsAudience( + PAIRWISE_TEST_CLIENT, + PAIRWISE_RESOURCE_SERVER_TEST, + PAIRWISE_AUTHZ_CLIENT_CONFIG); + } + + public void testResourceServerAsAudience(String testClientId, String resourceServerClientId, String configFile) throws Exception { AuthorizationRequest request = new AuthorizationRequest(); request.addPermission("Resource 1"); - String accessToken = new OAuthClient().realm("authz-test").clientId("test-client").doGrantAccessTokenRequest("secret", "marta", "password").getAccessToken(); - AuthorizationResponse response = getAuthzClient().authorization(accessToken).authorize(request); + String accessToken = new OAuthClient().realm("authz-test").clientId(testClientId).doGrantAccessTokenRequest("secret", "marta", "password").getAccessToken(); + AuthorizationResponse response = getAuthzClient(configFile).authorization(accessToken).authorize(request); AccessToken rpt = toAccessToken(response.getToken()); - assertEquals("resource-server-test", rpt.getAudience()[0]); + assertEquals(resourceServerClientId, rpt.getAudience()[0]); } private void assertResponse(Metadata metadata, Supplier responseSupplier) { @@ -268,15 +352,15 @@ public class EntitlementAPITest extends AbstractAuthzTest { return adminClient.realm("authz-test"); } - private ClientResource getClient(RealmResource realm) { + private ClientResource getClient(RealmResource realm, String clientId) { ClientsResource clients = realm.clients(); - return clients.findByClientId("resource-server-test").stream().map(representation -> clients.get(representation.getId())).findFirst().orElseThrow(() -> new RuntimeException("Expected client [resource-server-test]")); + return clients.findByClientId(clientId).stream().map(representation -> clients.get(representation.getId())).findFirst().orElseThrow(() -> new RuntimeException("Expected client [resource-server-test]")); } - private AuthzClient getAuthzClient() { + private AuthzClient getAuthzClient(String configFile) { if (authzClient == null) { try { - authzClient = AuthzClient.create(JsonSerialization.readValue(getClass().getResourceAsStream("/authorization-test/default-keycloak.json"), Configuration.class)); + authzClient = AuthzClient.create(JsonSerialization.readValue(getClass().getResourceAsStream("/authorization-test/" + configFile), Configuration.class)); } catch (IOException cause) { throw new RuntimeException("Failed to create authz client", cause); } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/ClientBuilder.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/ClientBuilder.java index 870d0599b1..3fef22aa12 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/ClientBuilder.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/ClientBuilder.java @@ -198,4 +198,12 @@ public class ClientBuilder { rep.getProtocolMappers().addAll(Arrays.asList(mappers)); return this; } + + public ClientBuilder pairwise(String sectorIdentifierUri, String salt) { + return protocolMapper(ProtocolMapperUtil.createPairwiseMapper(sectorIdentifierUri, salt)); + } + + public ClientBuilder pairwise(String sectorIdentifierUri) { + return protocolMapper(ProtocolMapperUtil.createPairwiseMapper(sectorIdentifierUri, null)); + } } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/ProtocolMapperUtil.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/ProtocolMapperUtil.java index 17e0eb3dc2..18b787210a 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/ProtocolMapperUtil.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/ProtocolMapperUtil.java @@ -6,6 +6,7 @@ import org.keycloak.protocol.oidc.mappers.AddressMapper; import org.keycloak.protocol.oidc.mappers.HardcodedClaim; import org.keycloak.protocol.oidc.mappers.HardcodedRole; import org.keycloak.protocol.oidc.mappers.RoleNameMapper; +import org.keycloak.protocol.oidc.mappers.SHA256PairwiseSubMapper; import org.keycloak.protocol.oidc.mappers.ScriptBasedOIDCProtocolMapper; import org.keycloak.protocol.oidc.mappers.UserAttributeMapper; import org.keycloak.protocol.oidc.mappers.UserClientRoleMappingMapper; @@ -164,4 +165,7 @@ public class ProtocolMapperUtil { ); } + public static ProtocolMapperRepresentation createPairwiseMapper(String sectorIdentifierUri, String salt) { + return SHA256PairwiseSubMapper.createPairwiseMapper(sectorIdentifierUri, salt); + } } diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/authorization-test/default-keycloak-pairwise.json b/testsuite/integration-arquillian/tests/base/src/test/resources/authorization-test/default-keycloak-pairwise.json new file mode 100644 index 0000000000..fbd7c14613 --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/resources/authorization-test/default-keycloak-pairwise.json @@ -0,0 +1,8 @@ +{ + "realm": "authz-test", + "auth-server-url" : "http://localhost:8180/auth", + "resource" : "pairwise-resource-server-test", + "credentials": { + "secret": "secret" + } +} \ No newline at end of file