[KEYCLOAK-9600] - Find by name in authz client returning wrong resource

This commit is contained in:
Pedro Igor 2019-12-19 18:25:49 -03:00 committed by Stian Thorgersen
parent 00a36e5f7b
commit 658a083a0c
6 changed files with 70 additions and 16 deletions

View file

@ -128,7 +128,7 @@ public class ProtectedResource {
* @return a {@link ResourceRepresentation} * @return a {@link ResourceRepresentation}
*/ */
public ResourceRepresentation findByName(String name) { public ResourceRepresentation findByName(String name) {
List<ResourceRepresentation> representations = find(null, name, null, configuration.getResource(), null, null, false, true, null, null); List<ResourceRepresentation> representations = find(null, name, null, configuration.getResource(), null, null, false, true, true, null, null);
if (representations.isEmpty()) { if (representations.isEmpty()) {
return null; return null;
@ -145,7 +145,7 @@ public class ProtectedResource {
* @return a {@link ResourceRepresentation} * @return a {@link ResourceRepresentation}
*/ */
public ResourceRepresentation findByName(String name, String ownerId) { public ResourceRepresentation findByName(String name, String ownerId) {
List<ResourceRepresentation> representations = find(null, name, null, ownerId, null, null, false, true,null, null); List<ResourceRepresentation> representations = find(null, name, null, ownerId, null, null, false, true, true, null, null);
if (representations.isEmpty()) { if (representations.isEmpty()) {
return null; return null;
@ -172,7 +172,7 @@ public class ProtectedResource {
Callable<String[]> callable = new Callable<String[]>() { Callable<String[]> callable = new Callable<String[]>() {
@Override @Override
public String[] call() throws Exception { public String[] call() throws Exception {
return (String[]) createFindRequest(id, name, uri, owner, type, scope, matchingUri, false, firstResult, maxResult).response().json(String[].class).execute(); return (String[]) createFindRequest(id, name, uri, owner, type, scope, matchingUri, false, false, firstResult, maxResult).response().json(String[].class).execute();
} }
}; };
try { try {
@ -183,8 +183,8 @@ public class ProtectedResource {
} }
/** /**
* Query the server for any resource with the matching arguments. * <p>Query the server for any resource with the matching arguments, where queries by name are partial.
* *
* @param id the resource id * @param id the resource id
* @param name the resource name * @param name the resource name
* @param uri the resource uri * @param uri the resource uri
@ -198,11 +198,31 @@ public class ProtectedResource {
* @return a list of resource representations or an array of strings representing resource ids, depending on the generic type * @return a list of resource representations or an array of strings representing resource ids, depending on the generic type
*/ */
public <R> R find(final String id, final String name, final String uri, final String owner, final String type, final String scope, final boolean matchingUri, final boolean deep, final Integer firstResult, final Integer maxResult) { public <R> R find(final String id, final String name, final String uri, final String owner, final String type, final String scope, final boolean matchingUri, final boolean deep, final Integer firstResult, final Integer maxResult) {
return find(id, name, uri, owner, type, scope, matchingUri, false, deep, firstResult, maxResult);
}
/**
* Query the server for any resource with the matching arguments.
*
* @param id the resource id
* @param name the resource name
* @param uri the resource uri
* @param owner the resource owner
* @param type the resource type
* @param scope the resource scope
* @param matchingUri the resource uri. Use this parameter to lookup a resource that best match the given uri
* @param exactName if the the {@code name} provided should have a exact match
* @param deep if the result should be a list of resource representations with details about the resource. If false, only ids are returned
* @param firstResult the position of the first resource to retrieve
* @param maxResult the maximum number of resources to retrieve
* @return a list of resource representations or an array of strings representing resource ids, depending on the generic type
*/
public <R> R find(final String id, final String name, final String uri, final String owner, final String type, final String scope, final boolean matchingUri, final boolean exactName, final boolean deep, final Integer firstResult, final Integer maxResult) {
if (deep) { if (deep) {
Callable<List<ResourceRepresentation>> callable = new Callable<List<ResourceRepresentation>>() { Callable<List<ResourceRepresentation>> callable = new Callable<List<ResourceRepresentation>>() {
@Override @Override
public List<ResourceRepresentation> call() { public List<ResourceRepresentation> call() {
return (List<ResourceRepresentation>) createFindRequest(id, name, uri, owner, type, scope, matchingUri, deep, firstResult, maxResult).response().json(new TypeReference<List<ResourceRepresentation>>() { return (List<ResourceRepresentation>) createFindRequest(id, name, uri, owner, type, scope, matchingUri, exactName, deep, firstResult, maxResult).response().json(new TypeReference<List<ResourceRepresentation>>() {
}).execute(); }).execute();
} }
}; };
@ -257,7 +277,7 @@ public class ProtectedResource {
* @param uri the resource uri * @param uri the resource uri
*/ */
public List<ResourceRepresentation> findByUri(String uri) { public List<ResourceRepresentation> findByUri(String uri) {
return find(null, null, uri, null, null, null, false, true, null, null); return find(null, null, uri, null, null, null, false, false, true, null, null);
} }
/** /**
@ -268,10 +288,10 @@ public class ProtectedResource {
* @return a list of resources * @return a list of resources
*/ */
public List<ResourceRepresentation> findByMatchingUri(String uri) { public List<ResourceRepresentation> findByMatchingUri(String uri) {
return find(null, null, uri, null, null, null, true, true,null, null); return find(null, null, uri, null, null, null, true, false, true,null, null);
} }
private HttpMethod createFindRequest(String id, String name, String uri, String owner, String type, String scope, boolean matchingUri, boolean deep, Integer firstResult, Integer maxResult) { private HttpMethod createFindRequest(String id, String name, String uri, String owner, String type, String scope, boolean matchingUri, boolean exactName, boolean deep, Integer firstResult, Integer maxResult) {
return http.get(serverConfiguration.getResourceRegistrationEndpoint()) return http.get(serverConfiguration.getResourceRegistrationEndpoint())
.authorizationBearer(pat.call()) .authorizationBearer(pat.call())
.param("_id", id) .param("_id", id)
@ -281,6 +301,7 @@ public class ProtectedResource {
.param("type", type) .param("type", type)
.param("scope", scope) .param("scope", scope)
.param("matchingUri", Boolean.valueOf(matchingUri).toString()) .param("matchingUri", Boolean.valueOf(matchingUri).toString())
.param("exactName", Boolean.valueOf(exactName).toString())
.param("deep", Boolean.toString(deep)) .param("deep", Boolean.toString(deep))
.param("first", firstResult != null ? firstResult.toString() : null) .param("first", firstResult != null ? firstResult.toString() : null)
.param("max", maxResult != null ? maxResult.toString() : Integer.toString(-1)); .param("max", maxResult != null ? maxResult.toString() : Integer.toString(-1));

View file

@ -217,9 +217,9 @@ public class JPAResourceStore implements ResourceStore {
predicates.add(root.get(name).in(value)); predicates.add(root.get(name).in(value));
} else if ("scope".equals(name)) { } else if ("scope".equals(name)) {
predicates.add(root.join("scopes").get("id").in(value)); predicates.add(root.join("scopes").get("id").in(value));
} else if ("ownerManagedAccess".equals(name)) { } else if ("ownerManagedAccess".equals(name) && value.length > 0) {
predicates.add(builder.equal(root.get(name), Boolean.valueOf(value[0]))); predicates.add(builder.equal(root.get(name), Boolean.valueOf(value[0])));
} else if ("uri".equals(name)) { } else if ("uri".equals(name) && value.length > 0 && value[0] != null) {
predicates.add(builder.lower(root.join("uris")).in(value[0].toLowerCase())); predicates.add(builder.lower(root.join("uris")).in(value[0].toLowerCase()));
} else if ("uri_not_null".equals(name)) { } else if ("uri_not_null".equals(name)) {
// predicates.add(builder.isNotEmpty(root.get("uris"))); looks like there is a bug in hibernate and this line doesn't work: https://hibernate.atlassian.net/browse/HHH-6686 // predicates.add(builder.isNotEmpty(root.get("uris"))); looks like there is a bug in hibernate and this line doesn't work: https://hibernate.atlassian.net/browse/HHH-6686
@ -228,8 +228,13 @@ public class JPAResourceStore implements ResourceStore {
predicates.add(builder.notEqual(urisSize, 0)); predicates.add(builder.notEqual(urisSize, 0));
} else if ("owner".equals(name)) { } else if ("owner".equals(name)) {
predicates.add(root.get(name).in(value)); predicates.add(root.get(name).in(value));
} else { } else if (!Resource.EXACT_NAME.equals(name)) {
predicates.add(builder.like(builder.lower(root.get(name)), "%" + value[0].toLowerCase() + "%")); if ("name".equals(name) && attributes.containsKey(Resource.EXACT_NAME) && Boolean.valueOf(attributes.get(Resource.EXACT_NAME)[0])
&& value.length > 0 && value[0] != null) {
predicates.add(builder.equal(builder.lower(root.get(name)), value[0].toLowerCase()));
} else if (value.length > 0 && value[0] != null) {
predicates.add(builder.like(builder.lower(root.get(name)), "%" + value[0].toLowerCase() + "%"));
}
} }
}); });

View file

@ -29,6 +29,8 @@ import java.util.Set;
*/ */
public interface Resource { public interface Resource {
String EXACT_NAME = "EXACT_NAME";
/** /**
* Returns the unique identifier for this instance. * Returns the unique identifier for this instance.
* *

View file

@ -335,10 +335,11 @@ public class ResourceSetService {
@QueryParam("type") String type, @QueryParam("type") String type,
@QueryParam("scope") String scope, @QueryParam("scope") String scope,
@QueryParam("matchingUri") Boolean matchingUri, @QueryParam("matchingUri") Boolean matchingUri,
@QueryParam("exactName") Boolean exactName,
@QueryParam("deep") Boolean deep, @QueryParam("deep") Boolean deep,
@QueryParam("first") Integer firstResult, @QueryParam("first") Integer firstResult,
@QueryParam("max") Integer maxResult) { @QueryParam("max") Integer maxResult) {
return find(id, name, uri, owner, type, scope, matchingUri, deep, firstResult, maxResult, (BiFunction<Resource, Boolean, ResourceRepresentation>) (resource, deep1) -> toRepresentation(resource, resourceServer, authorization, deep1)); return find(id, name, uri, owner, type, scope, matchingUri, exactName, deep, firstResult, maxResult, (BiFunction<Resource, Boolean, ResourceRepresentation>) (resource, deep1) -> toRepresentation(resource, resourceServer, authorization, deep1));
} }
public Response find(@QueryParam("_id") String id, public Response find(@QueryParam("_id") String id,
@ -348,6 +349,7 @@ public class ResourceSetService {
@QueryParam("type") String type, @QueryParam("type") String type,
@QueryParam("scope") String scope, @QueryParam("scope") String scope,
@QueryParam("matchingUri") Boolean matchingUri, @QueryParam("matchingUri") Boolean matchingUri,
@QueryParam("exactName") Boolean exactName,
@QueryParam("deep") Boolean deep, @QueryParam("deep") Boolean deep,
@QueryParam("first") Integer firstResult, @QueryParam("first") Integer firstResult,
@QueryParam("max") Integer maxResult, @QueryParam("max") Integer maxResult,
@ -368,6 +370,10 @@ public class ResourceSetService {
if (name != null && !"".equals(name.trim())) { if (name != null && !"".equals(name.trim())) {
search.put("name", new String[] {name}); search.put("name", new String[] {name});
if (exactName != null && exactName) {
search.put(Resource.EXACT_NAME, new String[] {Boolean.TRUE.toString()});
}
} }
if (uri != null && !"".equals(uri.trim())) { if (uri != null && !"".equals(uri.trim())) {

View file

@ -123,14 +123,15 @@ public class ResourceService {
@QueryParam("type") String type, @QueryParam("type") String type,
@QueryParam("scope") String scope, @QueryParam("scope") String scope,
@QueryParam("matchingUri") Boolean matchingUri, @QueryParam("matchingUri") Boolean matchingUri,
@QueryParam("exactName") Boolean exactName,
@QueryParam("deep") Boolean deep, @QueryParam("deep") Boolean deep,
@QueryParam("first") Integer firstResult, @QueryParam("first") Integer firstResult,
@QueryParam("max") Integer maxResult) { @QueryParam("max") Integer maxResult) {
if(deep != null && deep) { if(deep != null && deep) {
return resourceManager.find(id, name, uri, owner, type, scope, matchingUri, deep, firstResult, maxResult); return resourceManager.find(id, name, uri, owner, type, scope, matchingUri, exactName, deep, firstResult, maxResult);
} else { } else {
return resourceManager.find(id, name, uri, owner, type, scope, matchingUri, deep, firstResult, maxResult, (BiFunction<Resource, Boolean, String>) (resource, deep1) -> resource.getId()); return resourceManager.find(id, name, uri, owner, type, scope, matchingUri, exactName, deep, firstResult, maxResult, (BiFunction<Resource, Boolean, String>) (resource, deep1) -> resource.getId());
} }
} }

View file

@ -18,6 +18,7 @@ package org.keycloak.testsuite.authz;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
@ -229,6 +230,24 @@ public class AuthzClientCredentialsTest extends AbstractAuthzTest {
assertEquals(1, userSessions.size()); assertEquals(1, userSessions.size());
} }
@Test
public void testFindByName() {
AuthzClient authzClient = getAuthzClient("default-session-keycloak.json");
ProtectionResource protection = authzClient.protection();
protection.resource().create(new ResourceRepresentation("Admin Resources"));
protection.resource().create(new ResourceRepresentation("Resource"));
ResourceRepresentation resource = authzClient.protection().resource().findByName("Resource");
assertEquals("Resource", resource.getName());
ResourceRepresentation adminResource = authzClient.protection().resource().findByName("Admin Resources");
assertEquals("Admin Resources", adminResource.getName());
assertNotEquals(resource.getId(), adminResource.getId());
}
private RealmBuilder configureRealm(RealmBuilder builder, ClientBuilder clientBuilder) { private RealmBuilder configureRealm(RealmBuilder builder, ClientBuilder clientBuilder) {
return builder return builder
.roles(RolesBuilder.create().realmRole(new RoleRepresentation("uma_authorization", "", false))) .roles(RolesBuilder.create().realmRole(new RoleRepresentation("uma_authorization", "", false)))