Merge pull request #4471 from pedroigor/KEYCLOAK-5095

[KEYCLOAK-5095] - RPT should contain the RS as audience
This commit is contained in:
Pedro Igor 2017-09-18 09:32:47 -03:00 committed by GitHub
commit e8ef050093
6 changed files with 88 additions and 23 deletions

View file

@ -23,6 +23,7 @@ import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.authorization.model.Scope; import org.keycloak.authorization.model.Scope;
import org.keycloak.authorization.policy.evaluation.Result; import org.keycloak.authorization.policy.evaluation.Result;
import org.keycloak.authorization.util.Permissions; import org.keycloak.authorization.util.Permissions;
import org.keycloak.models.ClientModel;
import org.keycloak.representations.AccessToken; import org.keycloak.representations.AccessToken;
import org.keycloak.representations.idm.authorization.DecisionEffect; import org.keycloak.representations.idm.authorization.DecisionEffect;
import org.keycloak.representations.idm.authorization.PolicyEvaluationResponse; import org.keycloak.representations.idm.authorization.PolicyEvaluationResponse;
@ -55,6 +56,12 @@ public class PolicyEvaluationResponseBuilder {
authorizationData.setPermissions(Permissions.permits(results, null, authorization, resourceServer)); authorizationData.setPermissions(Permissions.permits(results, null, authorization, resourceServer));
accessToken.setAuthorization(authorizationData); accessToken.setAuthorization(authorizationData);
ClientModel clientModel = authorization.getRealm().getClientById(resourceServer.getId());
if (!accessToken.hasAudience(clientModel.getClientId())) {
accessToken.audience(clientModel.getClientId());
}
response.setRpt(accessToken); response.setRpt(accessToken);
if (results.stream().anyMatch(evaluationResult -> evaluationResult.getEffect().equals(Decision.Effect.DENY))) { if (results.stream().anyMatch(evaluationResult -> evaluationResult.getEffect().equals(Decision.Effect.DENY))) {

View file

@ -25,7 +25,6 @@ import org.keycloak.authorization.authorization.representation.AuthorizationRequ
import org.keycloak.authorization.authorization.representation.AuthorizationResponse; import org.keycloak.authorization.authorization.representation.AuthorizationResponse;
import org.keycloak.authorization.common.KeycloakEvaluationContext; import org.keycloak.authorization.common.KeycloakEvaluationContext;
import org.keycloak.authorization.common.KeycloakIdentity; import org.keycloak.authorization.common.KeycloakIdentity;
import org.keycloak.authorization.entitlement.representation.EntitlementResponse;
import org.keycloak.authorization.model.Resource; import org.keycloak.authorization.model.Resource;
import org.keycloak.authorization.model.ResourceServer; import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.authorization.model.Scope; import org.keycloak.authorization.model.Scope;
@ -39,6 +38,7 @@ import org.keycloak.authorization.util.Permissions;
import org.keycloak.authorization.util.Tokens; import org.keycloak.authorization.util.Tokens;
import org.keycloak.jose.jws.JWSInput; import org.keycloak.jose.jws.JWSInput;
import org.keycloak.jose.jws.JWSInputException; import org.keycloak.jose.jws.JWSInputException;
import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import org.keycloak.protocol.oidc.TokenManager; import org.keycloak.protocol.oidc.TokenManager;
@ -119,7 +119,7 @@ public class AuthorizationTokenService {
List<Permission> entitlements = Permissions.permits(result, authorizationRequest.getMetadata(), authorization, resourceServer); List<Permission> entitlements = Permissions.permits(result, authorizationRequest.getMetadata(), authorization, resourceServer);
if (!entitlements.isEmpty()) { if (!entitlements.isEmpty()) {
AuthorizationResponse response = new AuthorizationResponse(createRequestingPartyToken(entitlements, identity.getAccessToken())); AuthorizationResponse response = new AuthorizationResponse(createRequestingPartyToken(entitlements, identity.getAccessToken(), resourceServer));
return Cors.add(httpRequest, Response.status(Status.CREATED).entity(response)).allowedOrigins(identity.getAccessToken()) return Cors.add(httpRequest, Response.status(Status.CREATED).entity(response)).allowedOrigins(identity.getAccessToken())
.allowedMethods("POST") .allowedMethods("POST")
.exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS).build(); .exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS).build();
@ -254,12 +254,18 @@ public class AuthorizationTokenService {
return this.authorization.getKeycloakSession().getContext().getRealm(); return this.authorization.getKeycloakSession().getContext().getRealm();
} }
private String createRequestingPartyToken(List<Permission> permissions, AccessToken accessToken) { private String createRequestingPartyToken(List<Permission> permissions, AccessToken accessToken, ResourceServer resourceServer) {
AccessToken.Authorization authorization = new AccessToken.Authorization(); AccessToken.Authorization authorization = new AccessToken.Authorization();
authorization.setPermissions(permissions); authorization.setPermissions(permissions);
accessToken.setAuthorization(authorization); accessToken.setAuthorization(authorization);
ClientModel clientModel = this.authorization.getRealm().getClientById(resourceServer.getId());
if (!accessToken.hasAudience(clientModel.getClientId())) {
accessToken.audience(clientModel.getClientId());
}
return new TokenManager().encodeToken(session, getRealm(), accessToken); return new TokenManager().encodeToken(session, getRealm(), accessToken);
} }

View file

@ -167,7 +167,7 @@ public class EntitlementService {
List<Permission> entitlements = Permissions.permits(result, metadata, authorization, resourceServer); List<Permission> entitlements = Permissions.permits(result, metadata, authorization, resourceServer);
if (!entitlements.isEmpty()) { if (!entitlements.isEmpty()) {
return Cors.add(request, Response.ok().entity(new EntitlementResponse(createRequestingPartyToken(entitlements, identity.getAccessToken())))).allowedOrigins(identity.getAccessToken()).allowedMethods("GET").exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS).build(); return Cors.add(request, Response.ok().entity(new EntitlementResponse(createRequestingPartyToken(entitlements, identity.getAccessToken(), resourceServer)))).allowedOrigins(identity.getAccessToken()).allowedMethods("GET").exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS).build();
} }
} catch (Exception cause) { } catch (Exception cause) {
logger.error(cause); logger.error(cause);
@ -184,13 +184,19 @@ public class EntitlementService {
.exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS).build(); .exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS).build();
} }
private String createRequestingPartyToken(List<Permission> permissions, AccessToken accessToken) { private String createRequestingPartyToken(List<Permission> permissions, AccessToken accessToken, ResourceServer resourceServer) {
RealmModel realm = this.authorization.getKeycloakSession().getContext().getRealm(); RealmModel realm = this.authorization.getKeycloakSession().getContext().getRealm();
AccessToken.Authorization authorization = new AccessToken.Authorization(); AccessToken.Authorization authorization = new AccessToken.Authorization();
authorization.setPermissions(permissions); authorization.setPermissions(permissions);
accessToken.setAuthorization(authorization); accessToken.setAuthorization(authorization);
ClientModel clientModel = realm.getClientById(resourceServer.getId());
if (!accessToken.hasAudience(clientModel.getClientId())) {
accessToken.audience(clientModel.getClientId());
}
return new TokenManager().encodeToken(this.authorization.getKeycloakSession(), realm, accessToken); return new TokenManager().encodeToken(this.authorization.getKeycloakSession(), realm, accessToken);
} }

