diff --git a/authz/client/src/main/java/org/keycloak/authorization/client/resource/AuthorizationResource.java b/authz/client/src/main/java/org/keycloak/authorization/client/resource/AuthorizationResource.java index 84abc2d7db5..3bf90f37457 100644 --- a/authz/client/src/main/java/org/keycloak/authorization/client/resource/AuthorizationResource.java +++ b/authz/client/src/main/java/org/keycloak/authorization/client/resource/AuthorizationResource.java @@ -18,6 +18,7 @@ package org.keycloak.authorization.client.resource; +import java.util.List; import java.util.concurrent.Callable; import org.keycloak.authorization.client.AuthorizationDeniedException; @@ -25,10 +26,13 @@ import org.keycloak.authorization.client.Configuration; import org.keycloak.authorization.client.representation.ServerConfiguration; import org.keycloak.authorization.client.util.Http; import org.keycloak.authorization.client.util.HttpMethod; +import org.keycloak.authorization.client.util.HttpMethodResponse; import org.keycloak.authorization.client.util.Throwables; import org.keycloak.authorization.client.util.TokenCallable; import org.keycloak.representations.idm.authorization.AuthorizationRequest; import org.keycloak.representations.idm.authorization.AuthorizationResponse; +import org.keycloak.representations.idm.authorization.Permission; +import com.fasterxml.jackson.core.type.TypeReference; /** * An entry point for obtaining permissions from the server. @@ -67,29 +71,60 @@ public class AuthorizationResource { * @throws AuthorizationDeniedException in case the request was denied by the server */ public AuthorizationResponse authorize(final AuthorizationRequest request) throws AuthorizationDeniedException { + return invoke(request); + } + + /** + * Query the server for a list of permissions given an {@link AuthorizationRequest}. + * + * @param request an {@link AuthorizationRequest} (not {@code null}) + * @return a list of permissions granted by the server + * @throws AuthorizationDeniedException in case the request was denied by the server + */ + public List getPermissions(final AuthorizationRequest request) throws AuthorizationDeniedException { + AuthorizationRequest.Metadata metadata; + + if (request.getMetadata() == null) { + metadata = new AuthorizationRequest.Metadata(); + } else { + metadata = request.getMetadata(); + } + + metadata.setResponseMode("permissions"); + + return invoke(request); + } + + private T invoke(AuthorizationRequest request) { if (request == null) { throw new IllegalArgumentException("Authorization request must not be null"); } - Callable callable = new Callable() { + Callable callable = new Callable() { @Override - public AuthorizationResponse call() throws Exception { + public T call() throws Exception { if (request.getAudience() == null) { request.setAudience(configuration.getResource()); } - HttpMethod method = http.post(serverConfiguration.getTokenEndpoint()); + HttpMethod method = http.post(serverConfiguration.getTokenEndpoint()); if (token != null) { method = method.authorizationBearer(token.call()); } - return method + HttpMethodResponse response = method .authentication() .uma(request) - .response() - .json(AuthorizationResponse.class) - .execute(); + .response(); + + if (request.getMetadata() != null && "permissions".equals(request.getMetadata().getResponseMode())) { + response = response.json(new TypeReference(){}); + } else { + response = response.json((Class) AuthorizationResponse.class); + } + + return response.execute(); } }; try { diff --git a/authz/client/src/main/java/org/keycloak/authorization/client/util/HttpMethodAuthenticator.java b/authz/client/src/main/java/org/keycloak/authorization/client/util/HttpMethodAuthenticator.java index 3d3764b85ea..9f467228eed 100644 --- a/authz/client/src/main/java/org/keycloak/authorization/client/util/HttpMethodAuthenticator.java +++ b/authz/client/src/main/java/org/keycloak/authorization/client/util/HttpMethodAuthenticator.java @@ -125,6 +125,10 @@ public class HttpMethodAuthenticator { if (metadata.getLimit() != null) { method.param("response_permissions_limit", metadata.getLimit().toString()); } + + if (metadata.getResponseMode() != null) { + method.param("response_mode", metadata.getResponseMode()); + } } return method; 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 63befd07135..c6ab20382ee 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 @@ -17,7 +17,10 @@ package org.keycloak.testsuite.authz; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; import java.io.IOException; import java.util.Collections; @@ -39,6 +42,7 @@ import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.representations.idm.authorization.AuthorizationRequest; import org.keycloak.representations.idm.authorization.AuthorizationResponse; import org.keycloak.representations.idm.authorization.JSPolicyRepresentation; +import org.keycloak.representations.idm.authorization.Permission; import org.keycloak.representations.idm.authorization.PermissionRequest; import org.keycloak.representations.idm.authorization.ResourcePermissionRepresentation; import org.keycloak.representations.idm.authorization.ResourceRepresentation; @@ -185,6 +189,26 @@ public class AuthorizationAPITest extends AbstractAuthzTest { PAIRWISE_AUTHZ_CLIENT_CONFIG); } + @Test + public void testResponseMode() { + AuthzClient authzClient = getAuthzClient(AUTHZ_CLIENT_CONFIG); + PermissionRequest permission = new PermissionRequest("Resource A"); + + String ticket = authzClient.protection().permission().create(permission).getTicket(); + AuthorizationRequest request = new AuthorizationRequest(ticket); + AuthorizationResponse response = authzClient.authorization("marta", "password").authorize(request); + assertNotNull(response.getToken()); + + request.setMetadata(new AuthorizationRequest.Metadata()); + request.getMetadata().setResponseMode("decision"); + response = authzClient.authorization("marta", "password").authorize(request); + assertNull(response.getToken()); + assertTrue((Boolean) response.getOtherClaims().getOrDefault("result", "false")); + + List permissions = authzClient.authorization("marta", "password").getPermissions(request); + assertFalse(permissions.isEmpty()); + } + public void testResourceServerAsAudience(String clientId, String resourceServerClientId, String authzConfigFile) throws Exception { AuthzClient authzClient = getAuthzClient(authzConfigFile); PermissionRequest request = new PermissionRequest();