[KEYCLOAK-13044] Fix owner name representations of UMA tickets for client-owned resources

This commit is contained in:
Eric Rodrigues Pires 2020-02-19 14:37:03 -03:00 committed by Pedro Igor
parent 4a88c6472e
commit de9a0a0a4a
5 changed files with 230 additions and 12 deletions

View file

@ -1018,10 +1018,15 @@ public class ModelToRepresentation {
representation.setResourceName(resource.getName());
KeycloakSession keycloakSession = authorization.getKeycloakSession();
RealmModel realm = authorization.getRealm();
UserModel owner = keycloakSession.users().getUserById(ticket.getOwner(), realm);
UserModel userOwner = keycloakSession.users().getUserById(ticket.getOwner(), realm);
UserModel requester = keycloakSession.users().getUserById(ticket.getRequester(), realm);
representation.setRequesterName(requester.getUsername());
representation.setOwnerName(owner.getUsername());
if (userOwner != null) {
representation.setOwnerName(userOwner.getUsername());
} else {
ClientModel clientOwner = realm.getClientById(ticket.getOwner());
representation.setOwnerName(clientOwner.getClientId());
}
}
Scope scope = ticket.getScope();

View file

@ -27,6 +27,7 @@ import org.keycloak.authorization.model.Scope;
import org.keycloak.authorization.policy.evaluation.Result;
import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.representations.AccessToken;
import org.keycloak.representations.idm.authorization.DecisionEffect;
@ -196,11 +197,19 @@ public class PolicyEvaluationResponseBuilder {
if (!tickets.isEmpty()) {
KeycloakSession keycloakSession = authorization.getKeycloakSession();
RealmModel realm = authorization.getRealm();
PermissionTicket ticket = tickets.get(0);
UserModel owner = keycloakSession.users().getUserById(ticket.getOwner(), authorization.getRealm());
UserModel requester = keycloakSession.users().getUserById(ticket.getRequester(), authorization.getRealm());
UserModel userOwner = keycloakSession.users().getUserById(ticket.getOwner(), realm);
UserModel requester = keycloakSession.users().getUserById(ticket.getRequester(), realm);
String resourceOwner;
if (userOwner != null) {
resourceOwner = getUserEmailOrUserName(userOwner);
} else {
ClientModel clientOwner = realm.getClientById(ticket.getOwner());
resourceOwner = clientOwner.getClientId();
}
representation.setDescription("Resource owner (" + getUserEmailOrUserName(owner) + ") grants access to " + getUserEmailOrUserName(requester));
representation.setDescription("Resource owner (" + resourceOwner + ") grants access to " + getUserEmailOrUserName(requester));
} else {
String description = representation.getDescription();

View file

@ -225,7 +225,9 @@ public class AuthorizationBean {
public class ResourceBean {
private final ResourceServerBean resourceServer;
private final UserModel owner;
private final String ownerName;
private final UserModel userOwner;
private ClientModel clientOwner;
private Resource resource;
private Map<String, RequesterBean> permissions = new HashMap<>();
private Collection<RequesterBean> shares;
@ -234,7 +236,15 @@ public class AuthorizationBean {
RealmModel realm = authorization.getRealm();
resourceServer = new ResourceServerBean(realm.getClientById(resource.getResourceServer().getId()));
this.resource = resource;
owner = authorization.getKeycloakSession().users().getUserById(resource.getOwner(), realm);
userOwner = authorization.getKeycloakSession().users().getUserById(resource.getOwner(), realm);
if (userOwner == null) {
clientOwner = realm.getClientById(resource.getOwner());
ownerName = clientOwner.getClientId();
} else if (userOwner.getEmail() != null) {
ownerName = userOwner.getEmail();
} else {
ownerName = userOwner.getUsername();
}
}
public String getId() {
@ -253,8 +263,16 @@ public class AuthorizationBean {
return resource.getIconUri();
}
public UserModel getOwner() {
return owner;
public String getOwnerName() {
return ownerName;
}
public UserModel getUserOwner() {
return userOwner;
}
public ClientModel getClientOwner() {
return clientOwner;
}
public List<ScopeRepresentation> getScopes() {
@ -279,7 +297,11 @@ public class AuthorizationBean {
filters.put("type", new String[] {"uma"});
filters.put("resource", new String[] {this.resource.getId()});
filters.put("owner", new String[] {getOwner().getId()});
if (getUserOwner() != null) {
filters.put("owner", new String[] {getUserOwner().getId()});
} else {
filters.put("owner", new String[] {getClientOwner().getId()});
}
List<Policy> policies = authorization.getStoreFactory().getPolicyStore().findByResourceServer(filters, getResourceServer().getId(), -1, -1);

View file

@ -0,0 +1,182 @@
package org.keycloak.testsuite.authz;
import org.jboss.resteasy.spi.ResteasyUriInfo;
import org.junit.Assert;
import org.junit.Test;
import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.authorization.AuthorizationProvider;
import org.keycloak.authorization.client.resource.PermissionResource;
import org.keycloak.forms.account.freemarker.model.AuthorizationBean;
import org.keycloak.forms.account.freemarker.model.AuthorizationBean.ResourceBean;
import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.UserModel;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.representations.idm.authorization.*;
import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude;
import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude.AuthServer;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.List;
@AuthServerContainerExclude(AuthServer.REMOTE)
public class UmaRepresentationTest extends AbstractResourceServerTest {
private ResourceRepresentation resource;
private PermissionResource permission;
private void createPermissionTicket() {
PermissionTicketRepresentation ticket = new PermissionTicketRepresentation();
ticket.setOwner(resource.getOwner().getId());
ticket.setResource(resource.getId());
ticket.setRequesterName("kolo");
ticket.setScopeName("ScopeA");
ticket.setGranted(true);
permission.create(ticket);
}
@Test
public void testCanRepresentPermissionTicketWithNamesOfResourceOwnedByUser() throws Exception {
resource = addResource("Resource A", "marta", true, "ScopeA");
permission = getAuthzClient().protection("marta", "password").permission();
createPermissionTicket();
List<PermissionTicketRepresentation> permissionTickets = permission.find(resource.getId(), null, null, null, null, true, null, null);
Assert.assertFalse(permissionTickets.isEmpty());
Assert.assertEquals(1, permissionTickets.size());
PermissionTicketRepresentation ticket = permissionTickets.get(0);
Assert.assertEquals(ticket.getOwnerName(), "marta");
Assert.assertEquals(ticket.getRequesterName(), "kolo");
Assert.assertEquals(ticket.getResourceName(), "Resource A");
Assert.assertEquals(ticket.getScopeName(), "ScopeA");
Assert.assertTrue(ticket.isGranted());
}
@Test
public void testCanRepresentPermissionTicketWithNamesOfResourceOwnedByClient() throws Exception {
resource = addResource("Resource A", getClient(getRealm()).toRepresentation().getId(), true, "ScopeA");
permission = getAuthzClient().protection().permission();
createPermissionTicket();
List<PermissionTicketRepresentation> permissionTickets = permission.find(resource.getId(), null, null, null, null, true, null, null);
Assert.assertFalse(permissionTickets.isEmpty());
Assert.assertEquals(1, permissionTickets.size());
PermissionTicketRepresentation ticket = permissionTickets.get(0);
Assert.assertEquals(ticket.getOwnerName(), "resource-server-test");
Assert.assertEquals(ticket.getRequesterName(), "kolo");
Assert.assertEquals(ticket.getResourceName(), "Resource A");
Assert.assertEquals(ticket.getScopeName(), "ScopeA");
Assert.assertTrue(ticket.isGranted());
}
@Test
public void testCanRepresentPolicyResultGrantOfResourceOwnedByUser() throws Exception {
resource = addResource("Resource A", "marta", true, "ScopeA");
permission = getAuthzClient().protection("marta", "password").permission();
createPermissionTicket();
RealmResource realm = getRealm();
String resourceServerId = getClient(realm).toRepresentation().getId();
UserRepresentation user = realm.users().search("kolo").get(0);
PolicyEvaluationRequest request = new PolicyEvaluationRequest();
request.setUserId(user.getId());
request.setClientId(resourceServerId);
request.addResource("Resource A", "ScopeA");
PolicyEvaluationResponse result = getClient(realm).authorization().policies().evaluate(request);
Assert.assertEquals(result.getStatus(), DecisionEffect.PERMIT);
List<PolicyEvaluationResponse.EvaluationResultRepresentation> evaluations = result.getResults();
Assert.assertFalse(evaluations.isEmpty());
Assert.assertEquals(1, evaluations.size());
List<PolicyEvaluationResponse.PolicyResultRepresentation> policies = evaluations.get(0).getPolicies();
Assert.assertFalse(evaluations.isEmpty());
Assert.assertEquals(1, evaluations.size());
String description = policies.get(0).getPolicy().getDescription();
Assert.assertTrue(description.startsWith("Resource owner (marta) grants access"));
}
@Test
public void testCanRepresentPolicyResultGrantOfResourceOwnedByClient() throws Exception {
resource = addResource("Resource A", getClient(getRealm()).toRepresentation().getId(), true, "ScopeA");
permission = getAuthzClient().protection().permission();
createPermissionTicket();
RealmResource realm = getRealm();
String resourceServerId = getClient(realm).toRepresentation().getId();
UserRepresentation user = realm.users().search("kolo").get(0);
PolicyEvaluationRequest request = new PolicyEvaluationRequest();
request.setUserId(user.getId());
request.setClientId(resourceServerId);
request.addResource("Resource A", "ScopeA");
PolicyEvaluationResponse result = getClient(realm).authorization().policies().evaluate(request);
Assert.assertEquals(result.getStatus(), DecisionEffect.PERMIT);
List<PolicyEvaluationResponse.EvaluationResultRepresentation> evaluations = result.getResults();
Assert.assertFalse(evaluations.isEmpty());
Assert.assertEquals(1, evaluations.size());
List<PolicyEvaluationResponse.PolicyResultRepresentation> policies = evaluations.get(0).getPolicies();
Assert.assertFalse(evaluations.isEmpty());
Assert.assertEquals(1, evaluations.size());
String description = policies.get(0).getPolicy().getDescription();
Assert.assertTrue(description.startsWith("Resource owner (resource-server-test) grants access"));
}
@Test
public void testCanRepresentResourceBeanOfResourceOwnedByUser() throws Exception {
resource = addResource("Resource A", "marta", true, "ScopeA");
testingClient.server().run(UmaRepresentationTest::testCanRepresentResourceBeanOfResourceOwnedByUser);
}
public static void testCanRepresentResourceBeanOfResourceOwnedByUser(KeycloakSession session) {
session.getContext().setRealm(session.realms().getRealmByName("authz-test"));
AuthorizationProvider authorization = session.getProvider(AuthorizationProvider.class);
AuthorizationBean authorizationBean = new AuthorizationBean(session, null, session.getContext().getUri());
ClientModel client = session.getContext().getRealm().getClientByClientId("resource-server-test");
UserModel user = session.userStorageManager().getUserByUsername("marta", session.getContext().getRealm());
ResourceBean resourceBean = authorizationBean.new ResourceBean(
authorization.getStoreFactory().getResourceStore().findByName(
"Resource A", user.getId(), client.getId()
)
);
Assert.assertEquals("Resource A", resourceBean.getName());
Assert.assertEquals("marta", resourceBean.getOwnerName());
Assert.assertNotNull(resourceBean.getUserOwner());
Assert.assertEquals("marta", resourceBean.getUserOwner().getUsername());
Assert.assertNull(resourceBean.getClientOwner());
}
@Test
public void testCanRepresentResourceBeanOfResourceOwnedByClient() throws Exception {
resource = addResource("Resource A", getClient(getRealm()).toRepresentation().getId(), true, "ScopeA");
testingClient.server().run(UmaRepresentationTest::testCanRepresentResourceBeanOfResourceOwnedByClient);
}
public static void testCanRepresentResourceBeanOfResourceOwnedByClient(KeycloakSession session) {
session.getContext().setRealm(session.realms().getRealmByName("authz-test"));
AuthorizationProvider authorization = session.getProvider(AuthorizationProvider.class);
AuthorizationBean authorizationBean = new AuthorizationBean(session, null, session.getContext().getUri());
ClientModel client = session.getContext().getRealm().getClientByClientId("resource-server-test");
ResourceBean resourceBean = authorizationBean.new ResourceBean(
authorization.getStoreFactory().getResourceStore().findByName(
"Resource A", client.getId(), client.getId()
)
);
Assert.assertEquals("Resource A", resourceBean.getName());
Assert.assertEquals("resource-server-test", resourceBean.getOwnerName());
Assert.assertNotNull(resourceBean.getClientOwner());
Assert.assertEquals("resource-server-test", resourceBean.getClientOwner().getClientId());
Assert.assertNull(resourceBean.getUserOwner());
}
}

View file

@ -262,7 +262,7 @@
<#if resource.displayName??>${resource.displayName}<#else>${resource.name}</#if>
</td>
<td>
<#if resource.owner.email??>${resource.owner.email}<#else>${resource.owner.username}</#if>
${resource.ownerName}
</td>
<td>
<#if resource.resourceServer.baseUri??>
@ -362,7 +362,7 @@
<#if resource.displayName??>${resource.displayName}<#else>${resource.name}</#if>
</td>
<td>
<#if resource.owner.email??>${resource.owner.email}<#else>${resource.owner.username}</#if>
${resource.ownerName}
</td>
<td>
<ul>