View file

@ -1,6 +1,10 @@
package org.keycloak.testsuite.authz; package org.keycloak.testsuite.authz;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.keycloak.authorization.client.representation.EntitlementResponse;
import org.keycloak.jose.jws.JWSInput;
import org.keycloak.jose.jws.JWSInputException;
import org.keycloak.representations.AccessToken;
import org.keycloak.testsuite.AbstractKeycloakTest; import org.keycloak.testsuite.AbstractKeycloakTest;
import org.keycloak.testsuite.ProfileAssume; import org.keycloak.testsuite.ProfileAssume;
@ -13,4 +17,15 @@ public abstract class AbstractAuthzTest extends AbstractKeycloakTest {
public static void enabled() { public static void enabled() {
ProfileAssume.assumePreview(); ProfileAssume.assumePreview();
} }
protected AccessToken toAccessToken(String rpt) {
AccessToken accessToken;
try {
accessToken = new JWSInput(rpt).readJsonContent(AccessToken.class);
} catch (JWSInputException cause) {
throw new RuntimeException("Failed to deserialize RPT", cause);
}
return accessToken;
}
} }

View file

@ -39,13 +39,14 @@ import org.keycloak.authorization.client.representation.AuthorizationRequest;
import org.keycloak.authorization.client.representation.AuthorizationResponse; import org.keycloak.authorization.client.representation.AuthorizationResponse;
import org.keycloak.authorization.client.representation.PermissionRequest; import org.keycloak.authorization.client.representation.PermissionRequest;
import org.keycloak.authorization.client.util.HttpResponseException; import org.keycloak.authorization.client.util.HttpResponseException;
import org.keycloak.representations.AccessToken;
import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.RoleRepresentation; import org.keycloak.representations.idm.RoleRepresentation;
import org.keycloak.representations.idm.authorization.JSPolicyRepresentation; import org.keycloak.representations.idm.authorization.JSPolicyRepresentation;
import org.keycloak.representations.idm.authorization.ResourcePermissionRepresentation; import org.keycloak.representations.idm.authorization.ResourcePermissionRepresentation;
import org.keycloak.representations.idm.authorization.ResourceRepresentation; import org.keycloak.representations.idm.authorization.ResourceRepresentation;
import org.keycloak.testsuite.AbstractKeycloakTest;
import org.keycloak.testsuite.util.ClientBuilder; import org.keycloak.testsuite.util.ClientBuilder;
import org.keycloak.testsuite.util.OAuthClient;
import org.keycloak.testsuite.util.RealmBuilder; import org.keycloak.testsuite.util.RealmBuilder;
import org.keycloak.testsuite.util.RoleBuilder; import org.keycloak.testsuite.util.RoleBuilder;
import org.keycloak.testsuite.util.RolesBuilder; import org.keycloak.testsuite.util.RolesBuilder;
@ -55,7 +56,7 @@ import org.keycloak.util.JsonSerialization;
/** /**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a> * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/ */
public class RequireUmaAuthorizationScopeTest extends AbstractAuthzTest { public class AuthorizationAPITest extends AbstractAuthzTest {
@Override @Override
public void addTestRealms(List<RealmRepresentation> testRealms) { public void addTestRealms(List<RealmRepresentation> testRealms) {
@ -69,6 +70,11 @@ public class RequireUmaAuthorizationScopeTest extends AbstractAuthzTest {
.redirectUris("http://localhost/resource-server-test") .redirectUris("http://localhost/resource-server-test")
.defaultRoles("uma_protection") .defaultRoles("uma_protection")
.directAccessGrants()) .directAccessGrants())
.client(ClientBuilder.create().clientId("test-client")
.secret("secret")
.authorizationServicesEnabled(true)
.redirectUris("http://localhost/test-client")
.directAccessGrants())
.build()); .build());
} }
@ -143,6 +149,22 @@ public class RequireUmaAuthorizationScopeTest extends AbstractAuthzTest {
failAccessTokenWithoutUmaAuthorization(); failAccessTokenWithoutUmaAuthorization();
} }
@Test
public void testResourceServerAsAudience() throws Exception {
AuthzClient authzClient = getAuthzClient();
PermissionRequest request = new PermissionRequest();
request.setResourceSetName("Resource A");
String accessToken = new OAuthClient().realm("authz-test").clientId("test-client").doGrantAccessTokenRequest("secret", "marta", "password").getAccessToken();
String ticket = authzClient.protection().permission().forResource(request).getTicket();
AuthorizationResponse response = authzClient.authorization(accessToken).authorize(new AuthorizationRequest(ticket));
assertNotNull(response.getRpt());
AccessToken rpt = toAccessToken(response.getRpt());
assertEquals("resource-server-test", rpt.getAudience()[0]);
}
private RealmResource getRealm() throws Exception { private RealmResource getRealm() throws Exception {
return adminClient.realm("authz-test"); return adminClient.realm("authz-test");
} }

View file

@ -41,6 +41,7 @@ import org.keycloak.authorization.client.representation.EntitlementResponse;
import org.keycloak.authorization.client.representation.PermissionRequest; import org.keycloak.authorization.client.representation.PermissionRequest;
import org.keycloak.jose.jws.JWSInput; import org.keycloak.jose.jws.JWSInput;
import org.keycloak.jose.jws.JWSInputException; import org.keycloak.jose.jws.JWSInputException;
import org.keycloak.protocol.oidc.utils.OIDCResponseType;
import org.keycloak.representations.AccessToken; import org.keycloak.representations.AccessToken;
import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.authorization.JSPolicyRepresentation; import org.keycloak.representations.idm.authorization.JSPolicyRepresentation;
@ -49,6 +50,7 @@ import org.keycloak.representations.idm.authorization.ResourcePermissionRepresen
import org.keycloak.representations.idm.authorization.ResourceRepresentation; import org.keycloak.representations.idm.authorization.ResourceRepresentation;
import org.keycloak.testsuite.AbstractKeycloakTest; import org.keycloak.testsuite.AbstractKeycloakTest;
import org.keycloak.testsuite.util.ClientBuilder; import org.keycloak.testsuite.util.ClientBuilder;
import org.keycloak.testsuite.util.OAuthClient;
import org.keycloak.testsuite.util.RealmBuilder; import org.keycloak.testsuite.util.RealmBuilder;
import org.keycloak.testsuite.util.RoleBuilder; import org.keycloak.testsuite.util.RoleBuilder;
import org.keycloak.testsuite.util.RolesBuilder; import org.keycloak.testsuite.util.RolesBuilder;
@ -74,6 +76,11 @@ public class EntitlementAPITest extends AbstractAuthzTest {
.redirectUris("http://localhost/resource-server-test") .redirectUris("http://localhost/resource-server-test")
.defaultRoles("uma_protection") .defaultRoles("uma_protection")
.directAccessGrants()) .directAccessGrants())
.client(ClientBuilder.create().clientId("test-client")
.secret("secret")
.authorizationServicesEnabled(true)
.redirectUris("http://localhost/test-client")
.directAccessGrants())
.build()); .build());
} }
@ -155,7 +162,7 @@ public class EntitlementAPITest extends AbstractAuthzTest {
request.setMetadata(metadata); request.setMetadata(metadata);
EntitlementResponse response = getAuthzClient().entitlement(authzClient.obtainAccessToken("marta", "password").getToken()).get("resource-server-test", request); EntitlementResponse response = getAuthzClient().entitlement(authzClient.obtainAccessToken("marta", "password").getToken()).get("resource-server-test", request);
AccessToken rpt = toAccessToken(response); AccessToken rpt = toAccessToken(response.getRpt());
List<Permission> permissions = rpt.getAuthorization().getPermissions(); List<Permission> permissions = rpt.getAuthorization().getPermissions();
@ -175,7 +182,7 @@ public class EntitlementAPITest extends AbstractAuthzTest {
request.setRpt(response.getRpt()); request.setRpt(response.getRpt());
response = getAuthzClient().entitlement(authzClient.obtainAccessToken("marta", "password").getToken()).get("resource-server-test", request); response = getAuthzClient().entitlement(authzClient.obtainAccessToken("marta", "password").getToken()).get("resource-server-test", request);
rpt = toAccessToken(response); rpt = toAccessToken(response.getRpt());
permissions = rpt.getAuthorization().getPermissions(); permissions = rpt.getAuthorization().getPermissions();
@ -199,7 +206,7 @@ public class EntitlementAPITest extends AbstractAuthzTest {
request.setRpt(response.getRpt()); request.setRpt(response.getRpt());
response = getAuthzClient().entitlement(authzClient.obtainAccessToken("marta", "password").getToken()).get("resource-server-test", request); response = getAuthzClient().entitlement(authzClient.obtainAccessToken("marta", "password").getToken()).get("resource-server-test", request);
rpt = toAccessToken(response); rpt = toAccessToken(response.getRpt());
permissions = rpt.getAuthorization().getPermissions(); permissions = rpt.getAuthorization().getPermissions();
@ -222,7 +229,7 @@ public class EntitlementAPITest extends AbstractAuthzTest {
request.setRpt(response.getRpt()); request.setRpt(response.getRpt());
response = getAuthzClient().entitlement(authzClient.obtainAccessToken("marta", "password").getToken()).get("resource-server-test", request); response = getAuthzClient().entitlement(authzClient.obtainAccessToken("marta", "password").getToken()).get("resource-server-test", request);
rpt = toAccessToken(response); rpt = toAccessToken(response.getRpt());
permissions = rpt.getAuthorization().getPermissions(); permissions = rpt.getAuthorization().getPermissions();
@ -234,8 +241,21 @@ public class EntitlementAPITest extends AbstractAuthzTest {
assertEquals("Resource 12", permissions.get(4).getResourceSetName()); assertEquals("Resource 12", permissions.get(4).getResourceSetName());
} }
@Test
public void testResourceServerAsAudience() throws Exception {
EntitlementRequest request = new EntitlementRequest();
request.addPermission(new PermissionRequest("Resource 1"));
String accessToken = new OAuthClient().realm("authz-test").clientId("test-client").doGrantAccessTokenRequest("secret", "marta", "password").getAccessToken();
EntitlementResponse response = getAuthzClient().entitlement(accessToken).get("resource-server-test", request);
AccessToken rpt = toAccessToken(response.getRpt());
assertEquals("resource-server-test", rpt.getAudience()[0]);
}
private void assertResponse(AuthorizationRequestMetadata metadata, Supplier<EntitlementResponse> responseSupplier) { private void assertResponse(AuthorizationRequestMetadata metadata, Supplier<EntitlementResponse> responseSupplier) {
AccessToken.Authorization authorization = toAccessToken(responseSupplier.get()).getAuthorization(); AccessToken.Authorization authorization = toAccessToken(responseSupplier.get().getRpt()).getAuthorization();
List<Permission> permissions = authorization.getPermissions(); List<Permission> permissions = authorization.getPermissions();
@ -251,17 +271,6 @@ public class EntitlementAPITest extends AbstractAuthzTest {
} }
} }
private AccessToken toAccessToken(EntitlementResponse response) {
AccessToken accessToken;
try {
accessToken = new JWSInput(response.getRpt()).readJsonContent(AccessToken.class);
} catch (JWSInputException cause) {
throw new RuntimeException("Failed to deserialize RPT", cause);
}
return accessToken;
}
private RealmResource getRealm() throws Exception { private RealmResource getRealm() throws Exception {
return adminClient.realm("authz-test"); return adminClient.realm("authz-test");
} }