KEYCLOAK-16932 Authorization map storage
This commit is contained in:
parent
8b3e77bf81
commit
1e2db74d86
72 changed files with 3840 additions and 433 deletions
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
|
@ -160,7 +160,7 @@ jobs:
|
|||
run: |
|
||||
declare -A PARAMS TESTGROUP
|
||||
PARAMS["quarkus"]="-Pauth-server-quarkus"
|
||||
PARAMS["undertow-map"]="-Pauth-server-undertow -Dkeycloak.client.provider=map -Dkeycloak.group.provider=map -Dkeycloak.role.provider=map -Dkeycloak.authSession.provider=map -Dkeycloak.user.provider=map -Dkeycloak.clientScope.provider=map -Dkeycloak.realm.provider=map"
|
||||
PARAMS["undertow-map"]="-Pauth-server-undertow -Dkeycloak.client.provider=map -Dkeycloak.group.provider=map -Dkeycloak.role.provider=map -Dkeycloak.authSession.provider=map -Dkeycloak.user.provider=map -Dkeycloak.clientScope.provider=map -Dkeycloak.realm.provider=map -Dkeycloak.authorization.provider=map"
|
||||
PARAMS["wildfly"]="-Pauth-server-wildfly"
|
||||
TESTGROUP["group1"]="-Dtest=!**.crossdc.**,!**.cluster.**,%regex[org.keycloak.testsuite.(a[abc]|ad[a-l]|[^a-q]).*]" # Tests alphabetically before admin tests and those after "r"
|
||||
TESTGROUP["group2"]="-Dtest=!**.crossdc.**,!**.cluster.**,%regex[org.keycloak.testsuite.(ad[^a-l]|a[^a-d]|b).*]" # Admin tests and those starting with "b"
|
||||
|
|
|
@ -280,11 +280,6 @@ public class PolicyAdapter implements Policy, CachedModel<Policy> {
|
|||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFetched(String association) {
|
||||
return modelSupplier.get().isFetched(association);
|
||||
}
|
||||
|
||||
protected Set<Scope> scopes;
|
||||
|
||||
@Override
|
||||
|
|
|
@ -268,11 +268,6 @@ public class ResourceAdapter implements Resource, CachedModel<Resource> {
|
|||
updated.removeAttribute(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFetched(String association) {
|
||||
return modelSupplier.get().isFetched(association);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
|
|
|
@ -572,16 +572,12 @@ public class StoreFactoryCacheSession implements CachedStoreFactoryProvider {
|
|||
}
|
||||
|
||||
@Override
|
||||
public List<Scope> findByResourceServer(Map<String, String[]> attributes, String resourceServerId, int firstResult, int maxResult) {
|
||||
public List<Scope> findByResourceServer(Map<Scope.FilterOption, String[]> attributes, String resourceServerId, int firstResult, int maxResult) {
|
||||
return getScopeStoreDelegate().findByResourceServer(attributes, resourceServerId, firstResult, maxResult);
|
||||
}
|
||||
}
|
||||
|
||||
protected class ResourceCache implements ResourceStore {
|
||||
@Override
|
||||
public Resource create(String name, ResourceServer resourceServer, String owner) {
|
||||
return create(null, name, resourceServer, owner);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Resource create(String id, String name, ResourceServer resourceServer, String owner) {
|
||||
|
@ -706,7 +702,7 @@ public class StoreFactoryCacheSession implements CachedStoreFactoryProvider {
|
|||
}
|
||||
|
||||
@Override
|
||||
public List<Resource> findByResourceServer(Map<String, String[]> attributes, String resourceServerId, int firstResult, int maxResult) {
|
||||
public List<Resource> findByResourceServer(Map<Resource.FilterOption, String[]> attributes, String resourceServerId, int firstResult, int maxResult) {
|
||||
return getResourceStoreDelegate().findByResourceServer(attributes, resourceServerId, firstResult, maxResult);
|
||||
}
|
||||
|
||||
|
@ -962,7 +958,7 @@ public class StoreFactoryCacheSession implements CachedStoreFactoryProvider {
|
|||
}
|
||||
|
||||
@Override
|
||||
public List<Policy> findByResourceServer(Map<String, String[]> attributes, String resourceServerId, int firstResult, int maxResult) {
|
||||
public List<Policy> findByResourceServer(Map<Policy.FilterOption, String[]> attributes, String resourceServerId, int firstResult, int maxResult) {
|
||||
return getPolicyStoreDelegate().findByResourceServer(attributes, resourceServerId, firstResult, maxResult);
|
||||
}
|
||||
|
||||
|
@ -1115,7 +1111,7 @@ public class StoreFactoryCacheSession implements CachedStoreFactoryProvider {
|
|||
|
||||
protected class PermissionTicketCache implements PermissionTicketStore {
|
||||
@Override
|
||||
public long count(Map<String, String> attributes, String resourceServerId) {
|
||||
public long count(Map<PermissionTicket.FilterOption, String> attributes, String resourceServerId) {
|
||||
return getPermissionTicketStoreDelegate().count(attributes, resourceServerId);
|
||||
}
|
||||
|
||||
|
@ -1193,7 +1189,7 @@ public class StoreFactoryCacheSession implements CachedStoreFactoryProvider {
|
|||
}
|
||||
|
||||
@Override
|
||||
public List<PermissionTicket> find(Map<String, String> attributes, String resourceServerId, int firstResult, int maxResult) {
|
||||
public List<PermissionTicket> find(Map<PermissionTicket.FilterOption, String> attributes, String resourceServerId, int firstResult, int maxResult) {
|
||||
return getPermissionTicketStoreDelegate().find(attributes, resourceServerId, firstResult, maxResult);
|
||||
}
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ import javax.persistence.UniqueConstraint;
|
|||
*/
|
||||
@Entity
|
||||
@Table(name = "RESOURCE_SERVER_PERM_TICKET", uniqueConstraints = {
|
||||
@UniqueConstraint(columnNames = {"OWNER", "RESOURCE_SERVER_ID", "RESOURCE_ID", "SCOPE_ID"})
|
||||
@UniqueConstraint(columnNames = {"OWNER", "REQUESTER", "RESOURCE_SERVER_ID", "RESOURCE_ID", "SCOPE_ID"})
|
||||
})
|
||||
@NamedQueries(
|
||||
{
|
||||
|
|
|
@ -18,7 +18,7 @@ package org.keycloak.authorization.jpa.store;
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.EnumMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
@ -58,7 +58,7 @@ public class JPAPermissionTicketStore implements PermissionTicketStore {
|
|||
}
|
||||
|
||||
@Override
|
||||
public long count(Map<String, String> attributes, String resourceServerId) {
|
||||
public long count(Map<PermissionTicket.FilterOption, String> attributes, String resourceServerId) {
|
||||
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
|
||||
CriteriaQuery<Long> querybuilder = builder.createQuery(Long.class);
|
||||
Root<PermissionTicketEntity> root = querybuilder.from(PermissionTicketEntity.class);
|
||||
|
@ -77,46 +77,49 @@ public class JPAPermissionTicketStore implements PermissionTicketStore {
|
|||
private List<Predicate> getPredicates(CriteriaBuilder builder,
|
||||
Root<PermissionTicketEntity> root,
|
||||
String resourceServerId,
|
||||
Map<String, String> attributes) {
|
||||
List<Predicate> predicates = new ArrayList();
|
||||
Map<PermissionTicket.FilterOption, String> attributes) {
|
||||
List<Predicate> predicates = new ArrayList<>();
|
||||
|
||||
if (resourceServerId != null) {
|
||||
predicates.add(builder.equal(root.get("resourceServer").get("id"), resourceServerId));
|
||||
}
|
||||
|
||||
attributes.forEach((name, value) -> {
|
||||
if (PermissionTicket.ID.equals(name)) {
|
||||
predicates.add(root.get(name).in(value));
|
||||
} else if (PermissionTicket.SCOPE.equals(name)) {
|
||||
predicates.add(root.join("scope").get("id").in(value));
|
||||
} else if (PermissionTicket.SCOPE_IS_NULL.equals(name)) {
|
||||
if (Boolean.valueOf(value)) {
|
||||
attributes.forEach((filterOption, value) -> {
|
||||
switch (filterOption) {
|
||||
case ID:
|
||||
case OWNER:
|
||||
case REQUESTER:
|
||||
predicates.add(builder.equal(root.get(filterOption.getName()), value));
|
||||
break;
|
||||
case SCOPE_ID:
|
||||
case RESOURCE_ID:
|
||||
case RESOURCE_NAME:
|
||||
case POLICY_ID:
|
||||
String[] predicateValues = filterOption.getName().split("\\.");
|
||||
predicates.add(root.join(predicateValues[0]).get(predicateValues[1]).in(value));
|
||||
break;
|
||||
case SCOPE_IS_NULL:
|
||||
if (Boolean.parseBoolean(value)) {
|
||||
predicates.add(builder.isNull(root.get("scope")));
|
||||
} else {
|
||||
predicates.add(builder.isNotNull(root.get("scope")));
|
||||
}
|
||||
} else if (PermissionTicket.RESOURCE.equals(name)) {
|
||||
predicates.add(root.join("resource").get("id").in(value));
|
||||
} else if (PermissionTicket.RESOURCE_NAME.equals(name)) {
|
||||
predicates.add(root.join("resource").get("name").in(value));
|
||||
} else if (PermissionTicket.OWNER.equals(name)) {
|
||||
predicates.add(builder.equal(root.get("owner"), value));
|
||||
} else if (PermissionTicket.REQUESTER.equals(name)) {
|
||||
predicates.add(builder.equal(root.get("requester"), value));
|
||||
} else if (PermissionTicket.GRANTED.equals(name)) {
|
||||
if (Boolean.valueOf(value)) {
|
||||
break;
|
||||
case GRANTED:
|
||||
if (Boolean.parseBoolean(value)) {
|
||||
predicates.add(builder.isNotNull(root.get("grantedTimestamp")));
|
||||
} else {
|
||||
predicates.add(builder.isNull(root.get("grantedTimestamp")));
|
||||
}
|
||||
} else if (PermissionTicket.REQUESTER_IS_NULL.equals(name)) {
|
||||
break;
|
||||
case REQUESTER_IS_NULL:
|
||||
predicates.add(builder.isNull(root.get("requester")));
|
||||
} else if (PermissionTicket.POLICY_IS_NOT_NULL.equals(name)) {
|
||||
break;
|
||||
case POLICY_IS_NOT_NULL:
|
||||
predicates.add(builder.isNotNull(root.get("policy")));
|
||||
} else if (PermissionTicket.POLICY.equals(name)) {
|
||||
predicates.add(root.join("policy").get("id").in(value));
|
||||
} else {
|
||||
throw new RuntimeException("Unsupported filter [" + name + "]");
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unsupported filter [" + filterOption + "]");
|
||||
}
|
||||
});
|
||||
return predicates;
|
||||
|
@ -235,7 +238,7 @@ public class JPAPermissionTicketStore implements PermissionTicketStore {
|
|||
}
|
||||
|
||||
@Override
|
||||
public List<PermissionTicket> find(Map<String, String> attributes, String resourceServerId, int firstResult, int maxResult) {
|
||||
public List<PermissionTicket> find(Map<PermissionTicket.FilterOption, String> attributes, String resourceServerId, int firstResult, int maxResult) {
|
||||
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
|
||||
CriteriaQuery<PermissionTicketEntity> querybuilder = builder.createQuery(PermissionTicketEntity.class);
|
||||
Root<PermissionTicketEntity> root = querybuilder.from(PermissionTicketEntity.class);
|
||||
|
@ -264,21 +267,21 @@ public class JPAPermissionTicketStore implements PermissionTicketStore {
|
|||
|
||||
@Override
|
||||
public List<PermissionTicket> findGranted(String userId, String resourceServerId) {
|
||||
HashMap<String, String> filters = new HashMap<>();
|
||||
Map<PermissionTicket.FilterOption, String> filters = new EnumMap<>(PermissionTicket.FilterOption.class);
|
||||
|
||||
filters.put(PermissionTicket.GRANTED, Boolean.TRUE.toString());
|
||||
filters.put(PermissionTicket.REQUESTER, userId);
|
||||
filters.put(PermissionTicket.FilterOption.GRANTED, Boolean.TRUE.toString());
|
||||
filters.put(PermissionTicket.FilterOption.REQUESTER, userId);
|
||||
|
||||
return find(filters, resourceServerId, -1, -1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PermissionTicket> findGranted(String resourceName, String userId, String resourceServerId) {
|
||||
HashMap<String, String> filters = new HashMap<>();
|
||||
Map<PermissionTicket.FilterOption, String> filters = new EnumMap<>(PermissionTicket.FilterOption.class);
|
||||
|
||||
filters.put(PermissionTicket.RESOURCE_NAME, resourceName);
|
||||
filters.put(PermissionTicket.GRANTED, Boolean.TRUE.toString());
|
||||
filters.put(PermissionTicket.REQUESTER, userId);
|
||||
filters.put(PermissionTicket.FilterOption.RESOURCE_NAME, resourceName);
|
||||
filters.put(PermissionTicket.FilterOption.GRANTED, Boolean.TRUE.toString());
|
||||
filters.put(PermissionTicket.FilterOption.REQUESTER, userId);
|
||||
|
||||
return find(filters, resourceServerId, -1, -1);
|
||||
}
|
||||
|
|
|
@ -136,7 +136,7 @@ public class JPAPolicyStore implements PolicyStore {
|
|||
}
|
||||
|
||||
@Override
|
||||
public List<Policy> findByResourceServer(Map<String, String[]> attributes, String resourceServerId, int firstResult, int maxResult) {
|
||||
public List<Policy> findByResourceServer(Map<Policy.FilterOption, String[]> attributes, String resourceServerId, int firstResult, int maxResult) {
|
||||
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
|
||||
CriteriaQuery<PolicyEntity> querybuilder = builder.createQuery(PolicyEntity.class);
|
||||
Root<PolicyEntity> root = querybuilder.from(PolicyEntity.class);
|
||||
|
@ -147,32 +147,46 @@ public class JPAPolicyStore implements PolicyStore {
|
|||
predicates.add(builder.equal(root.get("resourceServer").get("id"), resourceServerId));
|
||||
}
|
||||
|
||||
attributes.forEach((name, value) -> {
|
||||
if ("permission".equals(name)) {
|
||||
if (Boolean.valueOf(value[0])) {
|
||||
attributes.forEach((filterOption, value) -> {
|
||||
switch (filterOption) {
|
||||
case ID:
|
||||
case OWNER:
|
||||
predicates.add(root.get(filterOption.getName()).in(value));
|
||||
break;
|
||||
case SCOPE_ID:
|
||||
case RESOURCE_ID:
|
||||
String[] predicateValues = filterOption.getName().split("\\.");
|
||||
predicates.add(root.join(predicateValues[0]).get(predicateValues[1]).in(value));
|
||||
break;
|
||||
case PERMISSION: {
|
||||
if (Boolean.parseBoolean(value[0])) {
|
||||
predicates.add(root.get("type").in("resource", "scope", "uma"));
|
||||
} else {
|
||||
predicates.add(builder.not(root.get("type").in("resource", "scope", "uma")));
|
||||
}
|
||||
} else if ("id".equals(name)) {
|
||||
predicates.add(root.get(name).in(value));
|
||||
} else if ("owner".equals(name)) {
|
||||
predicates.add(root.get(name).in(value));
|
||||
} else if ("owner_is_not_null".equals(name)) {
|
||||
}
|
||||
break;
|
||||
case OWNER_IS_NOT_NULL:
|
||||
predicates.add(builder.isNotNull(root.get("owner")));
|
||||
} else if ("resource".equals(name)) {
|
||||
predicates.add(root.join("resources").get("id").in(value));
|
||||
} else if ("scope".equals(name)) {
|
||||
predicates.add(root.join("scopes").get("id").in(value));
|
||||
} else if (name.startsWith("config:")) {
|
||||
predicates.add(root.joinMap("config").key().in(name.substring("config:".length())));
|
||||
predicates.add(builder.like(root.joinMap("config").value().as(String.class), "%" + value[0] + "%"));
|
||||
} else {
|
||||
predicates.add(builder.like(builder.lower(root.get(name)), "%" + value[0].toLowerCase() + "%"));
|
||||
break;
|
||||
case CONFIG:
|
||||
if (value.length != 2) {
|
||||
throw new IllegalArgumentException("Config filter option requires value with two items: [config_name, expected_config_value]");
|
||||
}
|
||||
|
||||
predicates.add(root.joinMap("config").key().in(value[0]));
|
||||
predicates.add(builder.like(root.joinMap("config").value().as(String.class), "%" + value[1] + "%"));
|
||||
break;
|
||||
case TYPE:
|
||||
case NAME:
|
||||
predicates.add(builder.like(builder.lower(root.get(filterOption.getName())), "%" + value[0].toLowerCase() + "%"));
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unsupported filter [" + filterOption + "]");
|
||||
}
|
||||
});
|
||||
|
||||
if (!attributes.containsKey("owner") && !attributes.containsKey("owner_is_not_null")) {
|
||||
if (!attributes.containsKey(Policy.FilterOption.OWNER) && !attributes.containsKey(Policy.FilterOption.OWNER_IS_NOT_NULL)) {
|
||||
predicates.add(builder.isNull(root.get("owner")));
|
||||
}
|
||||
|
||||
|
@ -191,15 +205,6 @@ public class JPAPolicyStore implements PolicyStore {
|
|||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Policy> findByResource(final String resourceId, String resourceServerId) {
|
||||
List<Policy> result = new LinkedList<>();
|
||||
|
||||
findByResource(resourceId, resourceServerId, result::add);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void findByResource(String resourceId, String resourceServerId, Consumer<Policy> consumer) {
|
||||
TypedQuery<PolicyEntity> query = entityManager.createNamedQuery("findPolicyIdByResource", PolicyEntity.class);
|
||||
|
@ -216,15 +221,6 @@ public class JPAPolicyStore implements PolicyStore {
|
|||
.forEach(consumer::accept);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Policy> findByResourceType(final String resourceType, String resourceServerId) {
|
||||
List<Policy> result = new LinkedList<>();
|
||||
|
||||
findByResourceType(resourceType, resourceServerId, result::add);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void findByResourceType(String resourceType, String resourceServerId, Consumer<Policy> consumer) {
|
||||
TypedQuery<PolicyEntity> query = entityManager.createNamedQuery("findPolicyIdByResourceType", PolicyEntity.class);
|
||||
|
@ -262,15 +258,6 @@ public class JPAPolicyStore implements PolicyStore {
|
|||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Policy> findByScopeIds(List<String> scopeIds, String resourceId, String resourceServerId) {
|
||||
List<Policy> result = new LinkedList<>();
|
||||
|
||||
findByScopeIds(scopeIds, resourceId, resourceServerId, result::add);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void findByScopeIds(List<String> scopeIds, String resourceId, String resourceServerId, Consumer<Policy> consumer) {
|
||||
// Use separate subquery to handle DB2 and MSSSQL
|
||||
|
|
|
@ -56,11 +56,6 @@ public class JPAResourceStore implements ResourceStore {
|
|||
this.provider = provider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Resource create(String name, ResourceServer resourceServer, String owner) {
|
||||
return create(null, name, resourceServer, owner);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Resource create(String id, String name, ResourceServer resourceServer, String owner) {
|
||||
ResourceEntity entity = new ResourceEntity();
|
||||
|
@ -101,15 +96,6 @@ public class JPAResourceStore implements ResourceStore {
|
|||
return new ResourceAdapter(entity, entityManager, provider.getStoreFactory());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Resource> findByOwner(String ownerId, String resourceServerId) {
|
||||
List<Resource> list = new LinkedList<>();
|
||||
|
||||
findByOwner(ownerId, resourceServerId, list::add);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void findByOwner(String ownerId, String resourceServerId, Consumer<Resource> consumer) {
|
||||
findByOwnerFilter(ownerId, resourceServerId, consumer, -1, -1);
|
||||
|
@ -195,7 +181,7 @@ public class JPAResourceStore implements ResourceStore {
|
|||
}
|
||||
|
||||
@Override
|
||||
public List<Resource> findByResourceServer(Map<String, String[]> attributes, String resourceServerId, int firstResult, int maxResult) {
|
||||
public List<Resource> findByResourceServer(Map<Resource.FilterOption, String[]> attributes, String resourceServerId, int firstResult, int maxResult) {
|
||||
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
|
||||
CriteriaQuery<ResourceEntity> querybuilder = builder.createQuery(ResourceEntity.class);
|
||||
Root<ResourceEntity> root = querybuilder.from(ResourceEntity.class);
|
||||
|
@ -206,29 +192,36 @@ public class JPAResourceStore implements ResourceStore {
|
|||
predicates.add(builder.equal(root.get("resourceServer"), resourceServerId));
|
||||
}
|
||||
|
||||
attributes.forEach((name, value) -> {
|
||||
if ("id".equals(name)) {
|
||||
predicates.add(root.get(name).in(value));
|
||||
} else if ("scope".equals(name)) {
|
||||
attributes.forEach((filterOption, value) -> {
|
||||
switch (filterOption) {
|
||||
case ID:
|
||||
case OWNER:
|
||||
predicates.add(root.get(filterOption.getName()).in(value));
|
||||
break;
|
||||
case SCOPE_ID:
|
||||
predicates.add(root.join("scopes").get("id").in(value));
|
||||
} else if ("ownerManagedAccess".equals(name) && value.length > 0) {
|
||||
predicates.add(builder.equal(root.get(name), Boolean.valueOf(value[0])));
|
||||
} else if ("uri".equals(name) && value.length > 0 && value[0] != null) {
|
||||
break;
|
||||
case OWNER_MANAGED_ACCESS:
|
||||
predicates.add(builder.equal(root.get(filterOption.getName()), Boolean.valueOf(value[0])));
|
||||
break;
|
||||
case URI:
|
||||
predicates.add(builder.lower(root.join("uris")).in(value[0].toLowerCase()));
|
||||
} else if ("uri_not_null".equals(name)) {
|
||||
break;
|
||||
case URI_NOT_NULL:
|
||||
// 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
|
||||
// Workaround
|
||||
Expression<Integer> urisSize = builder.size(root.get("uris"));
|
||||
predicates.add(builder.notEqual(urisSize, 0));
|
||||
} else if ("owner".equals(name)) {
|
||||
predicates.add(root.get(name).in(value));
|
||||
} else if (!Resource.EXACT_NAME.equals(name)) {
|
||||
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() + "%"));
|
||||
}
|
||||
break;
|
||||
case NAME:
|
||||
case TYPE:
|
||||
predicates.add(builder.like(builder.lower(root.get(filterOption.getName())), "%" + value[0].toLowerCase() + "%"));
|
||||
break;
|
||||
case EXACT_NAME:
|
||||
predicates.add(builder.equal(builder.lower(root.get(filterOption.getName())), value[0].toLowerCase()));
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unsupported filter [" + filterOption + "]");
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -251,15 +244,6 @@ public class JPAResourceStore implements ResourceStore {
|
|||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Resource> findByScope(List<String> scopes, String resourceServerId) {
|
||||
List<Resource> result = new ArrayList<>();
|
||||
|
||||
findByScope(scopes, resourceServerId, result::add);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void findByScope(List<String> scopes, String resourceServerId, Consumer<Resource> consumer) {
|
||||
TypedQuery<ResourceEntity> query = entityManager.createNamedQuery("findResourceIdByScope", ResourceEntity.class);
|
||||
|
@ -295,24 +279,6 @@ public class JPAResourceStore implements ResourceStore {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Resource> findByType(String type, String resourceServerId) {
|
||||
List<Resource> list = new LinkedList<>();
|
||||
|
||||
findByType(type, resourceServerId, list::add);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Resource> findByType(String type, String owner, String resourceServerId) {
|
||||
List<Resource> list = new LinkedList<>();
|
||||
|
||||
findByType(type, owner, resourceServerId, list::add);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void findByType(String type, String resourceServerId, Consumer<Resource> consumer) {
|
||||
findByType(type, resourceServerId, resourceServerId, consumer);
|
||||
|
@ -344,15 +310,6 @@ public class JPAResourceStore implements ResourceStore {
|
|||
.forEach(consumer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Resource> findByTypeInstance(String type, String resourceServerId) {
|
||||
List<Resource> list = new LinkedList<>();
|
||||
|
||||
findByTypeInstance(type, resourceServerId, list::add);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void findByTypeInstance(String type, String resourceServerId, Consumer<Resource> consumer) {
|
||||
TypedQuery<ResourceEntity> query = entityManager.createNamedQuery("findResourceIdByTypeInstance", ResourceEntity.class);
|
||||
|
|
|
@ -131,7 +131,7 @@ public class JPAScopeStore implements ScopeStore {
|
|||
}
|
||||
|
||||
@Override
|
||||
public List<Scope> findByResourceServer(Map<String, String[]> attributes, String resourceServerId, int firstResult, int maxResult) {
|
||||
public List<Scope> findByResourceServer(Map<Scope.FilterOption, String[]> attributes, String resourceServerId, int firstResult, int maxResult) {
|
||||
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
|
||||
CriteriaQuery<ScopeEntity> querybuilder = builder.createQuery(ScopeEntity.class);
|
||||
Root<ScopeEntity> root = querybuilder.from(ScopeEntity.class);
|
||||
|
@ -140,11 +140,16 @@ public class JPAScopeStore implements ScopeStore {
|
|||
|
||||
predicates.add(builder.equal(root.get("resourceServer").get("id"), resourceServerId));
|
||||
|
||||
attributes.forEach((name, value) -> {
|
||||
if ("id".equals(name)) {
|
||||
predicates.add(root.get(name).in(value));
|
||||
} else {
|
||||
predicates.add(builder.like(builder.lower(root.get(name)), "%" + value[0].toLowerCase() + "%"));
|
||||
attributes.forEach((filterOption, value) -> {
|
||||
switch (filterOption) {
|
||||
case ID:
|
||||
predicates.add(root.get(filterOption.getName()).in(value));
|
||||
break;
|
||||
case NAME:
|
||||
predicates.add(builder.like(builder.lower(root.get(filterOption.getName())), "%" + value[0].toLowerCase() + "%"));
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unsupported filter [" + filterOption + "]");
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -253,9 +253,4 @@ public class PolicyAdapter extends AbstractAuthorizationModel implements Policy,
|
|||
return em.getReference(PolicyEntity.class, policy.getId());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFetched(String association) {
|
||||
return em.getEntityManagerFactory().getPersistenceUnitUtil().isLoaded(entity, association);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -241,12 +241,6 @@ public class ResourceAdapter extends AbstractAuthorizationModel implements Resou
|
|||
entity.getAttributes().removeAll(toRemove);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFetched(String association) {
|
||||
return em.getEntityManagerFactory().getPersistenceUnitUtil().isLoaded(this.entity, association);
|
||||
}
|
||||
|
||||
|
||||
public static ResourceEntity toEntity(EntityManager em, Resource resource) {
|
||||
if (resource instanceof ResourceAdapter) {
|
||||
return ((ResourceAdapter)resource).getEntity();
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
* Copyright 2021 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.models.map.authorization;
|
||||
|
||||
import org.keycloak.authorization.AuthorizationProvider;
|
||||
import org.keycloak.authorization.model.PermissionTicket;
|
||||
import org.keycloak.authorization.model.Policy;
|
||||
import org.keycloak.authorization.model.Resource;
|
||||
import org.keycloak.authorization.model.ResourceServer;
|
||||
import org.keycloak.authorization.model.Scope;
|
||||
import org.keycloak.authorization.store.PermissionTicketStore;
|
||||
import org.keycloak.authorization.store.PolicyStore;
|
||||
import org.keycloak.authorization.store.ResourceServerStore;
|
||||
import org.keycloak.authorization.store.ResourceStore;
|
||||
import org.keycloak.authorization.store.ScopeStore;
|
||||
import org.keycloak.authorization.store.StoreFactory;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.map.authorization.entity.MapPermissionTicketEntity;
|
||||
import org.keycloak.models.map.authorization.entity.MapPolicyEntity;
|
||||
import org.keycloak.models.map.authorization.entity.MapResourceEntity;
|
||||
import org.keycloak.models.map.authorization.entity.MapResourceServerEntity;
|
||||
import org.keycloak.models.map.authorization.entity.MapScopeEntity;
|
||||
import org.keycloak.models.map.storage.MapStorage;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author mhajas
|
||||
*/
|
||||
public class MapAuthorizationStore implements StoreFactory {
|
||||
|
||||
private final PolicyStore policyStore;
|
||||
private final ResourceServerStore resourceServerStore;
|
||||
private final ResourceStore resourceStore;
|
||||
private final ScopeStore scopeStore;
|
||||
private final PermissionTicketStore permissionTicketStore;
|
||||
private boolean readOnly;
|
||||
|
||||
public MapAuthorizationStore(KeycloakSession session, MapStorage<UUID, MapPermissionTicketEntity, PermissionTicket> permissionTicketStore, MapStorage<UUID, MapPolicyEntity, Policy> policyStore, MapStorage<String, MapResourceServerEntity, ResourceServer> resourceServerStore, MapStorage<UUID, MapResourceEntity, Resource> resourceStore, MapStorage<UUID, MapScopeEntity, Scope> scopeStore, AuthorizationProvider provider) {
|
||||
this.permissionTicketStore = new MapPermissionTicketStore(session, permissionTicketStore, provider);
|
||||
this.policyStore = new MapPolicyStore(session, policyStore, provider);
|
||||
this.resourceServerStore = new MapResourceServerStore(session, resourceServerStore, provider);
|
||||
this.resourceStore = new MapResourceStore(session, resourceStore, provider);
|
||||
this.scopeStore = new MapScopeStore(session, scopeStore, provider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceStore getResourceStore() {
|
||||
return resourceStore;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceServerStore getResourceServerStore() {
|
||||
return resourceServerStore;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScopeStore getScopeStore() {
|
||||
return scopeStore;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PolicyStore getPolicyStore() {
|
||||
return policyStore;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PermissionTicketStore getPermissionTicketStore() {
|
||||
return permissionTicketStore;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReadOnly(boolean readOnly) {
|
||||
this.readOnly = readOnly;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReadOnly() {
|
||||
return readOnly;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* Copyright 2021 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.models.map.authorization;
|
||||
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.authorization.AuthorizationProvider;
|
||||
import org.keycloak.authorization.model.PermissionTicket;
|
||||
import org.keycloak.authorization.model.Policy;
|
||||
import org.keycloak.authorization.model.Resource;
|
||||
import org.keycloak.authorization.model.ResourceServer;
|
||||
import org.keycloak.authorization.model.Scope;
|
||||
import org.keycloak.authorization.store.AuthorizationStoreFactory;
|
||||
import org.keycloak.authorization.store.StoreFactory;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.map.authorization.entity.MapPermissionTicketEntity;
|
||||
import org.keycloak.models.map.authorization.entity.MapPolicyEntity;
|
||||
import org.keycloak.models.map.authorization.entity.MapResourceEntity;
|
||||
import org.keycloak.models.map.authorization.entity.MapResourceServerEntity;
|
||||
import org.keycloak.models.map.authorization.entity.MapScopeEntity;
|
||||
import org.keycloak.models.map.storage.MapStorage;
|
||||
import org.keycloak.models.map.storage.MapStorageProvider;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author mhajas
|
||||
*/
|
||||
public class MapAuthorizationStoreFactory implements AuthorizationStoreFactory {
|
||||
|
||||
private MapStorage<UUID, MapPermissionTicketEntity, PermissionTicket> permissionTicketStore;
|
||||
private MapStorage<UUID, MapPolicyEntity, Policy> policyStore;
|
||||
private MapStorage<String, MapResourceServerEntity, ResourceServer> resourceServerStore;
|
||||
private MapStorage<UUID, MapResourceEntity, Resource> resourceStore;
|
||||
private MapStorage<UUID, MapScopeEntity, Scope> scopeStore;
|
||||
|
||||
@Override
|
||||
public StoreFactory create(KeycloakSession session) {
|
||||
AuthorizationProvider provider = session.getProvider(AuthorizationProvider.class);
|
||||
return new MapAuthorizationStore(session,
|
||||
permissionTicketStore,
|
||||
policyStore,
|
||||
resourceServerStore,
|
||||
resourceStore,
|
||||
scopeStore,
|
||||
provider
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Config.Scope config) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postInit(KeycloakSessionFactory factory) {
|
||||
AuthorizationStoreFactory.super.postInit(factory);
|
||||
|
||||
MapStorageProvider mapStorageProvider = (MapStorageProvider) factory.getProviderFactory(MapStorageProvider.class);
|
||||
permissionTicketStore = mapStorageProvider.getStorage("authzPermissionTickets", UUID.class, MapPermissionTicketEntity.class, PermissionTicket.class);
|
||||
policyStore = mapStorageProvider.getStorage("authzPolicies", UUID.class, MapPolicyEntity.class, Policy.class);
|
||||
resourceServerStore = mapStorageProvider.getStorage("authzResourceServers", String.class, MapResourceServerEntity.class, ResourceServer.class);
|
||||
resourceStore = mapStorageProvider.getStorage("authzResources", UUID.class, MapResourceEntity.class, Resource.class);
|
||||
scopeStore = mapStorageProvider.getStorage("authzScopes", UUID.class, MapScopeEntity.class, Scope.class);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "map";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,320 @@
|
|||
/*
|
||||
* Copyright 2021 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.models.map.authorization;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.authorization.AuthorizationProvider;
|
||||
import org.keycloak.authorization.model.PermissionTicket;
|
||||
import org.keycloak.authorization.model.PermissionTicket.SearchableFields;
|
||||
import org.keycloak.authorization.model.Resource;
|
||||
import org.keycloak.authorization.model.ResourceServer;
|
||||
import org.keycloak.authorization.store.PermissionTicketStore;
|
||||
import org.keycloak.authorization.store.ResourceStore;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.ModelDuplicateException;
|
||||
import org.keycloak.models.map.authorization.adapter.MapPermissionTicketAdapter;
|
||||
import org.keycloak.models.map.authorization.entity.AbstractPermissionTicketEntity;
|
||||
import org.keycloak.models.map.authorization.entity.MapPermissionTicketEntity;
|
||||
import org.keycloak.models.map.common.Serialization;
|
||||
import org.keycloak.models.map.storage.MapKeycloakTransaction;
|
||||
import org.keycloak.models.map.storage.MapStorage;
|
||||
import org.keycloak.models.map.storage.ModelCriteriaBuilder;
|
||||
import org.keycloak.models.map.storage.ModelCriteriaBuilder.Operator;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.EnumMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.keycloak.common.util.StackUtil.getShortStackTrace;
|
||||
import static org.keycloak.utils.StreamsUtil.distinctByKey;
|
||||
import static org.keycloak.utils.StreamsUtil.paginatedStream;
|
||||
|
||||
public class MapPermissionTicketStore implements PermissionTicketStore {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(MapPermissionTicketStore.class);
|
||||
private final AuthorizationProvider authorizationProvider;
|
||||
final MapKeycloakTransaction<UUID, MapPermissionTicketEntity, PermissionTicket> tx;
|
||||
private final MapStorage<UUID, MapPermissionTicketEntity, PermissionTicket> permissionTicketStore;
|
||||
|
||||
public MapPermissionTicketStore(KeycloakSession session, MapStorage<UUID, MapPermissionTicketEntity, PermissionTicket> permissionTicketStore, AuthorizationProvider provider) {
|
||||
this.authorizationProvider = provider;
|
||||
this.permissionTicketStore = permissionTicketStore;
|
||||
this.tx = permissionTicketStore.createTransaction(session);
|
||||
session.getTransactionManager().enlist(tx);
|
||||
}
|
||||
|
||||
private MapPermissionTicketEntity registerEntityForChanges(MapPermissionTicketEntity origEntity) {
|
||||
final MapPermissionTicketEntity res = tx.read(origEntity.getId(), id -> Serialization.from(origEntity));
|
||||
tx.updateIfChanged(origEntity.getId(), res, MapPermissionTicketEntity::isUpdated);
|
||||
return res;
|
||||
}
|
||||
|
||||
private PermissionTicket entityToAdapter(MapPermissionTicketEntity origEntity) {
|
||||
if (origEntity == null) return null;
|
||||
// Clone entity before returning back, to avoid giving away a reference to the live object to the caller
|
||||
return new MapPermissionTicketAdapter(registerEntityForChanges(origEntity), authorizationProvider.getStoreFactory());
|
||||
}
|
||||
|
||||
private ModelCriteriaBuilder<PermissionTicket> forResourceServer(String resourceServerId) {
|
||||
ModelCriteriaBuilder<PermissionTicket> mcb = permissionTicketStore.createCriteriaBuilder();
|
||||
|
||||
return resourceServerId == null
|
||||
? mcb
|
||||
: mcb.compare(SearchableFields.RESOURCE_SERVER_ID, Operator.EQ,
|
||||
resourceServerId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long count(Map<PermissionTicket.FilterOption, String> attributes, String resourceServerId) {
|
||||
ModelCriteriaBuilder<PermissionTicket> mcb = forResourceServer(resourceServerId).and(
|
||||
attributes.entrySet().stream()
|
||||
.map(this::filterEntryToModelCriteriaBuilder)
|
||||
.toArray(ModelCriteriaBuilder[]::new)
|
||||
);
|
||||
|
||||
return tx.getCount(mcb);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PermissionTicket create(String resourceId, String scopeId, String requester, ResourceServer resourceServer) {
|
||||
LOG.tracef("create(%s, %s, %s, %s)%s", resourceId, scopeId, requester, resourceServer, getShortStackTrace());
|
||||
|
||||
String owner = authorizationProvider.getStoreFactory().getResourceStore().findById(resourceId, resourceServer.getId()).getOwner();
|
||||
|
||||
// @UniqueConstraint(columnNames = {"OWNER", "REQUESTER", "RESOURCE_SERVER_ID", "RESOURCE_ID", "SCOPE_ID"})
|
||||
ModelCriteriaBuilder<PermissionTicket> mcb = forResourceServer(resourceServer.getId())
|
||||
.compare(SearchableFields.OWNER, Operator.EQ, owner)
|
||||
.compare(SearchableFields.RESOURCE_ID, Operator.EQ, resourceId)
|
||||
.compare(SearchableFields.REQUESTER, Operator.EQ, requester);
|
||||
|
||||
if (scopeId != null) {
|
||||
mcb = mcb.compare(SearchableFields.SCOPE_ID, Operator.EQ, scopeId);
|
||||
}
|
||||
|
||||
if (tx.getCount(mcb) > 0) {
|
||||
throw new ModelDuplicateException("Permission ticket for resource server: '" + resourceServer.getId()
|
||||
+ ", Resource: " + resourceId + ", owner: " + owner + ", scopeId: " + scopeId + " already exists.");
|
||||
}
|
||||
|
||||
MapPermissionTicketEntity entity = new MapPermissionTicketEntity(UUID.randomUUID());
|
||||
entity.setResourceId(UUID.fromString(resourceId));
|
||||
entity.setRequester(requester);
|
||||
entity.setCreatedTimestamp(System.currentTimeMillis());
|
||||
|
||||
if (scopeId != null) {
|
||||
entity.setScopeId(UUID.fromString(scopeId));
|
||||
}
|
||||
|
||||
entity.setOwner(owner);
|
||||
entity.setResourceServerId(resourceServer.getId());
|
||||
|
||||
tx.create(entity.getId(), entity);
|
||||
|
||||
return entityToAdapter(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(String id) {
|
||||
LOG.tracef("delete(%s)%s", id, getShortStackTrace());
|
||||
tx.delete(UUID.fromString(id));
|
||||
}
|
||||
|
||||
@Override
|
||||
public PermissionTicket findById(String id, String resourceServerId) {
|
||||
LOG.tracef("findById(%s, %s)%s", id, resourceServerId, getShortStackTrace());
|
||||
|
||||
return tx.getUpdatedNotRemoved(forResourceServer(resourceServerId)
|
||||
.compare(PermissionTicket.SearchableFields.ID, Operator.EQ, id))
|
||||
.findFirst()
|
||||
.map(this::entityToAdapter)
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PermissionTicket> findByResourceServer(String resourceServerId) {
|
||||
LOG.tracef("findByResourceServer(%s)%s", resourceServerId, getShortStackTrace());
|
||||
|
||||
return tx.getUpdatedNotRemoved(forResourceServer(resourceServerId))
|
||||
.map(this::entityToAdapter)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PermissionTicket> findByOwner(String owner, String resourceServerId) {
|
||||
LOG.tracef("findByOwner(%s, %s)%s", owner, resourceServerId, getShortStackTrace());
|
||||
|
||||
return tx.getUpdatedNotRemoved(forResourceServer(resourceServerId)
|
||||
.compare(SearchableFields.OWNER, Operator.EQ, owner))
|
||||
.map(this::entityToAdapter)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PermissionTicket> findByResource(String resourceId, String resourceServerId) {
|
||||
LOG.tracef("findByResource(%s, %s)%s", resourceId, resourceServerId, getShortStackTrace());
|
||||
|
||||
return tx.getUpdatedNotRemoved(forResourceServer(resourceServerId)
|
||||
.compare(SearchableFields.RESOURCE_ID, Operator.EQ, resourceId))
|
||||
.map(this::entityToAdapter)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PermissionTicket> findByScope(String scopeId, String resourceServerId) {
|
||||
LOG.tracef("findByScope(%s, %s)%s", scopeId, resourceServerId, getShortStackTrace());
|
||||
|
||||
return tx.getUpdatedNotRemoved(forResourceServer(resourceServerId)
|
||||
.compare(SearchableFields.SCOPE_ID, Operator.EQ, scopeId))
|
||||
.map(this::entityToAdapter)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PermissionTicket> find(Map<PermissionTicket.FilterOption, String> attributes, String resourceServerId, int firstResult, int maxResult) {
|
||||
ModelCriteriaBuilder<PermissionTicket> mcb = forResourceServer(resourceServerId);
|
||||
|
||||
if (attributes.containsKey(PermissionTicket.FilterOption.RESOURCE_NAME)) {
|
||||
String expectedResourceName = attributes.remove(PermissionTicket.FilterOption.RESOURCE_NAME);
|
||||
|
||||
Map<Resource.FilterOption, String[]> filterOptionStringMap = new EnumMap<>(Resource.FilterOption.class);
|
||||
|
||||
filterOptionStringMap.put(Resource.FilterOption.EXACT_NAME, new String[]{expectedResourceName});
|
||||
|
||||
List<Resource> r = authorizationProvider.getStoreFactory().getResourceStore().findByResourceServer(filterOptionStringMap, resourceServerId, -1, -1);
|
||||
if (r == null || r.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
mcb = mcb.compare(SearchableFields.RESOURCE_ID, Operator.IN, r.stream().map(Resource::getId).collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
mcb = mcb.and(
|
||||
attributes.entrySet().stream()
|
||||
.map(this::filterEntryToModelCriteriaBuilder)
|
||||
.toArray(ModelCriteriaBuilder[]::new)
|
||||
);
|
||||
|
||||
return paginatedStream(tx.getUpdatedNotRemoved(mcb)
|
||||
.sorted(MapPermissionTicketEntity.COMPARE_BY_ID), firstResult, maxResult)
|
||||
.map(this::entityToAdapter)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private ModelCriteriaBuilder<PermissionTicket> filterEntryToModelCriteriaBuilder(Map.Entry<PermissionTicket.FilterOption, String> entry) {
|
||||
PermissionTicket.FilterOption name = entry.getKey();
|
||||
String value = entry.getValue();
|
||||
|
||||
switch (name) {
|
||||
case ID:
|
||||
case SCOPE_ID:
|
||||
case RESOURCE_ID:
|
||||
case OWNER:
|
||||
case REQUESTER:
|
||||
case POLICY_ID:
|
||||
return permissionTicketStore.createCriteriaBuilder()
|
||||
.compare(name.getSearchableModelField(), Operator.EQ, value);
|
||||
case SCOPE_IS_NULL:
|
||||
case GRANTED:
|
||||
case REQUESTER_IS_NULL: {
|
||||
Operator op = Operator.NOT_EXISTS;
|
||||
if (Boolean.parseBoolean(value)) {
|
||||
op = Operator.EXISTS;
|
||||
}
|
||||
return permissionTicketStore.createCriteriaBuilder()
|
||||
.compare(name.getSearchableModelField(), op);
|
||||
}
|
||||
case POLICY_IS_NOT_NULL:
|
||||
return permissionTicketStore.createCriteriaBuilder()
|
||||
.compare(SearchableFields.REQUESTER, Operator.NOT_EXISTS);
|
||||
default:
|
||||
throw new IllegalArgumentException("Unsupported filter [" + name + "]");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PermissionTicket> findGranted(String userId, String resourceServerId) {
|
||||
Map<PermissionTicket.FilterOption, String> filters = new EnumMap<>(PermissionTicket.FilterOption.class);
|
||||
|
||||
filters.put(PermissionTicket.FilterOption.GRANTED, Boolean.TRUE.toString());
|
||||
filters.put(PermissionTicket.FilterOption.REQUESTER, userId);
|
||||
|
||||
return find(filters, resourceServerId, -1, -1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PermissionTicket> findGranted(String resourceName, String userId, String resourceServerId) {
|
||||
Map<PermissionTicket.FilterOption, String> filters = new EnumMap<>(PermissionTicket.FilterOption.class);
|
||||
|
||||
filters.put(PermissionTicket.FilterOption.RESOURCE_NAME, resourceName);
|
||||
filters.put(PermissionTicket.FilterOption.GRANTED, Boolean.TRUE.toString());
|
||||
filters.put(PermissionTicket.FilterOption.REQUESTER, userId);
|
||||
|
||||
return find(filters, resourceServerId, -1, -1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Resource> findGrantedResources(String requester, String name, int first, int max) {
|
||||
ModelCriteriaBuilder<PermissionTicket> mcb = permissionTicketStore.createCriteriaBuilder()
|
||||
.compare(SearchableFields.REQUESTER, Operator.EQ, requester)
|
||||
.compare(SearchableFields.GRANTED_TIMESTAMP, Operator.EXISTS);
|
||||
|
||||
Function<MapPermissionTicketEntity, Resource> ticketResourceMapper;
|
||||
|
||||
ResourceStore resourceStore = authorizationProvider.getStoreFactory().getResourceStore();
|
||||
if (name != null) {
|
||||
ticketResourceMapper = ticket -> {
|
||||
Map<Resource.FilterOption, String[]> filterOptionMap = new EnumMap<>(Resource.FilterOption.class);
|
||||
|
||||
filterOptionMap.put(Resource.FilterOption.ID, new String[] {ticket.getResourceId().toString()});
|
||||
filterOptionMap.put(Resource.FilterOption.NAME, new String[] {name});
|
||||
|
||||
List<Resource> resource = resourceStore.findByResourceServer(filterOptionMap, ticket.getResourceServerId(), -1, 1);
|
||||
|
||||
return resource.isEmpty() ? null : resource.get(0);
|
||||
};
|
||||
} else {
|
||||
ticketResourceMapper = ticket -> resourceStore
|
||||
.findById(ticket.getResourceId().toString(), ticket.getResourceServerId());
|
||||
}
|
||||
|
||||
return paginatedStream(tx.getUpdatedNotRemoved(mcb)
|
||||
.filter(distinctByKey(AbstractPermissionTicketEntity::getResourceId))
|
||||
.sorted(MapPermissionTicketEntity.COMPARE_BY_RESOURCE_ID)
|
||||
.map(ticketResourceMapper)
|
||||
.filter(Objects::nonNull), first, max)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Resource> findGrantedOwnerResources(String owner, int first, int max) {
|
||||
ModelCriteriaBuilder<PermissionTicket> mcb = permissionTicketStore.createCriteriaBuilder()
|
||||
.compare(SearchableFields.OWNER, Operator.EQ, owner);
|
||||
|
||||
return paginatedStream(tx.getUpdatedNotRemoved(mcb)
|
||||
.filter(distinctByKey(AbstractPermissionTicketEntity::getResourceId))
|
||||
.sorted(MapPermissionTicketEntity.COMPARE_BY_RESOURCE_ID), first, max)
|
||||
.map(ticket -> authorizationProvider.getStoreFactory().getResourceStore()
|
||||
.findById(ticket.getResourceId().toString(), ticket.getResourceServerId()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,264 @@
|
|||
/*
|
||||
* Copyright 2021 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.models.map.authorization;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.authorization.AuthorizationProvider;
|
||||
import org.keycloak.authorization.model.Policy;
|
||||
import org.keycloak.authorization.model.Policy.SearchableFields;
|
||||
import org.keycloak.authorization.model.ResourceServer;
|
||||
import org.keycloak.authorization.store.PolicyStore;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.ModelDuplicateException;
|
||||
import org.keycloak.models.map.authorization.adapter.MapPolicyAdapter;
|
||||
import org.keycloak.models.map.authorization.entity.AbstractPolicyEntity;
|
||||
import org.keycloak.models.map.authorization.entity.MapPolicyEntity;
|
||||
import org.keycloak.models.map.common.Serialization;
|
||||
import org.keycloak.models.map.storage.MapKeycloakTransaction;
|
||||
import org.keycloak.models.map.storage.MapStorage;
|
||||
import org.keycloak.models.map.storage.ModelCriteriaBuilder;
|
||||
import org.keycloak.models.map.storage.ModelCriteriaBuilder.Operator;
|
||||
import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentation;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.keycloak.common.util.StackUtil.getShortStackTrace;
|
||||
import static org.keycloak.utils.StreamsUtil.paginatedStream;
|
||||
|
||||
public class MapPolicyStore implements PolicyStore {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(MapPolicyStore.class);
|
||||
private final AuthorizationProvider authorizationProvider;
|
||||
final MapKeycloakTransaction<UUID, MapPolicyEntity, Policy> tx;
|
||||
private final MapStorage<UUID, MapPolicyEntity, Policy> policyStore;
|
||||
|
||||
public MapPolicyStore(KeycloakSession session, MapStorage<UUID, MapPolicyEntity, Policy> policyStore, AuthorizationProvider provider) {
|
||||
this.authorizationProvider = provider;
|
||||
this.policyStore = policyStore;
|
||||
this.tx = policyStore.createTransaction(session);
|
||||
session.getTransactionManager().enlist(tx);
|
||||
}
|
||||
|
||||
private MapPolicyEntity registerEntityForChanges(MapPolicyEntity origEntity) {
|
||||
final MapPolicyEntity res = tx.read(origEntity.getId(), id -> Serialization.from(origEntity));
|
||||
tx.updateIfChanged(origEntity.getId(), res, MapPolicyEntity::isUpdated);
|
||||
return res;
|
||||
}
|
||||
|
||||
private Policy entityToAdapter(MapPolicyEntity origEntity) {
|
||||
if (origEntity == null) return null;
|
||||
// Clone entity before returning back, to avoid giving away a reference to the live object to the caller
|
||||
return new MapPolicyAdapter(registerEntityForChanges(origEntity), authorizationProvider.getStoreFactory());
|
||||
}
|
||||
|
||||
private ModelCriteriaBuilder<Policy> forResourceServer(String resourceServerId) {
|
||||
ModelCriteriaBuilder<Policy> mcb = policyStore.createCriteriaBuilder();
|
||||
|
||||
return resourceServerId == null
|
||||
? mcb
|
||||
: mcb.compare(SearchableFields.RESOURCE_SERVER_ID, Operator.EQ,
|
||||
resourceServerId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Policy create(AbstractPolicyRepresentation representation, ResourceServer resourceServer) {
|
||||
LOG.tracef("create(%s, %s, %s)%s", representation.getId(), resourceServer.getId(), resourceServer, getShortStackTrace());
|
||||
|
||||
// @UniqueConstraint(columnNames = {"NAME", "RESOURCE_SERVER_ID"})
|
||||
ModelCriteriaBuilder<Policy> mcb = forResourceServer(resourceServer.getId())
|
||||
.compare(SearchableFields.NAME, Operator.EQ, representation.getName());
|
||||
|
||||
if (tx.getCount(mcb) > 0) {
|
||||
throw new ModelDuplicateException("Policy with name '" + representation.getName() + "' for " + resourceServer.getId() + " already exists");
|
||||
}
|
||||
|
||||
UUID uid = representation.getId() == null ? UUID.randomUUID() : UUID.fromString(representation.getId());
|
||||
MapPolicyEntity entity = new MapPolicyEntity(uid);
|
||||
entity.setType(representation.getType());
|
||||
entity.setName(representation.getName());
|
||||
entity.setResourceServerId(resourceServer.getId());
|
||||
|
||||
tx.create(uid, entity);
|
||||
|
||||
return entityToAdapter(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(String id) {
|
||||
LOG.tracef("delete(%s)%s", id, getShortStackTrace());
|
||||
tx.delete(UUID.fromString(id));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Policy findById(String id, String resourceServerId) {
|
||||
LOG.tracef("findById(%s, %s)%s", id, resourceServerId, getShortStackTrace());
|
||||
|
||||
return tx.getUpdatedNotRemoved(forResourceServer(resourceServerId)
|
||||
.compare(SearchableFields.ID, Operator.EQ, id))
|
||||
.findFirst()
|
||||
.map(this::entityToAdapter)
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Policy findByName(String name, String resourceServerId) {
|
||||
LOG.tracef("findByName(%s, %s)%s", name, resourceServerId, getShortStackTrace());
|
||||
|
||||
return tx.getUpdatedNotRemoved(forResourceServer(resourceServerId)
|
||||
.compare(SearchableFields.NAME, Operator.EQ, name))
|
||||
.findFirst()
|
||||
.map(this::entityToAdapter)
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Policy> findByResourceServer(String id) {
|
||||
LOG.tracef("findByResourceServer(%s)%s", id, getShortStackTrace());
|
||||
|
||||
return tx.getUpdatedNotRemoved(forResourceServer(id))
|
||||
.map(this::entityToAdapter)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Policy> findByResourceServer(Map<Policy.FilterOption, String[]> attributes, String resourceServerId, int firstResult, int maxResult) {
|
||||
LOG.tracef("findByResource(%s, %s, %d, %d)%s", attributes, resourceServerId, firstResult, maxResult, getShortStackTrace());
|
||||
|
||||
ModelCriteriaBuilder<Policy> mcb = forResourceServer(resourceServerId).and(
|
||||
attributes.entrySet().stream()
|
||||
.map(this::filterEntryToModelCriteriaBuilder)
|
||||
.toArray(ModelCriteriaBuilder[]::new)
|
||||
);
|
||||
|
||||
if (!attributes.containsKey(Policy.FilterOption.OWNER) && !attributes.containsKey(Policy.FilterOption.OWNER_IS_NOT_NULL)) {
|
||||
mcb = mcb.compare(SearchableFields.OWNER, Operator.NOT_EXISTS);
|
||||
}
|
||||
|
||||
return paginatedStream(tx.getUpdatedNotRemoved(mcb)
|
||||
.sorted(AbstractPolicyEntity.COMPARE_BY_NAME), firstResult, maxResult)
|
||||
.map(MapPolicyEntity::getId)
|
||||
.map(UUID::toString)
|
||||
.map(id -> authorizationProvider.getStoreFactory().getPolicyStore().findById(id, resourceServerId)) // We need to go through cache
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private ModelCriteriaBuilder<Policy> filterEntryToModelCriteriaBuilder(Map.Entry<Policy.FilterOption, String[]> entry) {
|
||||
Policy.FilterOption name = entry.getKey();
|
||||
String[] value = entry.getValue();
|
||||
|
||||
switch (name) {
|
||||
case ID:
|
||||
case SCOPE_ID:
|
||||
case RESOURCE_ID:
|
||||
case OWNER:
|
||||
return policyStore.createCriteriaBuilder()
|
||||
.compare(name.getSearchableModelField(), Operator.IN, Arrays.asList(value));
|
||||
case PERMISSION: {
|
||||
ModelCriteriaBuilder<Policy> mcb = policyStore.createCriteriaBuilder()
|
||||
.compare(SearchableFields.TYPE, Operator.IN, Arrays.asList("resource", "scope", "uma"));
|
||||
|
||||
if (!Boolean.parseBoolean(value[0])) {
|
||||
mcb = policyStore.createCriteriaBuilder().not(mcb); // TODO: create NOT_IN operator
|
||||
}
|
||||
|
||||
return mcb;
|
||||
}
|
||||
case OWNER_IS_NOT_NULL:
|
||||
return policyStore.createCriteriaBuilder()
|
||||
.compare(SearchableFields.OWNER, Operator.EXISTS);
|
||||
case CONFIG:
|
||||
if (value.length != 2) {
|
||||
throw new IllegalArgumentException("Config filter option requires value with two items: [config_name, expected_config_value]");
|
||||
}
|
||||
|
||||
value[1] = "%" + value[1] + "%";
|
||||
return policyStore.createCriteriaBuilder()
|
||||
.compare(SearchableFields.CONFIG, Operator.LIKE, (Object[]) value);
|
||||
case TYPE:
|
||||
case NAME:
|
||||
return policyStore.createCriteriaBuilder().compare(name.getSearchableModelField(), Operator.ILIKE, "%" + value[0] + "%");
|
||||
default:
|
||||
throw new IllegalArgumentException("Unsupported filter [" + name + "]");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void findByResource(String resourceId, String resourceServerId, Consumer<Policy> consumer) {
|
||||
LOG.tracef("findByResource(%s, %s, %s)%s", resourceId, resourceServerId, consumer, getShortStackTrace());
|
||||
|
||||
tx.getUpdatedNotRemoved(forResourceServer(resourceServerId)
|
||||
.compare(Policy.SearchableFields.RESOURCE_ID, Operator.EQ, resourceId))
|
||||
.map(this::entityToAdapter)
|
||||
.forEach(consumer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void findByResourceType(String type, String resourceServerId, Consumer<Policy> policyConsumer) {
|
||||
tx.getUpdatedNotRemoved(forResourceServer(resourceServerId)
|
||||
.compare(SearchableFields.CONFIG, Operator.LIKE, (Object[]) new String[] {"defaultResourceType", type}))
|
||||
.map(this::entityToAdapter)
|
||||
.forEach(policyConsumer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Policy> findByScopeIds(List<String> scopeIds, String resourceServerId) {
|
||||
return tx.getUpdatedNotRemoved(forResourceServer(resourceServerId)
|
||||
.compare(SearchableFields.SCOPE_ID, Operator.IN, scopeIds))
|
||||
.map(this::entityToAdapter)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void findByScopeIds(List<String> scopeIds, String resourceId, String resourceServerId, Consumer<Policy> consumer) {
|
||||
ModelCriteriaBuilder<Policy> mcb = forResourceServer(resourceServerId)
|
||||
.compare(SearchableFields.TYPE, Operator.EQ, "scope")
|
||||
.compare(SearchableFields.SCOPE_ID, Operator.IN, scopeIds);
|
||||
|
||||
if (resourceId != null) {
|
||||
mcb = mcb.compare(SearchableFields.RESOURCE_ID, Operator.EQ, resourceId);
|
||||
// @NamedQuery(name="findPolicyIdByNullResourceScope", query="PolicyEntity pe left join fetch pe.config c inner join pe.scopes s where pe.resourceServer.id = :serverId and pe.type = 'scope' and pe.resources is empty and s.id in (:scopeIds) and not exists (select pec from pe.config pec where KEY(pec) = 'defaultResourceType')"),
|
||||
} else {
|
||||
mcb = mcb.compare(SearchableFields.RESOURCE_ID, Operator.NOT_EXISTS)
|
||||
.compare(SearchableFields.CONFIG, Operator.NOT_EXISTS, (Object[]) new String[] {"defaultResourceType"});
|
||||
}
|
||||
|
||||
tx.getUpdatedNotRemoved(mcb).map(this::entityToAdapter).forEach(consumer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Policy> findByType(String type, String resourceServerId) {
|
||||
return tx.getUpdatedNotRemoved(forResourceServer(resourceServerId)
|
||||
.compare(SearchableFields.TYPE, Operator.EQ, type))
|
||||
.map(this::entityToAdapter)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Policy> findDependentPolicies(String id, String resourceServerId) {
|
||||
return tx.getUpdatedNotRemoved(forResourceServer(resourceServerId)
|
||||
.compare(SearchableFields.ASSOCIATED_POLICY_ID, Operator.EQ, id))
|
||||
.map(this::entityToAdapter)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,131 @@
|
|||
/*
|
||||
* Copyright 2021 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.models.map.authorization;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.authorization.AuthorizationProvider;
|
||||
import org.keycloak.authorization.model.PermissionTicket;
|
||||
import org.keycloak.authorization.model.Policy;
|
||||
import org.keycloak.authorization.model.Resource;
|
||||
import org.keycloak.authorization.model.ResourceServer;
|
||||
import org.keycloak.authorization.model.Scope;
|
||||
import org.keycloak.authorization.store.PermissionTicketStore;
|
||||
import org.keycloak.authorization.store.PolicyStore;
|
||||
import org.keycloak.authorization.store.ResourceServerStore;
|
||||
import org.keycloak.authorization.store.ResourceStore;
|
||||
import org.keycloak.authorization.store.ScopeStore;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.ModelDuplicateException;
|
||||
import org.keycloak.models.ModelException;
|
||||
import org.keycloak.models.map.authorization.adapter.MapResourceServerAdapter;
|
||||
import org.keycloak.models.map.authorization.entity.MapResourceServerEntity;
|
||||
import org.keycloak.models.map.common.Serialization;
|
||||
import org.keycloak.models.map.storage.MapKeycloakTransaction;
|
||||
import org.keycloak.models.map.storage.MapStorage;
|
||||
import org.keycloak.storage.StorageId;
|
||||
|
||||
import static org.keycloak.common.util.StackUtil.getShortStackTrace;
|
||||
|
||||
public class MapResourceServerStore implements ResourceServerStore {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(MapResourceServerStore.class);
|
||||
private final AuthorizationProvider authorizationProvider;
|
||||
final MapKeycloakTransaction<String, MapResourceServerEntity, ResourceServer> tx;
|
||||
private final MapStorage<String, MapResourceServerEntity, ResourceServer> resourceServerStore;
|
||||
|
||||
public MapResourceServerStore(KeycloakSession session, MapStorage<String, MapResourceServerEntity, ResourceServer> resourceServerStore, AuthorizationProvider provider) {
|
||||
this.resourceServerStore = resourceServerStore;
|
||||
this.tx = resourceServerStore.createTransaction(session);
|
||||
this.authorizationProvider = provider;
|
||||
session.getTransactionManager().enlist(tx);
|
||||
}
|
||||
|
||||
private MapResourceServerEntity registerEntityForChanges(MapResourceServerEntity origEntity) {
|
||||
final MapResourceServerEntity res = tx.read(origEntity.getId(), id -> Serialization.from(origEntity));
|
||||
tx.updateIfChanged(origEntity.getId(), res, MapResourceServerEntity::isUpdated);
|
||||
return res;
|
||||
}
|
||||
|
||||
private ResourceServer entityToAdapter(MapResourceServerEntity origEntity) {
|
||||
if (origEntity == null) return null;
|
||||
// Clone entity before returning back, to avoid giving away a reference to the live object to the caller
|
||||
return new MapResourceServerAdapter(registerEntityForChanges(origEntity), authorizationProvider.getStoreFactory());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceServer create(String clientId) {
|
||||
LOG.tracef("create(%s)%s", clientId, getShortStackTrace());
|
||||
|
||||
if (clientId == null) return null;
|
||||
|
||||
if (!StorageId.isLocalStorage(clientId)) {
|
||||
throw new ModelException("Creating resource server from federated ClientModel not supported");
|
||||
}
|
||||
|
||||
if (tx.read(clientId) != null) {
|
||||
throw new ModelDuplicateException("Resource server already exists: " + clientId);
|
||||
}
|
||||
|
||||
MapResourceServerEntity entity = new MapResourceServerEntity(clientId);
|
||||
|
||||
tx.create(entity.getId(), entity);
|
||||
|
||||
return entityToAdapter(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(String id) {
|
||||
LOG.tracef("delete(%s, %s)%s", id, getShortStackTrace());
|
||||
if (id == null) return;
|
||||
|
||||
PolicyStore policyStore = authorizationProvider.getStoreFactory().getPolicyStore();
|
||||
policyStore.findByResourceServer(id).stream()
|
||||
.map(Policy::getId)
|
||||
.forEach(policyStore::delete);
|
||||
|
||||
PermissionTicketStore permissionTicketStore = authorizationProvider.getStoreFactory().getPermissionTicketStore();
|
||||
permissionTicketStore.findByResourceServer(id).stream()
|
||||
.map(PermissionTicket::getId)
|
||||
.forEach(permissionTicketStore::delete);
|
||||
|
||||
ResourceStore resourceStore = authorizationProvider.getStoreFactory().getResourceStore();
|
||||
resourceStore.findByResourceServer(id).stream()
|
||||
.map(Resource::getId)
|
||||
.forEach(resourceStore::delete);
|
||||
|
||||
ScopeStore scopeStore = authorizationProvider.getStoreFactory().getScopeStore();
|
||||
scopeStore.findByResourceServer(id).stream()
|
||||
.map(Scope::getId)
|
||||
.forEach(scopeStore::delete);
|
||||
|
||||
tx.delete(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceServer findById(String id) {
|
||||
LOG.tracef("findById(%s)%s", id, getShortStackTrace());
|
||||
|
||||
if (id == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
MapResourceServerEntity entity = tx.read(id);
|
||||
return entityToAdapter(entity);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,270 @@
|
|||
/*
|
||||
* Copyright 2021 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.models.map.authorization;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.authorization.AuthorizationProvider;
|
||||
import org.keycloak.authorization.model.Resource;
|
||||
import org.keycloak.authorization.model.Resource.SearchableFields;
|
||||
import org.keycloak.authorization.model.ResourceServer;
|
||||
import org.keycloak.authorization.store.ResourceStore;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.ModelDuplicateException;
|
||||
import org.keycloak.models.map.authorization.adapter.MapResourceAdapter;
|
||||
import org.keycloak.models.map.authorization.entity.AbstractResourceEntity;
|
||||
import org.keycloak.models.map.authorization.entity.MapResourceEntity;
|
||||
import org.keycloak.models.map.common.Serialization;
|
||||
import org.keycloak.models.map.storage.MapKeycloakTransaction;
|
||||
import org.keycloak.models.map.storage.MapStorage;
|
||||
import org.keycloak.models.map.storage.ModelCriteriaBuilder;
|
||||
import org.keycloak.models.map.storage.ModelCriteriaBuilder.Operator;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.keycloak.common.util.StackUtil.getShortStackTrace;
|
||||
import static org.keycloak.utils.StreamsUtil.paginatedStream;
|
||||
|
||||
public class MapResourceStore implements ResourceStore {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(MapResourceStore.class);
|
||||
private final AuthorizationProvider authorizationProvider;
|
||||
final MapKeycloakTransaction<UUID, MapResourceEntity, Resource> tx;
|
||||
private final MapStorage<UUID, MapResourceEntity, Resource> resourceStore;
|
||||
|
||||
public MapResourceStore(KeycloakSession session, MapStorage<UUID, MapResourceEntity, Resource> resourceStore, AuthorizationProvider provider) {
|
||||
this.resourceStore = resourceStore;
|
||||
this.tx = resourceStore.createTransaction(session);
|
||||
session.getTransactionManager().enlist(tx);
|
||||
authorizationProvider = provider;
|
||||
}
|
||||
|
||||
private MapResourceEntity registerEntityForChanges(MapResourceEntity origEntity) {
|
||||
final MapResourceEntity res = tx.read(origEntity.getId(), id -> Serialization.from(origEntity));
|
||||
tx.updateIfChanged(origEntity.getId(), res, MapResourceEntity::isUpdated);
|
||||
return res;
|
||||
}
|
||||
|
||||
private Resource entityToAdapter(MapResourceEntity origEntity) {
|
||||
if (origEntity == null) return null;
|
||||
// Clone entity before returning back, to avoid giving away a reference to the live object to the caller
|
||||
return new MapResourceAdapter(registerEntityForChanges(origEntity), authorizationProvider.getStoreFactory());
|
||||
}
|
||||
|
||||
private ModelCriteriaBuilder<Resource> forResourceServer(String resourceServerId) {
|
||||
ModelCriteriaBuilder<Resource> mcb = resourceStore.createCriteriaBuilder();
|
||||
|
||||
return resourceServerId == null
|
||||
? mcb
|
||||
: mcb.compare(SearchableFields.RESOURCE_SERVER_ID, Operator.EQ,
|
||||
resourceServerId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Resource create(String id, String name, ResourceServer resourceServer, String owner) {
|
||||
LOG.tracef("create(%s, %s, %s, %s)%s", id, name, resourceServer, owner, getShortStackTrace());
|
||||
// @UniqueConstraint(columnNames = {"NAME", "RESOURCE_SERVER_ID", "OWNER"})
|
||||
ModelCriteriaBuilder<Resource> mcb = forResourceServer(resourceServer.getId())
|
||||
.compare(SearchableFields.NAME, Operator.EQ, name)
|
||||
.compare(SearchableFields.OWNER, Operator.EQ, owner);
|
||||
|
||||
if (tx.getCount(mcb) > 0) {
|
||||
throw new ModelDuplicateException("Resource with name '" + name + "' for " + resourceServer.getId() + " already exists for request owner " + owner);
|
||||
}
|
||||
|
||||
UUID uid = id == null ? UUID.randomUUID() : UUID.fromString(id);
|
||||
MapResourceEntity entity = new MapResourceEntity(uid);
|
||||
|
||||
entity.setName(name);
|
||||
entity.setResourceServerId(resourceServer.getId());
|
||||
entity.setOwner(owner);
|
||||
|
||||
tx.create(uid, entity);
|
||||
|
||||
return entityToAdapter(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(String id) {
|
||||
LOG.tracef("delete(%s)%s", id, getShortStackTrace());
|
||||
|
||||
tx.delete(UUID.fromString(id));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Resource findById(String id, String resourceServerId) {
|
||||
LOG.tracef("findById(%s, %s)%s", id, resourceServerId, getShortStackTrace());
|
||||
|
||||
return tx.getUpdatedNotRemoved(forResourceServer(resourceServerId)
|
||||
.compare(SearchableFields.ID, Operator.EQ, id))
|
||||
.findFirst()
|
||||
.map(this::entityToAdapter)
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void findByOwner(String ownerId, String resourceServerId, Consumer<Resource> consumer) {
|
||||
findByOwnerFilter(ownerId, resourceServerId, consumer, -1, -1);
|
||||
}
|
||||
|
||||
private void findByOwnerFilter(String ownerId, String resourceServerId, Consumer<Resource> consumer, int firstResult, int maxResult) {
|
||||
LOG.tracef("findByOwnerFilter(%s, %s, %s, %d, %d)%s", ownerId, resourceServerId, consumer, firstResult, maxResult, getShortStackTrace());
|
||||
paginatedStream(tx.getUpdatedNotRemoved(forResourceServer(resourceServerId)
|
||||
.compare(SearchableFields.OWNER, Operator.EQ, ownerId))
|
||||
.sorted(MapResourceEntity.COMPARE_BY_ID), firstResult, maxResult)
|
||||
.map(this::entityToAdapter)
|
||||
.forEach(consumer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Resource> findByOwner(String ownerId, String resourceServerId, int first, int max) {
|
||||
List<Resource> resourceList = new LinkedList<>();
|
||||
|
||||
findByOwnerFilter(ownerId, resourceServerId, resourceList::add, first, max);
|
||||
|
||||
return resourceList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Resource> findByUri(String uri, String resourceServerId) {
|
||||
LOG.tracef("findByUri(%s, %s)%s", uri, resourceServerId, getShortStackTrace());
|
||||
|
||||
return tx.getUpdatedNotRemoved(forResourceServer(resourceServerId)
|
||||
.compare(SearchableFields.URI, Operator.EQ, uri))
|
||||
.map(this::entityToAdapter)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Resource> findByResourceServer(String resourceServerId) {
|
||||
LOG.tracef("findByResourceServer(%s)%s", resourceServerId, getShortStackTrace());
|
||||
|
||||
return tx.getUpdatedNotRemoved(forResourceServer(resourceServerId))
|
||||
.map(this::entityToAdapter)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Resource> findByResourceServer(Map<Resource.FilterOption, String[]> attributes, String resourceServerId, int firstResult, int maxResult) {
|
||||
LOG.tracef("findByResourceServer(%s, %s, %d, %d)%s", attributes, resourceServerId, firstResult, maxResult, getShortStackTrace());
|
||||
ModelCriteriaBuilder<Resource> mcb = forResourceServer(resourceServerId).and(
|
||||
attributes.entrySet().stream()
|
||||
.map(this::filterEntryToModelCriteriaBuilder)
|
||||
.toArray(ModelCriteriaBuilder[]::new)
|
||||
);
|
||||
|
||||
return paginatedStream(tx.getUpdatedNotRemoved(mcb)
|
||||
.sorted(AbstractResourceEntity.COMPARE_BY_NAME), firstResult, maxResult)
|
||||
.map(this::entityToAdapter)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private ModelCriteriaBuilder<Resource> filterEntryToModelCriteriaBuilder(Map.Entry<Resource.FilterOption, String[]> entry) {
|
||||
Resource.FilterOption name = entry.getKey();
|
||||
String[] value = entry.getValue();
|
||||
|
||||
switch (name) {
|
||||
case ID:
|
||||
case SCOPE_ID:
|
||||
case OWNER:
|
||||
case URI:
|
||||
return resourceStore.createCriteriaBuilder()
|
||||
.compare(name.getSearchableModelField(), Operator.IN, Arrays.asList(value));
|
||||
case URI_NOT_NULL:
|
||||
return resourceStore.createCriteriaBuilder().compare(SearchableFields.URI, Operator.EXISTS);
|
||||
case OWNER_MANAGED_ACCESS:
|
||||
return resourceStore.createCriteriaBuilder()
|
||||
.compare(SearchableFields.OWNER_MANAGED_ACCESS, Operator.EQ, Boolean.valueOf(value[0]));
|
||||
case EXACT_NAME:
|
||||
return resourceStore.createCriteriaBuilder()
|
||||
.compare(SearchableFields.NAME, Operator.EQ, value[0]);
|
||||
case NAME:
|
||||
return resourceStore.createCriteriaBuilder()
|
||||
.compare(SearchableFields.NAME, Operator.ILIKE, "%" + value[0] + "%");
|
||||
default:
|
||||
throw new IllegalArgumentException("Unsupported filter [" + name + "]");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void findByScope(List<String> scopes, String resourceServerId, Consumer<Resource> consumer) {
|
||||
LOG.tracef("findByScope(%s, %s, %s)%s", scopes, resourceServerId, consumer, getShortStackTrace());
|
||||
|
||||
tx.getUpdatedNotRemoved(forResourceServer(resourceServerId)
|
||||
.compare(SearchableFields.SCOPE_ID, Operator.IN, scopes))
|
||||
.map(this::entityToAdapter)
|
||||
.forEach(consumer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Resource findByName(String name, String resourceServerId) {
|
||||
return findByName(name, resourceServerId, resourceServerId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Resource findByName(String name, String ownerId, String resourceServerId) {
|
||||
LOG.tracef("findByName(%s, %s, %s)%s", name, ownerId, resourceServerId, getShortStackTrace());
|
||||
return tx.getUpdatedNotRemoved(forResourceServer(resourceServerId)
|
||||
.compare(SearchableFields.OWNER, Operator.EQ, ownerId)
|
||||
.compare(SearchableFields.NAME, Operator.EQ, name))
|
||||
.findFirst()
|
||||
.map(this::entityToAdapter)
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void findByType(String type, String resourceServerId, Consumer<Resource> consumer) {
|
||||
LOG.tracef("findByType(%s, %s, %s)%s", type, resourceServerId, consumer, getShortStackTrace());
|
||||
tx.getUpdatedNotRemoved(forResourceServer(resourceServerId)
|
||||
.compare(SearchableFields.TYPE, Operator.EQ, type))
|
||||
.map(this::entityToAdapter)
|
||||
.forEach(consumer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void findByType(String type, String owner, String resourceServerId, Consumer<Resource> consumer) {
|
||||
LOG.tracef("findByType(%s, %s, %s, %s)%s", type, owner, resourceServerId, consumer, getShortStackTrace());
|
||||
|
||||
ModelCriteriaBuilder<Resource> mcb = forResourceServer(resourceServerId)
|
||||
.compare(SearchableFields.TYPE, Operator.EQ, type);
|
||||
|
||||
if (owner != null) {
|
||||
mcb = mcb.compare(SearchableFields.OWNER, Operator.EQ, owner);
|
||||
}
|
||||
|
||||
tx.getUpdatedNotRemoved(mcb)
|
||||
.map(this::entityToAdapter)
|
||||
.forEach(consumer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void findByTypeInstance(String type, String resourceServerId, Consumer<Resource> consumer) {
|
||||
LOG.tracef("findByTypeInstance(%s, %s, %s)%s", type, resourceServerId, consumer, getShortStackTrace());
|
||||
tx.getUpdatedNotRemoved(forResourceServer(resourceServerId)
|
||||
.compare(SearchableFields.OWNER, Operator.NE, resourceServerId)
|
||||
.compare(SearchableFields.TYPE, Operator.EQ, type))
|
||||
.map(this::entityToAdapter)
|
||||
.forEach(consumer);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,163 @@
|
|||
/*
|
||||
* Copyright 2021 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.models.map.authorization;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.authorization.AuthorizationProvider;
|
||||
import org.keycloak.authorization.model.ResourceServer;
|
||||
import org.keycloak.authorization.model.Scope;
|
||||
import org.keycloak.authorization.model.Scope.SearchableFields;
|
||||
import org.keycloak.authorization.store.ScopeStore;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.ModelDuplicateException;
|
||||
import org.keycloak.models.map.authorization.adapter.MapScopeAdapter;
|
||||
import org.keycloak.models.map.authorization.entity.MapScopeEntity;
|
||||
import org.keycloak.models.map.common.Serialization;
|
||||
import org.keycloak.models.map.storage.MapKeycloakTransaction;
|
||||
import org.keycloak.models.map.storage.MapStorage;
|
||||
import org.keycloak.models.map.storage.ModelCriteriaBuilder;
|
||||
import org.keycloak.models.map.storage.ModelCriteriaBuilder.Operator;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.keycloak.common.util.StackUtil.getShortStackTrace;
|
||||
import static org.keycloak.utils.StreamsUtil.paginatedStream;
|
||||
|
||||
public class MapScopeStore implements ScopeStore {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(MapScopeStore.class);
|
||||
private final AuthorizationProvider authorizationProvider;
|
||||
final MapKeycloakTransaction<UUID, MapScopeEntity, Scope> tx;
|
||||
private final MapStorage<UUID, MapScopeEntity, Scope> scopeStore;
|
||||
|
||||
public MapScopeStore(KeycloakSession session, MapStorage<UUID, MapScopeEntity, Scope> scopeStore, AuthorizationProvider provider) {
|
||||
this.authorizationProvider = provider;
|
||||
this.scopeStore = scopeStore;
|
||||
this.tx = scopeStore.createTransaction(session);
|
||||
session.getTransactionManager().enlist(tx);
|
||||
}
|
||||
|
||||
private MapScopeEntity registerEntityForChanges(MapScopeEntity origEntity) {
|
||||
final MapScopeEntity res = tx.read(origEntity.getId(), id -> Serialization.from(origEntity));
|
||||
tx.updateIfChanged(origEntity.getId(), res, MapScopeEntity::isUpdated);
|
||||
return res;
|
||||
}
|
||||
|
||||
private Scope entityToAdapter(MapScopeEntity origEntity) {
|
||||
if (origEntity == null) return null;
|
||||
// Clone entity before returning back, to avoid giving away a reference to the live object to the caller
|
||||
return new MapScopeAdapter(registerEntityForChanges(origEntity), authorizationProvider.getStoreFactory());
|
||||
}
|
||||
|
||||
private ModelCriteriaBuilder<Scope> forResourceServer(String resourceServerId) {
|
||||
ModelCriteriaBuilder<Scope> mcb = scopeStore.createCriteriaBuilder();
|
||||
|
||||
return resourceServerId == null
|
||||
? mcb
|
||||
: mcb.compare(SearchableFields.RESOURCE_SERVER_ID, Operator.EQ,
|
||||
resourceServerId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Scope create(String id, String name, ResourceServer resourceServer) {
|
||||
LOG.tracef("create(%s, %s, %s)%s", id, name, resourceServer, getShortStackTrace());
|
||||
|
||||
|
||||
// @UniqueConstraint(columnNames = {"NAME", "RESOURCE_SERVER_ID"})
|
||||
ModelCriteriaBuilder<Scope> mcb = forResourceServer(resourceServer.getId())
|
||||
.compare(SearchableFields.NAME, Operator.EQ, name);
|
||||
|
||||
if (tx.getCount(mcb) > 0) {
|
||||
throw new ModelDuplicateException("Scope with name '" + name + "' for " + resourceServer.getId() + " already exists");
|
||||
}
|
||||
|
||||
UUID uid = id == null ? UUID.randomUUID() : UUID.fromString(id);
|
||||
MapScopeEntity entity = new MapScopeEntity(uid);
|
||||
|
||||
entity.setName(name);
|
||||
entity.setResourceServerId(resourceServer.getId());
|
||||
|
||||
tx.create(uid, entity);
|
||||
|
||||
return entityToAdapter(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(String id) {
|
||||
LOG.tracef("delete(%s)%s", id, getShortStackTrace());
|
||||
tx.delete(UUID.fromString(id));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Scope findById(String id, String resourceServerId) {
|
||||
LOG.tracef("findById(%s, %s)%s", id, resourceServerId, getShortStackTrace());
|
||||
|
||||
return tx.getUpdatedNotRemoved(forResourceServer(resourceServerId)
|
||||
.compare(Scope.SearchableFields.ID, Operator.EQ, id))
|
||||
.findFirst()
|
||||
.map(this::entityToAdapter)
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Scope findByName(String name, String resourceServerId) {
|
||||
LOG.tracef("findByName(%s, %s)%s", name, resourceServerId, getShortStackTrace());
|
||||
|
||||
return tx.getUpdatedNotRemoved(forResourceServer(resourceServerId).compare(Scope.SearchableFields.NAME,
|
||||
Operator.EQ, name))
|
||||
.findFirst()
|
||||
.map(this::entityToAdapter)
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Scope> findByResourceServer(String id) {
|
||||
LOG.tracef("findByResourceServer(%s)%s", id, getShortStackTrace());
|
||||
|
||||
return tx.getUpdatedNotRemoved(forResourceServer(id))
|
||||
.map(this::entityToAdapter)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Scope> findByResourceServer(Map<Scope.FilterOption, String[]> attributes, String resourceServerId, int firstResult, int maxResult) {
|
||||
ModelCriteriaBuilder<Scope> mcb = forResourceServer(resourceServerId);
|
||||
|
||||
for (Scope.FilterOption filterOption : attributes.keySet()) {
|
||||
String[] value = attributes.get(filterOption);
|
||||
|
||||
switch (filterOption) {
|
||||
case ID:
|
||||
mcb = mcb.compare(Scope.SearchableFields.ID, Operator.IN, Arrays.asList(value));
|
||||
break;
|
||||
case NAME:
|
||||
mcb = mcb.compare(Scope.SearchableFields.NAME, Operator.ILIKE, "%" + value[0] + "%");
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unsupported filter [" + filterOption + "]");
|
||||
}
|
||||
}
|
||||
|
||||
return paginatedStream(tx.getUpdatedNotRemoved(mcb).map(this::entityToAdapter), firstResult, maxResult)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright 2021 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.models.map.authorization.adapter;
|
||||
|
||||
import org.keycloak.authorization.model.PermissionTicket;
|
||||
import org.keycloak.authorization.store.StoreFactory;
|
||||
import org.keycloak.models.map.common.AbstractEntity;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public abstract class AbstractPermissionTicketModel<E extends AbstractEntity> implements PermissionTicket {
|
||||
|
||||
protected final E entity;
|
||||
protected final StoreFactory storeFactory;
|
||||
|
||||
public AbstractPermissionTicketModel(E entity, StoreFactory storeFactory) {
|
||||
Objects.requireNonNull(entity, "entity");
|
||||
Objects.requireNonNull(storeFactory, "storeFactory");
|
||||
|
||||
this.storeFactory = storeFactory;
|
||||
this.entity = entity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof PermissionTicket)) return false;
|
||||
|
||||
PermissionTicket that = (PermissionTicket) o;
|
||||
return Objects.equals(that.getId(), getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return getId().hashCode();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright 2021 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.models.map.authorization.adapter;
|
||||
|
||||
import org.keycloak.authorization.model.AbstractAuthorizationModel;
|
||||
import org.keycloak.authorization.model.Policy;
|
||||
import org.keycloak.authorization.store.StoreFactory;
|
||||
import org.keycloak.models.map.common.AbstractEntity;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public abstract class AbstractPolicyModel<E extends AbstractEntity<?>> extends AbstractAuthorizationModel implements Policy {
|
||||
|
||||
protected final E entity;
|
||||
|
||||
public AbstractPolicyModel(E entity, StoreFactory storeFactory) {
|
||||
super(storeFactory);
|
||||
|
||||
Objects.requireNonNull(entity, "entity");
|
||||
this.entity = entity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof Policy)) return false;
|
||||
|
||||
Policy that = (Policy) o;
|
||||
return Objects.equals(that.getId(), getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return getId().hashCode();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright 2021 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.models.map.authorization.adapter;
|
||||
|
||||
import org.keycloak.authorization.model.AbstractAuthorizationModel;
|
||||
import org.keycloak.authorization.model.Resource;
|
||||
import org.keycloak.authorization.store.StoreFactory;
|
||||
import org.keycloak.models.map.common.AbstractEntity;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public abstract class AbstractResourceModel<E extends AbstractEntity> extends AbstractAuthorizationModel implements Resource {
|
||||
|
||||
protected final E entity;
|
||||
|
||||
public AbstractResourceModel(E entity, StoreFactory storeFactory) {
|
||||
super(storeFactory);
|
||||
|
||||
Objects.requireNonNull(entity, "entity");
|
||||
this.entity = entity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof Resource)) return false;
|
||||
|
||||
Resource that = (Resource) o;
|
||||
return Objects.equals(that.getId(), getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return getId().hashCode();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright 2021 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.models.map.authorization.adapter;
|
||||
|
||||
import org.keycloak.authorization.model.AbstractAuthorizationModel;
|
||||
import org.keycloak.authorization.model.ResourceServer;
|
||||
import org.keycloak.authorization.store.StoreFactory;
|
||||
import org.keycloak.models.map.common.AbstractEntity;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public abstract class AbstractResourceServerModel<E extends AbstractEntity> extends AbstractAuthorizationModel implements ResourceServer {
|
||||
|
||||
protected final E entity;
|
||||
|
||||
public AbstractResourceServerModel(E entity, StoreFactory storeFactory) {
|
||||
super(storeFactory);
|
||||
|
||||
Objects.requireNonNull(entity, "entity");
|
||||
this.entity = entity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof ResourceServer)) return false;
|
||||
|
||||
ResourceServer that = (ResourceServer) o;
|
||||
return Objects.equals(that.getId(), getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return getId().hashCode();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright 2021 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.models.map.authorization.adapter;
|
||||
|
||||
import org.keycloak.authorization.model.AbstractAuthorizationModel;
|
||||
import org.keycloak.authorization.model.Scope;
|
||||
import org.keycloak.authorization.store.StoreFactory;
|
||||
import org.keycloak.models.map.common.AbstractEntity;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public abstract class AbstractScopeModel<E extends AbstractEntity> extends AbstractAuthorizationModel implements Scope {
|
||||
|
||||
protected final E entity;
|
||||
|
||||
public AbstractScopeModel(E entity, StoreFactory storeFactory) {
|
||||
super(storeFactory);
|
||||
|
||||
Objects.requireNonNull(entity, "entity");
|
||||
this.entity = entity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof Scope)) return false;
|
||||
|
||||
Scope that = (Scope) o;
|
||||
return Objects.equals(that.getId(), getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return getId().hashCode();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,107 @@
|
|||
/*
|
||||
* Copyright 2021 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.models.map.authorization.adapter;
|
||||
|
||||
|
||||
import org.keycloak.authorization.model.Policy;
|
||||
import org.keycloak.authorization.model.Resource;
|
||||
import org.keycloak.authorization.model.ResourceServer;
|
||||
import org.keycloak.authorization.model.Scope;
|
||||
import org.keycloak.authorization.store.StoreFactory;
|
||||
import org.keycloak.models.map.authorization.entity.MapPermissionTicketEntity;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.keycloak.authorization.UserManagedPermissionUtil.updatePolicy;
|
||||
|
||||
public class MapPermissionTicketAdapter extends AbstractPermissionTicketModel<MapPermissionTicketEntity> {
|
||||
|
||||
public MapPermissionTicketAdapter(MapPermissionTicketEntity entity, StoreFactory storeFactory) {
|
||||
super(entity, storeFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return entity.getId().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getOwner() {
|
||||
return entity.getOwner();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRequester() {
|
||||
return entity.getRequester();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Resource getResource() {
|
||||
return storeFactory.getResourceStore().findById(entity.getResourceId().toString(), entity.getResourceServerId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Scope getScope() {
|
||||
if (entity.getScopeId() == null) return null;
|
||||
return storeFactory.getScopeStore().findById(entity.getScopeId().toString(), entity.getResourceServerId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isGranted() {
|
||||
return entity.getGrantedTimestamp() != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getCreatedTimestamp() {
|
||||
return entity.getCreatedTimestamp();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getGrantedTimestamp() {
|
||||
return entity.getGrantedTimestamp();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setGrantedTimestamp(Long millis) {
|
||||
entity.setGrantedTimestamp(millis);
|
||||
updatePolicy(this, storeFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceServer getResourceServer() {
|
||||
return storeFactory.getResourceServerStore().findById(entity.getResourceServerId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Policy getPolicy() {
|
||||
if (entity.getPolicyId() == null) return null;
|
||||
return storeFactory.getPolicyStore().findById(entity.getPolicyId().toString(), entity.getResourceServerId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPolicy(Policy policy) {
|
||||
if (policy != null) {
|
||||
entity.setPolicyId(UUID.fromString(policy.getId()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s@%08x", getId(), System.identityHashCode(this));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,197 @@
|
|||
/*
|
||||
* Copyright 2021 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.models.map.authorization.adapter;
|
||||
|
||||
import org.keycloak.authorization.model.Policy;
|
||||
import org.keycloak.authorization.model.Resource;
|
||||
import org.keycloak.authorization.model.ResourceServer;
|
||||
import org.keycloak.authorization.model.Scope;
|
||||
import org.keycloak.authorization.store.StoreFactory;
|
||||
import org.keycloak.models.map.authorization.entity.MapPolicyEntity;
|
||||
import org.keycloak.representations.idm.authorization.DecisionStrategy;
|
||||
import org.keycloak.representations.idm.authorization.Logic;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class MapPolicyAdapter extends AbstractPolicyModel<MapPolicyEntity> {
|
||||
|
||||
public MapPolicyAdapter(MapPolicyEntity entity, StoreFactory storeFactory) {
|
||||
super(entity, storeFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return entity.getId().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return entity.getType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DecisionStrategy getDecisionStrategy() {
|
||||
return entity.getDecisionStrategy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDecisionStrategy(DecisionStrategy decisionStrategy) {
|
||||
throwExceptionIfReadonly();
|
||||
entity.setDecisionStrategy(decisionStrategy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Logic getLogic() {
|
||||
return entity.getLogic();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLogic(Logic logic) {
|
||||
throwExceptionIfReadonly();
|
||||
entity.setLogic(logic);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getConfig() {
|
||||
return entity.getConfig();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setConfig(Map<String, String> config) {
|
||||
throwExceptionIfReadonly();
|
||||
entity.setConfig(config);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeConfig(String name) {
|
||||
throwExceptionIfReadonly();
|
||||
entity.removeConfig(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putConfig(String name, String value) {
|
||||
throwExceptionIfReadonly();
|
||||
entity.putConfig(name, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return entity.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setName(String name) {
|
||||
throwExceptionIfReadonly();
|
||||
entity.setName(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return entity.getDescription();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDescription(String description) {
|
||||
throwExceptionIfReadonly();
|
||||
entity.setDescription(description);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceServer getResourceServer() {
|
||||
return storeFactory.getResourceServerStore().findById(entity.getResourceServerId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Policy> getAssociatedPolicies() {
|
||||
String resourceServerId = entity.getResourceServerId();
|
||||
return entity.getAssociatedPoliciesIds().stream()
|
||||
.map(policyId -> storeFactory.getPolicyStore().findById(policyId.toString(), resourceServerId))
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Resource> getResources() {
|
||||
String resourceServerId = entity.getResourceServerId();
|
||||
return entity.getResourceIds().stream()
|
||||
.map(resourceId -> storeFactory.getResourceStore().findById(resourceId.toString(), resourceServerId))
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Scope> getScopes() {
|
||||
String resourceServerId = entity.getResourceServerId();
|
||||
return entity.getScopeIds().stream()
|
||||
.map(scopeId -> storeFactory.getScopeStore().findById(scopeId.toString(), resourceServerId))
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getOwner() {
|
||||
return entity.getOwner();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOwner(String owner) {
|
||||
throwExceptionIfReadonly();
|
||||
entity.setOwner(owner);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addScope(Scope scope) {
|
||||
throwExceptionIfReadonly();
|
||||
entity.addScope(UUID.fromString(scope.getId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeScope(Scope scope) {
|
||||
throwExceptionIfReadonly();
|
||||
entity.removeScope(UUID.fromString(scope.getId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAssociatedPolicy(Policy associatedPolicy) {
|
||||
throwExceptionIfReadonly();
|
||||
entity.addAssociatedPolicy(UUID.fromString(associatedPolicy.getId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAssociatedPolicy(Policy associatedPolicy) {
|
||||
throwExceptionIfReadonly();
|
||||
entity.removeAssociatedPolicy(UUID.fromString(associatedPolicy.getId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addResource(Resource resource) {
|
||||
throwExceptionIfReadonly();
|
||||
entity.addResource(UUID.fromString(resource.getId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeResource(Resource resource) {
|
||||
throwExceptionIfReadonly();
|
||||
entity.removeResource(UUID.fromString(resource.getId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s@%08x", getId(), System.identityHashCode(this));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,164 @@
|
|||
/*
|
||||
* Copyright 2021 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.models.map.authorization.adapter;
|
||||
|
||||
import org.keycloak.authorization.model.Scope;
|
||||
import org.keycloak.authorization.store.StoreFactory;
|
||||
import org.keycloak.models.map.authorization.entity.MapResourceEntity;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class MapResourceAdapter extends AbstractResourceModel<MapResourceEntity> {
|
||||
|
||||
public MapResourceAdapter(MapResourceEntity entity, StoreFactory storeFactory) {
|
||||
super(entity, storeFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return entity.getId().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return entity.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setName(String name) {
|
||||
throwExceptionIfReadonly();
|
||||
entity.setName(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return entity.getDisplayName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDisplayName(String name) {
|
||||
throwExceptionIfReadonly();
|
||||
entity.setDisplayName(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getUris() {
|
||||
return entity.getUris();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateUris(Set<String> uri) {
|
||||
throwExceptionIfReadonly();
|
||||
entity.setUris(uri);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return entity.getType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setType(String type) {
|
||||
throwExceptionIfReadonly();
|
||||
entity.setType(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Scope> getScopes() {
|
||||
return entity.getScopeIds().stream()
|
||||
.map(id -> storeFactory
|
||||
.getScopeStore().findById(id.toString(), entity.getResourceServerId()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIconUri() {
|
||||
return entity.getIconUri();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIconUri(String iconUri) {
|
||||
throwExceptionIfReadonly();
|
||||
entity.setIconUri(iconUri);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getResourceServer() {
|
||||
return entity.getResourceServerId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getOwner() {
|
||||
return entity.getOwner();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOwnerManagedAccess() {
|
||||
return entity.isOwnerManagedAccess();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOwnerManagedAccess(boolean ownerManagedAccess) {
|
||||
throwExceptionIfReadonly();
|
||||
entity.setOwnerManagedAccess(ownerManagedAccess);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateScopes(Set<Scope> scopes) {
|
||||
throwExceptionIfReadonly();
|
||||
entity.setScopeIds(scopes.stream().map(Scope::getId).map(UUID::fromString).collect(Collectors.toSet()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, List<String>> getAttributes() {
|
||||
return Collections.unmodifiableMap(new HashMap<>(entity.getAttributes()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSingleAttribute(String name) {
|
||||
return entity.getSingleAttribute(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getAttribute(String name) {
|
||||
return entity.getAttribute(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAttribute(String name, List<String> values) {
|
||||
throwExceptionIfReadonly();
|
||||
entity.setAttribute(name, values);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAttribute(String name) {
|
||||
throwExceptionIfReadonly();
|
||||
entity.removeAttribute(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s@%08x", getId(), System.identityHashCode(this));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* Copyright 2021 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.models.map.authorization.adapter;
|
||||
|
||||
|
||||
import org.keycloak.authorization.store.StoreFactory;
|
||||
import org.keycloak.models.map.authorization.entity.MapResourceServerEntity;
|
||||
import org.keycloak.representations.idm.authorization.DecisionStrategy;
|
||||
import org.keycloak.representations.idm.authorization.PolicyEnforcementMode;
|
||||
|
||||
public class MapResourceServerAdapter extends AbstractResourceServerModel<MapResourceServerEntity> {
|
||||
|
||||
public MapResourceServerAdapter(MapResourceServerEntity entity, StoreFactory storeFactory) {
|
||||
super(entity, storeFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return entity.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAllowRemoteResourceManagement() {
|
||||
return entity.isAllowRemoteResourceManagement();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAllowRemoteResourceManagement(boolean allowRemoteResourceManagement) {
|
||||
throwExceptionIfReadonly();
|
||||
entity.setAllowRemoteResourceManagement(allowRemoteResourceManagement);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PolicyEnforcementMode getPolicyEnforcementMode() {
|
||||
return entity.getPolicyEnforcementMode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPolicyEnforcementMode(PolicyEnforcementMode enforcementMode) {
|
||||
throwExceptionIfReadonly();
|
||||
entity.setPolicyEnforcementMode(enforcementMode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDecisionStrategy(DecisionStrategy decisionStrategy) {
|
||||
throwExceptionIfReadonly();
|
||||
entity.setDecisionStrategy(decisionStrategy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DecisionStrategy getDecisionStrategy() {
|
||||
return entity.getDecisionStrategy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s@%08x", getId(), System.identityHashCode(this));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* Copyright 2021 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.models.map.authorization.adapter;
|
||||
|
||||
|
||||
import org.keycloak.authorization.model.ResourceServer;
|
||||
import org.keycloak.authorization.store.StoreFactory;
|
||||
import org.keycloak.models.map.authorization.entity.MapScopeEntity;
|
||||
|
||||
public class MapScopeAdapter extends AbstractScopeModel<MapScopeEntity> {
|
||||
|
||||
public MapScopeAdapter(MapScopeEntity entity, StoreFactory storeFactory) {
|
||||
super(entity, storeFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return entity.getId().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return entity.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setName(String name) {
|
||||
throwExceptionIfReadonly();
|
||||
entity.setName(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return entity.getDisplayName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDisplayName(String name) {
|
||||
throwExceptionIfReadonly();
|
||||
entity.setDisplayName(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIconUri() {
|
||||
return entity.getIconUri();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIconUri(String iconUri) {
|
||||
throwExceptionIfReadonly();
|
||||
entity.setIconUri(iconUri);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceServer getResourceServer() {
|
||||
return storeFactory.getResourceServerStore().findById(entity.getResourceServerId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s@%08x", getId(), System.identityHashCode(this));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,127 @@
|
|||
/*
|
||||
* Copyright 2021 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.models.map.authorization.entity;
|
||||
|
||||
import org.keycloak.models.map.common.AbstractEntity;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public abstract class AbstractPermissionTicketEntity<K> implements AbstractEntity<K> {
|
||||
|
||||
private final K id;
|
||||
private String owner;
|
||||
private String requester;
|
||||
private Long createdTimestamp;
|
||||
private Long grantedTimestamp;
|
||||
private K resourceId;
|
||||
private K scopeId;
|
||||
private String resourceServerId;
|
||||
private K policyId;
|
||||
private boolean updated = false;
|
||||
|
||||
protected AbstractPermissionTicketEntity(K id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public AbstractPermissionTicketEntity() {
|
||||
this.id = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public K getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getOwner() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
public void setOwner(String owner) {
|
||||
this.updated |= !Objects.equals(this.owner, owner);
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
public String getRequester() {
|
||||
return requester;
|
||||
}
|
||||
|
||||
public void setRequester(String requester) {
|
||||
this.updated |= !Objects.equals(this.requester, requester);
|
||||
this.requester = requester;
|
||||
}
|
||||
|
||||
public Long getCreatedTimestamp() {
|
||||
return createdTimestamp;
|
||||
}
|
||||
|
||||
public void setCreatedTimestamp(Long createdTimestamp) {
|
||||
this.updated |= !Objects.equals(this.createdTimestamp, createdTimestamp);
|
||||
this.createdTimestamp = createdTimestamp;
|
||||
}
|
||||
|
||||
public Long getGrantedTimestamp() {
|
||||
return grantedTimestamp;
|
||||
}
|
||||
|
||||
public void setGrantedTimestamp(Long grantedTimestamp) {
|
||||
this.updated |= !Objects.equals(this.grantedTimestamp, grantedTimestamp);
|
||||
this.grantedTimestamp = grantedTimestamp;
|
||||
}
|
||||
|
||||
public K getResourceId() {
|
||||
return resourceId;
|
||||
}
|
||||
|
||||
public void setResourceId(K resourceId) {
|
||||
this.updated |= !Objects.equals(this.resourceId, resourceId);
|
||||
this.resourceId = resourceId;
|
||||
}
|
||||
|
||||
public K getScopeId() {
|
||||
return scopeId;
|
||||
}
|
||||
|
||||
public void setScopeId(K scopeId) {
|
||||
this.updated |= !Objects.equals(this.scopeId, scopeId);
|
||||
this.scopeId = scopeId;
|
||||
}
|
||||
|
||||
public String getResourceServerId() {
|
||||
return resourceServerId;
|
||||
}
|
||||
|
||||
public void setResourceServerId(String resourceServerId) {
|
||||
this.updated |= !Objects.equals(this.resourceServerId, resourceServerId);
|
||||
this.resourceServerId = resourceServerId;
|
||||
}
|
||||
|
||||
public K getPolicyId() {
|
||||
return policyId;
|
||||
}
|
||||
|
||||
public void setPolicyId(K policyId) {
|
||||
this.updated |= !Objects.equals(this.policyId, policyId);
|
||||
this.policyId = policyId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUpdated() {
|
||||
return updated;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,191 @@
|
|||
/*
|
||||
* Copyright 2021 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.models.map.authorization.entity;
|
||||
|
||||
import org.keycloak.models.map.common.AbstractEntity;
|
||||
import org.keycloak.representations.idm.authorization.DecisionStrategy;
|
||||
import org.keycloak.representations.idm.authorization.Logic;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
public abstract class AbstractPolicyEntity<K> implements AbstractEntity<K> {
|
||||
|
||||
public static final Comparator<AbstractPolicyEntity<?>> COMPARE_BY_NAME = Comparator.comparing(AbstractPolicyEntity::getName);
|
||||
|
||||
private final K id;
|
||||
private String name;
|
||||
private String description;
|
||||
private String type;
|
||||
private DecisionStrategy decisionStrategy = DecisionStrategy.UNANIMOUS;
|
||||
private Logic logic = Logic.POSITIVE;
|
||||
private final Map<String, String> config = new HashMap<>();
|
||||
private String resourceServerId;
|
||||
private final Set<K> associatedPoliciesIds = new HashSet<>();
|
||||
private final Set<K> resourceIds = new HashSet<>();
|
||||
private final Set<K> scopeIds = new HashSet<>();
|
||||
private String owner;
|
||||
private boolean updated = false;
|
||||
|
||||
protected AbstractPolicyEntity(K id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public AbstractPolicyEntity() {
|
||||
this.id = null;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.updated |= !Objects.equals(this.name, name);
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.updated |= !Objects.equals(this.description, description);
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.updated |= !Objects.equals(this.type, type);
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public DecisionStrategy getDecisionStrategy() {
|
||||
return decisionStrategy;
|
||||
}
|
||||
|
||||
public void setDecisionStrategy(DecisionStrategy decisionStrategy) {
|
||||
this.updated |= !Objects.equals(this.decisionStrategy, decisionStrategy);
|
||||
this.decisionStrategy = decisionStrategy;
|
||||
}
|
||||
|
||||
public Logic getLogic() {
|
||||
return logic;
|
||||
}
|
||||
|
||||
public void setLogic(Logic logic) {
|
||||
this.updated |= !Objects.equals(this.logic, logic);
|
||||
this.logic = logic;
|
||||
}
|
||||
|
||||
public Map<String, String> getConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
public String getConfigValue(String name) {
|
||||
return config.get(name);
|
||||
}
|
||||
|
||||
public void setConfig(Map<String, String> config) {
|
||||
if (Objects.equals(this.config, config)) return;
|
||||
|
||||
this.updated = true;
|
||||
this.config.clear();
|
||||
if (config != null) {
|
||||
this.config.putAll(config);
|
||||
}
|
||||
}
|
||||
|
||||
public void removeConfig(String name) {
|
||||
this.updated |= this.config.remove(name) != null;
|
||||
}
|
||||
|
||||
public void putConfig(String name, String value) {
|
||||
this.updated |= !Objects.equals(value, this.config.put(name, value));
|
||||
}
|
||||
|
||||
public String getResourceServerId() {
|
||||
return resourceServerId;
|
||||
}
|
||||
|
||||
public void setResourceServerId(String resourceServerId) {
|
||||
this.updated |= !Objects.equals(this.resourceServerId, resourceServerId);
|
||||
this.resourceServerId = resourceServerId;
|
||||
}
|
||||
|
||||
public Set<K> getAssociatedPoliciesIds() {
|
||||
return associatedPoliciesIds;
|
||||
}
|
||||
|
||||
public void addAssociatedPolicy(K policyId) {
|
||||
this.updated |= this.associatedPoliciesIds.add(policyId);
|
||||
}
|
||||
|
||||
public void removeAssociatedPolicy(K policyId) {
|
||||
this.updated |= this.associatedPoliciesIds.remove(policyId);
|
||||
}
|
||||
|
||||
public Set<K> getResourceIds() {
|
||||
return resourceIds;
|
||||
}
|
||||
|
||||
public void addResource(K resourceId) {
|
||||
this.updated |= this.resourceIds.add(resourceId);
|
||||
}
|
||||
|
||||
public void removeResource(K resourceId) {
|
||||
this.updated |= this.resourceIds.remove(resourceId);
|
||||
}
|
||||
|
||||
public Set<K> getScopeIds() {
|
||||
return scopeIds;
|
||||
}
|
||||
|
||||
public void addScope(K scopeId) {
|
||||
this.updated |= this.scopeIds.add(scopeId);
|
||||
}
|
||||
|
||||
public void removeScope(K scopeId) {
|
||||
this.updated |= this.scopeIds.remove(scopeId);
|
||||
}
|
||||
|
||||
public String getOwner() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
public void setOwner(String owner) {
|
||||
this.updated |= !Objects.equals(this.owner, owner);
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public K getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUpdated() {
|
||||
return updated;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,183 @@
|
|||
/*
|
||||
* Copyright 2021 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.models.map.authorization.entity;
|
||||
|
||||
import org.keycloak.models.map.common.AbstractEntity;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
public abstract class AbstractResourceEntity<K> implements AbstractEntity<K> {
|
||||
|
||||
public static final Comparator<AbstractResourceEntity<?>> COMPARE_BY_NAME = Comparator.comparing(AbstractResourceEntity::getName);
|
||||
|
||||
private final K id;
|
||||
private String name;
|
||||
private String displayName;
|
||||
private final Set<String> uris = new HashSet<>();
|
||||
private String type;
|
||||
private String iconUri;
|
||||
private String owner;
|
||||
private boolean ownerManagedAccess;
|
||||
private String resourceServerId;
|
||||
private final Set<K> scopeIds = new HashSet<>();
|
||||
private final Set<K> policyIds = new HashSet<>();
|
||||
private final Map<String, List<String>> attributes = new HashMap<>();
|
||||
private boolean updated = false;
|
||||
|
||||
protected AbstractResourceEntity(K id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public AbstractResourceEntity() {
|
||||
this.id = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public K getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.updated |= !Objects.equals(this.name, name);
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
public void setDisplayName(String displayName) {
|
||||
this.updated |= !Objects.equals(this.displayName, displayName);
|
||||
this.displayName = displayName;
|
||||
}
|
||||
|
||||
public Set<String> getUris() {
|
||||
return uris;
|
||||
}
|
||||
|
||||
public void setUris(Set<String> uris) {
|
||||
if (Objects.equals(this.uris, uris)) return;
|
||||
|
||||
this.updated = true;
|
||||
this.uris.clear();
|
||||
|
||||
if (uris != null) {
|
||||
this.uris.addAll(uris);
|
||||
}
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.updated |= !Objects.equals(this.type, type);
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getIconUri() {
|
||||
return iconUri;
|
||||
}
|
||||
|
||||
public void setIconUri(String iconUri) {
|
||||
this.updated |= !Objects.equals(this.iconUri, iconUri);
|
||||
this.iconUri = iconUri;
|
||||
}
|
||||
|
||||
public String getOwner() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
public void setOwner(String owner) {
|
||||
this.updated |= !Objects.equals(this.owner, owner);
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
public boolean isOwnerManagedAccess() {
|
||||
return ownerManagedAccess;
|
||||
}
|
||||
|
||||
public void setOwnerManagedAccess(boolean ownerManagedAccess) {
|
||||
this.updated |= this.ownerManagedAccess != ownerManagedAccess;
|
||||
this.ownerManagedAccess = ownerManagedAccess;
|
||||
}
|
||||
|
||||
public String getResourceServerId() {
|
||||
return resourceServerId;
|
||||
}
|
||||
|
||||
public void setResourceServerId(String resourceServerId) {
|
||||
this.updated |= !Objects.equals(this.resourceServerId, resourceServerId);
|
||||
this.resourceServerId = resourceServerId;
|
||||
}
|
||||
|
||||
public Set<K> getScopeIds() {
|
||||
return scopeIds;
|
||||
}
|
||||
|
||||
public void setScopeIds(Set<K> scopeIds) {
|
||||
if (Objects.equals(this.scopeIds, scopeIds)) return;
|
||||
|
||||
this.updated = true;
|
||||
this.scopeIds.clear();
|
||||
if (scopeIds != null) {
|
||||
this.scopeIds.addAll(scopeIds);
|
||||
}
|
||||
}
|
||||
|
||||
public Set<K> getPolicyIds() {
|
||||
return policyIds;
|
||||
}
|
||||
|
||||
public Map<String, List<String>> getAttributes() {
|
||||
return attributes;
|
||||
}
|
||||
|
||||
public List<String> getAttribute(String name) {
|
||||
return attributes.get(name);
|
||||
}
|
||||
|
||||
public String getSingleAttribute(String name) {
|
||||
List<String> attributeValues = attributes.get(name);
|
||||
return attributeValues == null || attributeValues.isEmpty() ? null : attributeValues.get(0);
|
||||
}
|
||||
|
||||
public void setAttribute(String name, List<String> value) {
|
||||
this.updated |= !Objects.equals(this.attributes.put(name, value), value);
|
||||
}
|
||||
|
||||
public void removeAttribute(String name) {
|
||||
this.updated |= this.attributes.remove(name) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUpdated() {
|
||||
return updated;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* Copyright 2021 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.models.map.authorization.entity;
|
||||
|
||||
import org.keycloak.models.map.common.AbstractEntity;
|
||||
import org.keycloak.representations.idm.authorization.DecisionStrategy;
|
||||
import org.keycloak.representations.idm.authorization.PolicyEnforcementMode;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public abstract class AbstractResourceServerEntity<K> implements AbstractEntity<K> {
|
||||
|
||||
private final K id;
|
||||
private boolean updated = false;
|
||||
|
||||
private boolean allowRemoteResourceManagement;
|
||||
private PolicyEnforcementMode policyEnforcementMode = PolicyEnforcementMode.ENFORCING;
|
||||
private DecisionStrategy decisionStrategy = DecisionStrategy.UNANIMOUS;
|
||||
|
||||
protected AbstractResourceServerEntity(K id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public AbstractResourceServerEntity() {
|
||||
this.id = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public K getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public boolean isAllowRemoteResourceManagement() {
|
||||
return allowRemoteResourceManagement;
|
||||
}
|
||||
|
||||
public void setAllowRemoteResourceManagement(boolean allowRemoteResourceManagement) {
|
||||
this.updated |= this.allowRemoteResourceManagement != allowRemoteResourceManagement;
|
||||
this.allowRemoteResourceManagement = allowRemoteResourceManagement;
|
||||
}
|
||||
|
||||
public PolicyEnforcementMode getPolicyEnforcementMode() {
|
||||
return policyEnforcementMode;
|
||||
}
|
||||
|
||||
public void setPolicyEnforcementMode(PolicyEnforcementMode policyEnforcementMode) {
|
||||
this.updated |= !Objects.equals(this.policyEnforcementMode, policyEnforcementMode);
|
||||
this.policyEnforcementMode = policyEnforcementMode;
|
||||
}
|
||||
|
||||
public DecisionStrategy getDecisionStrategy() {
|
||||
return decisionStrategy;
|
||||
}
|
||||
|
||||
public void setDecisionStrategy(DecisionStrategy decisionStrategy) {
|
||||
this.updated |= !Objects.equals(this.decisionStrategy, decisionStrategy);
|
||||
this.decisionStrategy = decisionStrategy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUpdated() {
|
||||
return updated;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
* Copyright 2021 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.models.map.authorization.entity;
|
||||
|
||||
import org.keycloak.models.map.common.AbstractEntity;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public abstract class AbstractScopeEntity<K> implements AbstractEntity<K> {
|
||||
|
||||
private final K id;
|
||||
private String name;
|
||||
private String displayName;
|
||||
private String iconUri;
|
||||
private String resourceServerId;
|
||||
private boolean updated = false;
|
||||
|
||||
protected AbstractScopeEntity(K id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public AbstractScopeEntity() {
|
||||
this.id = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public K getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.updated |= !Objects.equals(this.name, name);
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
public void setDisplayName(String displayName) {
|
||||
this.updated |= !Objects.equals(this.displayName, displayName);
|
||||
this.displayName = displayName;
|
||||
}
|
||||
|
||||
public String getIconUri() {
|
||||
return iconUri;
|
||||
}
|
||||
|
||||
public void setIconUri(String iconUri) {
|
||||
this.updated |= !Objects.equals(this.iconUri, iconUri);
|
||||
this.iconUri = iconUri;
|
||||
}
|
||||
|
||||
public String getResourceServerId() {
|
||||
return resourceServerId;
|
||||
}
|
||||
|
||||
public void setResourceServerId(String resourceServerId) {
|
||||
this.updated |= !Objects.equals(this.resourceServerId, resourceServerId);
|
||||
this.resourceServerId = resourceServerId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUpdated() {
|
||||
return updated;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright 2021 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.models.map.authorization.entity;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.UUID;
|
||||
|
||||
public class MapPermissionTicketEntity extends AbstractPermissionTicketEntity<UUID> {
|
||||
|
||||
public static final Comparator<AbstractPermissionTicketEntity<UUID>> COMPARE_BY_ID = Comparator.comparing(AbstractPermissionTicketEntity::getId);
|
||||
public static final Comparator<AbstractPermissionTicketEntity<UUID>> COMPARE_BY_RESOURCE_ID = Comparator.comparing(AbstractPermissionTicketEntity::getResourceId);
|
||||
|
||||
|
||||
protected MapPermissionTicketEntity() {
|
||||
super();
|
||||
}
|
||||
|
||||
public MapPermissionTicketEntity(UUID id) {
|
||||
super(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s@%08x", getId(), System.identityHashCode(this));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Copyright 2021 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.models.map.authorization.entity;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class MapPolicyEntity extends AbstractPolicyEntity<UUID> {
|
||||
protected MapPolicyEntity() {
|
||||
super();
|
||||
}
|
||||
|
||||
public MapPolicyEntity(UUID id) {
|
||||
super(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s@%08x", getId(), System.identityHashCode(this));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright 2021 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.models.map.authorization.entity;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.UUID;
|
||||
|
||||
public class MapResourceEntity extends AbstractResourceEntity<UUID> {
|
||||
public static final Comparator<AbstractResourceEntity<UUID>> COMPARE_BY_ID = Comparator.comparing(AbstractResourceEntity::getId);
|
||||
|
||||
protected MapResourceEntity() {
|
||||
super();
|
||||
}
|
||||
|
||||
public MapResourceEntity(UUID id) {
|
||||
super(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s@%08x", getId(), System.identityHashCode(this));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright 2021 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.models.map.authorization.entity;
|
||||
|
||||
public class MapResourceServerEntity extends AbstractResourceServerEntity<String> {
|
||||
protected MapResourceServerEntity() {
|
||||
super();
|
||||
}
|
||||
|
||||
public MapResourceServerEntity(String id) {
|
||||
super(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s@%08x", getId(), System.identityHashCode(this));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Copyright 2021 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.models.map.authorization.entity;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class MapScopeEntity extends AbstractScopeEntity<UUID> {
|
||||
protected MapScopeEntity() {
|
||||
super();
|
||||
}
|
||||
|
||||
public MapScopeEntity(UUID id) {
|
||||
super(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s@%08x", getId(), System.identityHashCode(this));
|
||||
}
|
||||
}
|
|
@ -16,6 +16,11 @@
|
|||
*/
|
||||
package org.keycloak.models.map.storage;
|
||||
|
||||
import org.keycloak.authorization.model.PermissionTicket;
|
||||
import org.keycloak.authorization.model.Policy;
|
||||
import org.keycloak.authorization.model.Resource;
|
||||
import org.keycloak.authorization.model.ResourceServer;
|
||||
import org.keycloak.authorization.model.Scope;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.ClientScopeModel;
|
||||
import org.keycloak.models.GroupModel;
|
||||
|
@ -23,6 +28,11 @@ import org.keycloak.models.RealmModel;
|
|||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.map.authSession.AbstractRootAuthenticationSessionEntity;
|
||||
import org.keycloak.models.map.authorization.entity.AbstractPermissionTicketEntity;
|
||||
import org.keycloak.models.map.authorization.entity.AbstractPolicyEntity;
|
||||
import org.keycloak.models.map.authorization.entity.AbstractResourceEntity;
|
||||
import org.keycloak.models.map.authorization.entity.AbstractResourceServerEntity;
|
||||
import org.keycloak.models.map.authorization.entity.AbstractScopeEntity;
|
||||
import org.keycloak.models.map.client.AbstractClientEntity;
|
||||
import org.keycloak.models.map.clientscope.AbstractClientScopeEntity;
|
||||
import org.keycloak.models.map.common.AbstractEntity;
|
||||
|
@ -58,6 +68,11 @@ public class MapFieldPredicates {
|
|||
public static final Map<SearchableModelField<RoleModel>, UpdatePredicatesFunc<Object, AbstractRoleEntity<Object>, RoleModel>> ROLE_PREDICATES = basePredicates(RoleModel.SearchableFields.ID);
|
||||
public static final Map<SearchableModelField<UserModel>, UpdatePredicatesFunc<Object, AbstractUserEntity<Object>, UserModel>> USER_PREDICATES = basePredicates(UserModel.SearchableFields.ID);
|
||||
public static final Map<SearchableModelField<RootAuthenticationSessionModel>, UpdatePredicatesFunc<Object, AbstractRootAuthenticationSessionEntity<Object>, RootAuthenticationSessionModel>> AUTHENTICATION_SESSION_PREDICATES = basePredicates(RootAuthenticationSessionModel.SearchableFields.ID);
|
||||
public static final Map<SearchableModelField<ResourceServer>, UpdatePredicatesFunc<Object, AbstractResourceServerEntity<Object>, ResourceServer>> AUTHZ_RESOURCE_SERVER_PREDICATES = basePredicates(ResourceServer.SearchableFields.ID);
|
||||
public static final Map<SearchableModelField<Resource>, UpdatePredicatesFunc<Object, AbstractResourceEntity<Object>, Resource>> AUTHZ_RESOURCE_PREDICATES = basePredicates(Resource.SearchableFields.ID);
|
||||
public static final Map<SearchableModelField<Scope>, UpdatePredicatesFunc<Object, AbstractScopeEntity<Object>, Scope>> AUTHZ_SCOPE_PREDICATES = basePredicates(Scope.SearchableFields.ID);
|
||||
public static final Map<SearchableModelField<PermissionTicket>, UpdatePredicatesFunc<Object, AbstractPermissionTicketEntity<Object>, PermissionTicket>> AUTHZ_PERMISSION_TICKET_PREDICATES = basePredicates(PermissionTicket.SearchableFields.ID);
|
||||
public static final Map<SearchableModelField<Policy>, UpdatePredicatesFunc<Object, AbstractPolicyEntity<Object>, Policy>> AUTHZ_POLICY_PREDICATES = basePredicates(Policy.SearchableFields.ID);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static final Map<Class<?>, Map> PREDICATES = new HashMap<>();
|
||||
|
@ -105,6 +120,40 @@ public class MapFieldPredicates {
|
|||
|
||||
put(AUTHENTICATION_SESSION_PREDICATES, RootAuthenticationSessionModel.SearchableFields.REALM_ID, AbstractRootAuthenticationSessionEntity::getRealmId);
|
||||
put(AUTHENTICATION_SESSION_PREDICATES, RootAuthenticationSessionModel.SearchableFields.TIMESTAMP, AbstractRootAuthenticationSessionEntity::getTimestamp);
|
||||
|
||||
put(AUTHZ_RESOURCE_SERVER_PREDICATES, ResourceServer.SearchableFields.ID, AbstractResourceServerEntity::getId);
|
||||
|
||||
put(AUTHZ_RESOURCE_PREDICATES, Resource.SearchableFields.ID, predicateForKeyField(AbstractResourceEntity::getId));
|
||||
put(AUTHZ_RESOURCE_PREDICATES, Resource.SearchableFields.NAME, AbstractResourceEntity::getName);
|
||||
put(AUTHZ_RESOURCE_PREDICATES, Resource.SearchableFields.RESOURCE_SERVER_ID, AbstractResourceEntity::getResourceServerId);
|
||||
put(AUTHZ_RESOURCE_PREDICATES, Resource.SearchableFields.OWNER, AbstractResourceEntity::getOwner);
|
||||
put(AUTHZ_RESOURCE_PREDICATES, Resource.SearchableFields.TYPE, AbstractResourceEntity::getType);
|
||||
put(AUTHZ_RESOURCE_PREDICATES, Resource.SearchableFields.URI, MapFieldPredicates::checkResourceUri);
|
||||
put(AUTHZ_RESOURCE_PREDICATES, Resource.SearchableFields.SCOPE_ID, MapFieldPredicates::checkResourceScopes);
|
||||
put(AUTHZ_RESOURCE_PREDICATES, Resource.SearchableFields.OWNER_MANAGED_ACCESS, AbstractResourceEntity::isOwnerManagedAccess);
|
||||
|
||||
put(AUTHZ_SCOPE_PREDICATES, Scope.SearchableFields.ID, predicateForKeyField(AbstractScopeEntity::getId));
|
||||
put(AUTHZ_SCOPE_PREDICATES, Scope.SearchableFields.RESOURCE_SERVER_ID, AbstractScopeEntity::getResourceServerId);
|
||||
put(AUTHZ_SCOPE_PREDICATES, Scope.SearchableFields.NAME, AbstractScopeEntity::getName);
|
||||
|
||||
put(AUTHZ_PERMISSION_TICKET_PREDICATES, PermissionTicket.SearchableFields.ID, predicateForKeyField(AbstractPermissionTicketEntity::getId));
|
||||
put(AUTHZ_PERMISSION_TICKET_PREDICATES, PermissionTicket.SearchableFields.OWNER, AbstractPermissionTicketEntity::getOwner);
|
||||
put(AUTHZ_PERMISSION_TICKET_PREDICATES, PermissionTicket.SearchableFields.REQUESTER, AbstractPermissionTicketEntity::getRequester);
|
||||
put(AUTHZ_PERMISSION_TICKET_PREDICATES, PermissionTicket.SearchableFields.RESOURCE_SERVER_ID, AbstractPermissionTicketEntity::getResourceServerId);
|
||||
put(AUTHZ_PERMISSION_TICKET_PREDICATES, PermissionTicket.SearchableFields.RESOURCE_ID, predicateForKeyField(AbstractPermissionTicketEntity::getResourceId));
|
||||
put(AUTHZ_PERMISSION_TICKET_PREDICATES, PermissionTicket.SearchableFields.SCOPE_ID, predicateForKeyField(AbstractPermissionTicketEntity::getScopeId));
|
||||
put(AUTHZ_PERMISSION_TICKET_PREDICATES, PermissionTicket.SearchableFields.POLICY_ID, predicateForKeyField(AbstractPermissionTicketEntity::getPolicyId));
|
||||
put(AUTHZ_PERMISSION_TICKET_PREDICATES, PermissionTicket.SearchableFields.GRANTED_TIMESTAMP, AbstractPermissionTicketEntity::getGrantedTimestamp);
|
||||
|
||||
put(AUTHZ_POLICY_PREDICATES, Policy.SearchableFields.ID, predicateForKeyField(AbstractPolicyEntity::getId));
|
||||
put(AUTHZ_POLICY_PREDICATES, Policy.SearchableFields.NAME, AbstractPolicyEntity::getName);
|
||||
put(AUTHZ_POLICY_PREDICATES, Policy.SearchableFields.OWNER, AbstractPolicyEntity::getOwner);
|
||||
put(AUTHZ_POLICY_PREDICATES, Policy.SearchableFields.TYPE, AbstractPolicyEntity::getType);
|
||||
put(AUTHZ_POLICY_PREDICATES, Policy.SearchableFields.RESOURCE_SERVER_ID, AbstractPolicyEntity::getResourceServerId);
|
||||
put(AUTHZ_POLICY_PREDICATES, Policy.SearchableFields.RESOURCE_ID, MapFieldPredicates::checkPolicyResources);
|
||||
put(AUTHZ_POLICY_PREDICATES, Policy.SearchableFields.SCOPE_ID, MapFieldPredicates::checkPolicyScopes);
|
||||
put(AUTHZ_POLICY_PREDICATES, Policy.SearchableFields.CONFIG, MapFieldPredicates::checkPolicyConfig);
|
||||
put(AUTHZ_POLICY_PREDICATES, Policy.SearchableFields.ASSOCIATED_POLICY_ID, MapFieldPredicates::checkAssociatedPolicy);
|
||||
}
|
||||
|
||||
static {
|
||||
|
@ -115,6 +164,11 @@ public class MapFieldPredicates {
|
|||
PREDICATES.put(GroupModel.class, GROUP_PREDICATES);
|
||||
PREDICATES.put(UserModel.class, USER_PREDICATES);
|
||||
PREDICATES.put(RootAuthenticationSessionModel.class, AUTHENTICATION_SESSION_PREDICATES);
|
||||
PREDICATES.put(ResourceServer.class, AUTHZ_RESOURCE_SERVER_PREDICATES);
|
||||
PREDICATES.put(Resource.class, AUTHZ_RESOURCE_PREDICATES);
|
||||
PREDICATES.put(Scope.class, AUTHZ_SCOPE_PREDICATES);
|
||||
PREDICATES.put(PermissionTicket.class, AUTHZ_PERMISSION_TICKET_PREDICATES);
|
||||
PREDICATES.put(Policy.class, AUTHZ_POLICY_PREDICATES);
|
||||
}
|
||||
|
||||
private static <K, V extends AbstractEntity<K>, M> void put(
|
||||
|
@ -129,7 +183,18 @@ public class MapFieldPredicates {
|
|||
map.put(field, function);
|
||||
}
|
||||
|
||||
private static <V extends AbstractEntity<?>> Function<V, Object> predicateForKeyField(Function<V, Object> extractor) {
|
||||
return entity -> {
|
||||
Object o = extractor.apply(entity);
|
||||
return o == null ? null : o.toString();
|
||||
};
|
||||
}
|
||||
|
||||
private static String ensureEqSingleValue(SearchableModelField<?> field, String parameterName, Operator op, Object[] values) throws CriterionNotSupportedException {
|
||||
return ensureEqSingleValue(field, parameterName, op, values, String.class);
|
||||
}
|
||||
|
||||
private static <T> T ensureEqSingleValue(SearchableModelField<?> field, String parameterName, Operator op, Object[] values, Class<T> expectedType) throws CriterionNotSupportedException {
|
||||
if (op != Operator.EQ) {
|
||||
throw new CriterionNotSupportedException(field, op);
|
||||
}
|
||||
|
@ -138,11 +203,11 @@ public class MapFieldPredicates {
|
|||
}
|
||||
|
||||
final Object ob = values[0];
|
||||
if (! (ob instanceof String)) {
|
||||
throw new CriterionNotSupportedException(field, op, "Invalid arguments, expected (String role_id), got: " + Arrays.toString(values));
|
||||
if (!expectedType.isAssignableFrom(ob.getClass())) {
|
||||
throw new CriterionNotSupportedException(field, op, "Invalid arguments, expected (" + expectedType.getName() + "), got: " + Arrays.toString(values));
|
||||
}
|
||||
String s = (String) ob;
|
||||
return s;
|
||||
|
||||
return expectedType.cast(ob);
|
||||
}
|
||||
|
||||
private static MapModelCriteriaBuilder<Object, AbstractClientEntity<Object>, ClientModel> checkScopeMappingRole(MapModelCriteriaBuilder<Object, AbstractClientEntity<Object>, ClientModel> mcb, Operator op, Object[] values) {
|
||||
|
@ -198,6 +263,100 @@ public class MapFieldPredicates {
|
|||
return mcb.fieldCompare(Boolean.TRUE::equals, getter);
|
||||
}
|
||||
|
||||
private static MapModelCriteriaBuilder<Object, AbstractResourceEntity<Object>, Resource> checkResourceUri(MapModelCriteriaBuilder<Object, AbstractResourceEntity<Object>, Resource> mcb, Operator op, Object[] values) {
|
||||
Function<AbstractResourceEntity<Object>, ?> getter;
|
||||
|
||||
if (Operator.EXISTS.equals(op)) {
|
||||
getter = re -> re.getUris() != null && !re.getUris().isEmpty();
|
||||
} else if (op == Operator.IN && values != null && values.length == 1 && (values[0] instanceof Collection)) {
|
||||
Collection<?> c = (Collection<?>) values[0];
|
||||
getter = re -> re.getUris().stream().anyMatch(c::contains);
|
||||
} else {
|
||||
String uri = ensureEqSingleValue(Resource.SearchableFields.URI, "uri", op, values);
|
||||
getter = re -> re.getUris().contains(uri);
|
||||
}
|
||||
|
||||
return mcb.fieldCompare(Boolean.TRUE::equals, getter);
|
||||
}
|
||||
|
||||
private static MapModelCriteriaBuilder<Object, AbstractResourceEntity<Object>, Resource> checkResourceScopes(MapModelCriteriaBuilder<Object, AbstractResourceEntity<Object>, Resource> mcb, Operator op, Object[] values) {
|
||||
Function<AbstractResourceEntity<Object>, ?> getter;
|
||||
|
||||
if (op == Operator.IN && values != null && values.length == 1 && (values[0] instanceof Collection)) {
|
||||
Collection<?> c = (Collection<?>) values[0];
|
||||
getter = re -> re.getScopeIds().stream().map(Object::toString).anyMatch(c::contains);
|
||||
} else {
|
||||
String scope = ensureEqSingleValue(Resource.SearchableFields.URI, "scope_id", op, values);
|
||||
getter = re -> re.getScopeIds().stream().map(Object::toString).anyMatch(scope::equals);
|
||||
}
|
||||
|
||||
return mcb.fieldCompare(Boolean.TRUE::equals, getter);
|
||||
}
|
||||
|
||||
private static MapModelCriteriaBuilder<Object, AbstractPolicyEntity<Object>, Policy> checkPolicyResources(MapModelCriteriaBuilder<Object, AbstractPolicyEntity<Object>, Policy> mcb, Operator op, Object[] values) {
|
||||
Function<AbstractPolicyEntity<Object>, ?> getter;
|
||||
|
||||
if (op == Operator.NOT_EXISTS) {
|
||||
getter = re -> re.getResourceIds().isEmpty();
|
||||
} else if (op == Operator.IN && values != null && values.length == 1 && (values[0] instanceof Collection)) {
|
||||
Collection<?> c = (Collection<?>) values[0];
|
||||
getter = re -> re.getResourceIds().stream().map(Object::toString).anyMatch(c::contains);
|
||||
} else {
|
||||
String scope = ensureEqSingleValue(Policy.SearchableFields.RESOURCE_ID, "resource_id", op, values, String.class);
|
||||
getter = re -> re.getResourceIds().stream().map(Object::toString).anyMatch(scope::equals);
|
||||
}
|
||||
|
||||
return mcb.fieldCompare(Boolean.TRUE::equals, getter);
|
||||
}
|
||||
|
||||
private static MapModelCriteriaBuilder<Object, AbstractPolicyEntity<Object>, Policy> checkPolicyScopes(MapModelCriteriaBuilder<Object, AbstractPolicyEntity<Object>, Policy> mcb, Operator op, Object[] values) {
|
||||
Function<AbstractPolicyEntity<Object>, ?> getter;
|
||||
|
||||
if (op == Operator.IN && values != null && values.length == 1 && (values[0] instanceof Collection)) {
|
||||
Collection<?> c = (Collection<?>) values[0];
|
||||
getter = re -> re.getScopeIds().stream().map(Object::toString).anyMatch(c::contains); // TODO: Use KeyConverter
|
||||
} else {
|
||||
String scope = ensureEqSingleValue(Policy.SearchableFields.CONFIG, "scope_id", op, values);
|
||||
getter = re -> re.getScopeIds().stream().map(Object::toString).anyMatch(scope::equals);
|
||||
}
|
||||
|
||||
return mcb.fieldCompare(Boolean.TRUE::equals, getter);
|
||||
}
|
||||
|
||||
private static MapModelCriteriaBuilder<Object, AbstractPolicyEntity<Object>, Policy> checkPolicyConfig(MapModelCriteriaBuilder<Object, AbstractPolicyEntity<Object>, Policy> mcb, Operator op, Object[] values) {
|
||||
Function<AbstractPolicyEntity<Object>, ?> getter;
|
||||
|
||||
final Object attrName = values[0];
|
||||
if (!(attrName instanceof String)) {
|
||||
throw new CriterionNotSupportedException(UserModel.SearchableFields.ATTRIBUTE, op, "Invalid arguments, expected (String attribute_name), got: " + Arrays.toString(values));
|
||||
}
|
||||
|
||||
String attrNameS = (String) attrName;
|
||||
Object[] realValues = new Object[values.length - 1];
|
||||
System.arraycopy(values, 1, realValues, 0, values.length - 1);
|
||||
Predicate<Object> valueComparator = CriteriaOperator.predicateFor(op, realValues);
|
||||
getter = pe -> {
|
||||
final String configValue = pe.getConfigValue(attrNameS);
|
||||
return valueComparator.test(configValue);
|
||||
};
|
||||
|
||||
return mcb.fieldCompare(Boolean.TRUE::equals, getter);
|
||||
}
|
||||
|
||||
private static MapModelCriteriaBuilder<Object, AbstractPolicyEntity<Object>, Policy> checkAssociatedPolicy(MapModelCriteriaBuilder<Object, AbstractPolicyEntity<Object>, Policy> mcb, Operator op, Object[] values) {
|
||||
Function<AbstractPolicyEntity<Object>, ?> getter;
|
||||
|
||||
if (op == Operator.IN && values != null && values.length == 1 && (values[0] instanceof Collection)) {
|
||||
Collection<?> c = (Collection<?>) values[0];
|
||||
getter = re -> re.getAssociatedPoliciesIds().stream().map(Object::toString).anyMatch(c::contains);
|
||||
} else {
|
||||
String policyId = ensureEqSingleValue(Policy.SearchableFields.ASSOCIATED_POLICY_ID, "associated_policy_id", op, values);
|
||||
getter = re -> re.getAssociatedPoliciesIds().stream().map(Object::toString).anyMatch(policyId::equals);
|
||||
}
|
||||
|
||||
return mcb.fieldCompare(Boolean.TRUE::equals, getter);
|
||||
}
|
||||
|
||||
private static MapModelCriteriaBuilder<Object, AbstractUserEntity<Object>, UserModel> checkUserGroup(MapModelCriteriaBuilder<Object, AbstractUserEntity<Object>, UserModel> mcb, Operator op, Object[] values) {
|
||||
Function<AbstractUserEntity<Object>, ?> getter;
|
||||
if (op == Operator.IN && values != null && values.length == 1 && (values[0] instanceof Collection)) {
|
||||
|
|
|
@ -91,10 +91,11 @@ public class MapModelCriteriaBuilder<K, V extends AbstractEntity<K>, M> implemen
|
|||
}
|
||||
Predicate<? super K> resIndexFilter = b.getKeyFilter() == ALWAYS_TRUE ? ALWAYS_TRUE : b.getKeyFilter().negate();
|
||||
Predicate<? super V> resEntityFilter = b.getEntityFilter() == ALWAYS_TRUE ? ALWAYS_TRUE : b.getEntityFilter().negate();
|
||||
|
||||
return new MapModelCriteriaBuilder<>(
|
||||
fieldPredicates,
|
||||
v -> keyFilter.test(v) && ! resIndexFilter.test(v),
|
||||
v -> entityFilter.test(v) && ! resEntityFilter.test(v)
|
||||
v -> keyFilter.test(v) && resIndexFilter.test(v),
|
||||
v -> entityFilter.test(v) && resEntityFilter.test(v)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -50,6 +50,7 @@ import org.keycloak.storage.StorageId;
|
|||
import org.keycloak.storage.UserStorageProvider;
|
||||
import org.keycloak.storage.client.ClientStorageProvider;
|
||||
|
||||
import java.util.EnumMap;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
@ -691,9 +692,8 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialStor
|
|||
|
||||
HashSet<String> authorizedGroups = new HashSet<>(userGroups);
|
||||
authorizedGroups.removeIf(id -> {
|
||||
Map<String, String[]> values = new HashMap<>();
|
||||
values.put(Resource.EXACT_NAME, new String[] { "true" });
|
||||
values.put("name", new String[] { "group.resource." + id });
|
||||
Map<Resource.FilterOption, String[]> values = new EnumMap<>(Resource.FilterOption.class);
|
||||
values.put(Resource.FilterOption.EXACT_NAME, new String[] { "group.resource." + id });
|
||||
return resourceStore.findByResourceServer(values, null, 0, 1).isEmpty();
|
||||
});
|
||||
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
#
|
||||
# JBoss, Home of Professional Open Source.
|
||||
# Copyright 2021 Red Hat, Inc., and individual contributors
|
||||
# as indicated by the @author tags.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
org.keycloak.models.map.authorization.MapAuthorizationStoreFactory
|
|
@ -280,7 +280,7 @@ public final class AuthorizationProvider implements Provider {
|
|||
}
|
||||
|
||||
@Override
|
||||
public List<Scope> findByResourceServer(Map<String, String[]> attributes, String resourceServerId, int firstResult, int maxResult) {
|
||||
public List<Scope> findByResourceServer(Map<Scope.FilterOption, String[]> attributes, String resourceServerId, int firstResult, int maxResult) {
|
||||
return delegate.findByResourceServer(attributes, resourceServerId, firstResult, maxResult);
|
||||
}
|
||||
};
|
||||
|
@ -385,7 +385,7 @@ public final class AuthorizationProvider implements Provider {
|
|||
}
|
||||
|
||||
@Override
|
||||
public List<Policy> findByResourceServer(Map<String, String[]> attributes, String resourceServerId, int firstResult, int maxResult) {
|
||||
public List<Policy> findByResourceServer(Map<Policy.FilterOption, String[]> attributes, String resourceServerId, int firstResult, int maxResult) {
|
||||
return policyStore.findByResourceServer(attributes, resourceServerId, firstResult, maxResult);
|
||||
}
|
||||
|
||||
|
@ -506,7 +506,7 @@ public final class AuthorizationProvider implements Provider {
|
|||
}
|
||||
|
||||
@Override
|
||||
public List<Resource> findByResourceServer(Map<String, String[]> attributes, String resourceServerId, int firstResult, int maxResult) {
|
||||
public List<Resource> findByResourceServer(Map<Resource.FilterOption, String[]> attributes, String resourceServerId, int firstResult, int maxResult) {
|
||||
return delegate.findByResourceServer(attributes, resourceServerId, firstResult, maxResult);
|
||||
}
|
||||
|
||||
|
|
|
@ -16,8 +16,9 @@
|
|||
*/
|
||||
package org.keycloak.authorization;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.EnumMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.keycloak.authorization.model.PermissionTicket;
|
||||
import org.keycloak.authorization.model.Policy;
|
||||
|
@ -38,12 +39,12 @@ public class UserManagedPermissionUtil {
|
|||
Policy policy = ticket.getPolicy();
|
||||
|
||||
if (policy == null) {
|
||||
HashMap<String, String> filter = new HashMap<>();
|
||||
Map<PermissionTicket.FilterOption, String> filter = new EnumMap<>(PermissionTicket.FilterOption.class);
|
||||
|
||||
filter.put(PermissionTicket.OWNER, ticket.getOwner());
|
||||
filter.put(PermissionTicket.REQUESTER, ticket.getRequester());
|
||||
filter.put(PermissionTicket.RESOURCE, ticket.getResource().getId());
|
||||
filter.put(PermissionTicket.POLICY_IS_NOT_NULL, Boolean.TRUE.toString());
|
||||
filter.put(PermissionTicket.FilterOption.OWNER, ticket.getOwner());
|
||||
filter.put(PermissionTicket.FilterOption.REQUESTER, ticket.getRequester());
|
||||
filter.put(PermissionTicket.FilterOption.RESOURCE_ID, ticket.getResource().getId());
|
||||
filter.put(PermissionTicket.FilterOption.POLICY_IS_NOT_NULL, Boolean.TRUE.toString());
|
||||
|
||||
List<PermissionTicket> tickets = storeFactory.getPermissionTicketStore().find(filter, ticket.getResourceServer().getId(), -1, 1);
|
||||
|
||||
|
@ -72,12 +73,12 @@ public class UserManagedPermissionUtil {
|
|||
Policy policy = ticket.getPolicy();
|
||||
|
||||
if (policy != null) {
|
||||
HashMap<String, String> filter = new HashMap<>();
|
||||
Map<PermissionTicket.FilterOption, String> filter = new EnumMap<>(PermissionTicket.FilterOption.class);
|
||||
|
||||
filter.put(PermissionTicket.OWNER, ticket.getOwner());
|
||||
filter.put(PermissionTicket.REQUESTER, ticket.getRequester());
|
||||
filter.put(PermissionTicket.RESOURCE, ticket.getResource().getId());
|
||||
filter.put(PermissionTicket.GRANTED, Boolean.TRUE.toString());
|
||||
filter.put(PermissionTicket.FilterOption.OWNER, ticket.getOwner());
|
||||
filter.put(PermissionTicket.FilterOption.REQUESTER, ticket.getRequester());
|
||||
filter.put(PermissionTicket.FilterOption.RESOURCE_ID, ticket.getResource().getId());
|
||||
filter.put(PermissionTicket.FilterOption.GRANTED, Boolean.TRUE.toString());
|
||||
|
||||
List<PermissionTicket> tickets = storeFactory.getPermissionTicketStore().find(filter, ticket.getResourceServer().getId(), -1, -1);
|
||||
|
||||
|
|
|
@ -18,11 +18,14 @@ package org.keycloak.authorization.model;
|
|||
|
||||
import org.keycloak.authorization.store.StoreFactory;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public abstract class AbstractAuthorizationModel {
|
||||
|
||||
private StoreFactory storeFactory;
|
||||
protected final StoreFactory storeFactory;
|
||||
|
||||
public AbstractAuthorizationModel(StoreFactory storeFactory) {
|
||||
Objects.requireNonNull(storeFactory, "storeFactory");
|
||||
this.storeFactory = storeFactory;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,22 +16,55 @@
|
|||
*/
|
||||
package org.keycloak.authorization.model;
|
||||
|
||||
import org.keycloak.storage.SearchableModelField;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
public interface PermissionTicket {
|
||||
|
||||
String ID = "id";
|
||||
String RESOURCE = "resource.id";
|
||||
String RESOURCE_NAME = "resource.name";
|
||||
String SCOPE = "scope.id";
|
||||
String SCOPE_IS_NULL = "scope_is_null";
|
||||
String OWNER = "owner";
|
||||
String GRANTED = "granted";
|
||||
String REQUESTER = "requester";
|
||||
String REQUESTER_IS_NULL = "requester_is_null";
|
||||
String POLICY_IS_NOT_NULL = "policy_is_not_null";
|
||||
String POLICY = "policy";
|
||||
public static class SearchableFields {
|
||||
public static final SearchableModelField<PermissionTicket> ID = new SearchableModelField<>("id", String.class);
|
||||
public static final SearchableModelField<PermissionTicket> RESOURCE_ID = new SearchableModelField<>("resourceId", String.class);
|
||||
public static final SearchableModelField<PermissionTicket> RESOURCE_SERVER_ID = new SearchableModelField<>("resourceServerId", String.class);
|
||||
public static final SearchableModelField<PermissionTicket> OWNER = new SearchableModelField<>("owner", String.class);
|
||||
public static final SearchableModelField<PermissionTicket> REQUESTER = new SearchableModelField<>("requester", String.class);
|
||||
public static final SearchableModelField<PermissionTicket> SCOPE_ID = new SearchableModelField<>("scopeId", String.class);
|
||||
public static final SearchableModelField<PermissionTicket> POLICY_ID = new SearchableModelField<>("policyId", String.class);
|
||||
public static final SearchableModelField<PermissionTicket> GRANTED_TIMESTAMP = new SearchableModelField<>("grantedTimestamp", String.class);
|
||||
}
|
||||
|
||||
public static enum FilterOption {
|
||||
ID("id", SearchableFields.ID),
|
||||
RESOURCE_ID("resource.id", SearchableFields.RESOURCE_ID),
|
||||
RESOURCE_NAME("resource.name", SearchableFields.RESOURCE_ID),
|
||||
SCOPE_ID("scope.id", SearchableFields.SCOPE_ID),
|
||||
SCOPE_IS_NULL("scope_is_null", SearchableFields.SCOPE_ID),
|
||||
OWNER("owner", SearchableFields.OWNER),
|
||||
GRANTED("granted", SearchableFields.GRANTED_TIMESTAMP),
|
||||
REQUESTER("requester", SearchableFields.REQUESTER),
|
||||
REQUESTER_IS_NULL("requester_is_null", SearchableFields.REQUESTER),
|
||||
POLICY_IS_NOT_NULL("policy_is_not_null", SearchableFields.POLICY_ID),
|
||||
POLICY_ID("policy.id", SearchableFields.POLICY_ID)
|
||||
;
|
||||
|
||||
private final String name;
|
||||
private final SearchableModelField<PermissionTicket> searchableModelField;
|
||||
|
||||
FilterOption(String name, SearchableModelField<PermissionTicket> searchableModelField) {
|
||||
this.name = name;
|
||||
this.searchableModelField = searchableModelField;
|
||||
}
|
||||
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public SearchableModelField<PermissionTicket> getSearchableModelField() {
|
||||
return searchableModelField;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the unique identifier for this instance.
|
||||
|
|
|
@ -20,6 +20,7 @@ package org.keycloak.authorization.model;
|
|||
|
||||
import org.keycloak.representations.idm.authorization.DecisionStrategy;
|
||||
import org.keycloak.representations.idm.authorization.Logic;
|
||||
import org.keycloak.storage.SearchableModelField;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
@ -31,6 +32,49 @@ import java.util.Set;
|
|||
*/
|
||||
public interface Policy {
|
||||
|
||||
public static class SearchableFields {
|
||||
public static final SearchableModelField<Policy> ID = new SearchableModelField<>("id", String.class);
|
||||
public static final SearchableModelField<Policy> NAME = new SearchableModelField<>("name", String.class);
|
||||
public static final SearchableModelField<Policy> RESOURCE_SERVER_ID = new SearchableModelField<>("resourceServerId", String.class);
|
||||
public static final SearchableModelField<Policy> RESOURCE_ID = new SearchableModelField<>("resourceId", String.class);
|
||||
public static final SearchableModelField<Policy> SCOPE_ID = new SearchableModelField<>("scopeId", String.class);
|
||||
public static final SearchableModelField<Policy> TYPE = new SearchableModelField<>("type", String.class);
|
||||
public static final SearchableModelField<Policy> OWNER = new SearchableModelField<>("owner", String.class);
|
||||
public static final SearchableModelField<Policy> CONFIG = new SearchableModelField<>("config", String.class);
|
||||
public static final SearchableModelField<Policy> ASSOCIATED_POLICY_ID = new SearchableModelField<>("associatedPolicyId", String.class);
|
||||
}
|
||||
|
||||
public static enum FilterOption {
|
||||
ID("id", SearchableFields.ID),
|
||||
PERMISSION("permission", SearchableFields.TYPE),
|
||||
OWNER("owner", SearchableFields.OWNER),
|
||||
OWNER_IS_NOT_NULL("owner_is_not_null", SearchableFields.OWNER),
|
||||
RESOURCE_ID("resources.id", SearchableFields.RESOURCE_ID),
|
||||
SCOPE_ID("scopes.id", SearchableFields.SCOPE_ID),
|
||||
CONFIG("config", SearchableFields.CONFIG),
|
||||
TYPE("type", SearchableFields.TYPE),
|
||||
NAME("name", SearchableFields.NAME);
|
||||
|
||||
private final String name;
|
||||
private final SearchableModelField<Policy> searchableModelField;
|
||||
|
||||
FilterOption(String name, SearchableModelField<Policy> searchableModelField) {
|
||||
this.name = name;
|
||||
this.searchableModelField = searchableModelField;
|
||||
}
|
||||
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public SearchableModelField<Policy> getSearchableModelField() {
|
||||
return searchableModelField;
|
||||
}
|
||||
}
|
||||
|
||||
public static final String CONFIG_SEPARATOR = "##";
|
||||
|
||||
/**
|
||||
* Returns the unique identifier for this instance.
|
||||
*
|
||||
|
@ -163,6 +207,4 @@ public interface Policy {
|
|||
void addResource(Resource resource);
|
||||
|
||||
void removeResource(Resource resource);
|
||||
|
||||
boolean isFetched(String association);
|
||||
}
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
|
||||
package org.keycloak.authorization.model;
|
||||
|
||||
import org.keycloak.storage.SearchableModelField;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
@ -29,7 +31,47 @@ import java.util.Set;
|
|||
*/
|
||||
public interface Resource {
|
||||
|
||||
String EXACT_NAME = "EXACT_NAME";
|
||||
public static class SearchableFields {
|
||||
public static final SearchableModelField<Resource> ID = new SearchableModelField<>("id", String.class);
|
||||
public static final SearchableModelField<Resource> NAME = new SearchableModelField<>("name", String.class);
|
||||
public static final SearchableModelField<Resource> RESOURCE_SERVER_ID = new SearchableModelField<>("resourceServerId", String.class);
|
||||
public static final SearchableModelField<Resource> OWNER = new SearchableModelField<>("owner", String.class);
|
||||
public static final SearchableModelField<Resource> TYPE = new SearchableModelField<>("type", String.class);
|
||||
|
||||
public static final SearchableModelField<Resource> URI = new SearchableModelField<>("uris", String.class);
|
||||
public static final SearchableModelField<Resource> SCOPE_ID = new SearchableModelField<>("scope", String.class);
|
||||
public static final SearchableModelField<Resource> OWNER_MANAGED_ACCESS = new SearchableModelField<>("ownerManagedAccess", Boolean.class);
|
||||
}
|
||||
|
||||
public static enum FilterOption {
|
||||
ID("id", SearchableFields.ID),
|
||||
NAME("name", SearchableFields.NAME),
|
||||
EXACT_NAME("name", SearchableFields.NAME),
|
||||
OWNER("owner", SearchableFields.OWNER),
|
||||
TYPE("type", SearchableFields.TYPE),
|
||||
URI("uri", SearchableFields.URI),
|
||||
URI_NOT_NULL("uri_not_null", SearchableFields.URI),
|
||||
OWNER_MANAGED_ACCESS("ownerManagedAccess", SearchableFields.OWNER_MANAGED_ACCESS),
|
||||
SCOPE_ID("scopes.id", SearchableFields.SCOPE_ID);
|
||||
|
||||
private final String name;
|
||||
private final SearchableModelField<Resource> searchableModelField;
|
||||
|
||||
FilterOption(String name, SearchableModelField<Resource> searchableModelField) {
|
||||
this.name = name;
|
||||
this.searchableModelField = searchableModelField;
|
||||
}
|
||||
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public SearchableModelField<Resource> getSearchableModelField() {
|
||||
return searchableModelField;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the unique identifier for this instance.
|
||||
|
@ -182,6 +224,4 @@ public interface Resource {
|
|||
void setAttribute(String name, List<String> values);
|
||||
|
||||
void removeAttribute(String name);
|
||||
|
||||
boolean isFetched(String association);
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ package org.keycloak.authorization.model;
|
|||
|
||||
import org.keycloak.representations.idm.authorization.DecisionStrategy;
|
||||
import org.keycloak.representations.idm.authorization.PolicyEnforcementMode;
|
||||
import org.keycloak.storage.SearchableModelField;
|
||||
|
||||
/**
|
||||
* Represents a resource server, whose resources are managed and protected. A resource server is basically an existing
|
||||
|
@ -29,6 +30,10 @@ import org.keycloak.representations.idm.authorization.PolicyEnforcementMode;
|
|||
*/
|
||||
public interface ResourceServer {
|
||||
|
||||
public static class SearchableFields {
|
||||
public static final SearchableModelField<ResourceServer> ID = new SearchableModelField<>("id", String.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the unique identifier for this instance.
|
||||
*
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
|
||||
package org.keycloak.authorization.model;
|
||||
|
||||
import org.keycloak.storage.SearchableModelField;
|
||||
|
||||
/**
|
||||
* Represents a scope, which is usually associated with one or more resources in order to define the actions that can be performed
|
||||
* or a specific access context.
|
||||
|
@ -26,6 +28,34 @@ package org.keycloak.authorization.model;
|
|||
*/
|
||||
public interface Scope {
|
||||
|
||||
public static class SearchableFields {
|
||||
public static final SearchableModelField<Scope> ID = new SearchableModelField<>("id", String.class);
|
||||
public static final SearchableModelField<Scope> NAME = new SearchableModelField<>("name", String.class);
|
||||
public static final SearchableModelField<Scope> RESOURCE_SERVER_ID = new SearchableModelField<>("resourceServerId", String.class);
|
||||
}
|
||||
|
||||
public static enum FilterOption {
|
||||
ID("id", SearchableFields.ID),
|
||||
NAME("name", SearchableFields.NAME);
|
||||
|
||||
private final String name;
|
||||
private final SearchableModelField<Scope> searchableModelField;
|
||||
|
||||
FilterOption(String name, SearchableModelField<Scope> searchableModelField) {
|
||||
this.name = name;
|
||||
this.searchableModelField = searchableModelField;
|
||||
}
|
||||
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public SearchableModelField<Scope> getSearchableModelField() {
|
||||
return searchableModelField;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the unique identifier for this instance.
|
||||
*
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
*/
|
||||
package org.keycloak.authorization.policy.evaluation;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.EnumMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
@ -110,11 +110,11 @@ public class PermissionTicketAwareDecisionResultCollector extends DecisionPermis
|
|||
}
|
||||
|
||||
if (scopes.isEmpty()) {
|
||||
Map<String, String> filters = new HashMap<>();
|
||||
Map<PermissionTicket.FilterOption, String> filters = new EnumMap<>(PermissionTicket.FilterOption.class);
|
||||
|
||||
filters.put(PermissionTicket.RESOURCE, resource.getId());
|
||||
filters.put(PermissionTicket.REQUESTER, identity.getId());
|
||||
filters.put(PermissionTicket.SCOPE_IS_NULL, Boolean.TRUE.toString());
|
||||
filters.put(PermissionTicket.FilterOption.RESOURCE_ID, resource.getId());
|
||||
filters.put(PermissionTicket.FilterOption.REQUESTER, identity.getId());
|
||||
filters.put(PermissionTicket.FilterOption.SCOPE_IS_NULL, Boolean.TRUE.toString());
|
||||
|
||||
List<PermissionTicket> tickets = authorization.getStoreFactory().getPermissionTicketStore().find(filters, resource.getResourceServer(), -1, -1);
|
||||
|
||||
|
@ -131,11 +131,11 @@ public class PermissionTicketAwareDecisionResultCollector extends DecisionPermis
|
|||
scope = scopeStore.findById(scopeId, resourceServer.getId());
|
||||
}
|
||||
|
||||
Map<String, String> filters = new HashMap<>();
|
||||
Map<PermissionTicket.FilterOption, String> filters = new EnumMap<>(PermissionTicket.FilterOption.class);
|
||||
|
||||
filters.put(PermissionTicket.RESOURCE, resource.getId());
|
||||
filters.put(PermissionTicket.REQUESTER, identity.getId());
|
||||
filters.put(PermissionTicket.SCOPE, scope.getId());
|
||||
filters.put(PermissionTicket.FilterOption.RESOURCE_ID, resource.getId());
|
||||
filters.put(PermissionTicket.FilterOption.REQUESTER, identity.getId());
|
||||
filters.put(PermissionTicket.FilterOption.SCOPE_ID, scope.getId());
|
||||
|
||||
List<PermissionTicket> tickets = authorization.getStoreFactory().getPermissionTicketStore().find(filters, resource.getResourceServer(), -1, -1);
|
||||
|
||||
|
|
|
@ -32,13 +32,14 @@ import org.keycloak.authorization.model.ResourceServer;
|
|||
public interface PermissionTicketStore {
|
||||
|
||||
/**
|
||||
* Returns a list of {@link PermissionTicket}, filtered by the given attributes.
|
||||
* Returns count of {@link PermissionTicket}, filtered by the given attributes.
|
||||
*
|
||||
* @param attributes permission tickets that do not match the attributes are not included with the count.
|
||||
* @param attributes permission tickets that do not match the attributes are not included with the count; possible filter options are given by {@link PermissionTicket.FilterOption}
|
||||
* @param resourceServerId the resource server id
|
||||
* @return an integer indicating the amount of permission tickets
|
||||
* @throws IllegalArgumentException when there is an unknown attribute in the {@code attributes} map
|
||||
*/
|
||||
long count(Map<String, String> attributes, String resourceServerId);
|
||||
long count(Map<PermissionTicket.FilterOption, String> attributes, String resourceServerId);
|
||||
|
||||
/**
|
||||
* Creates a new {@link PermissionTicket} instance.
|
||||
|
@ -99,7 +100,19 @@ public interface PermissionTicketStore {
|
|||
*/
|
||||
List<PermissionTicket> findByScope(String scopeId, String resourceServerId);
|
||||
|
||||
List<PermissionTicket> find(Map<String, String> attributes, String resourceServerId, int firstResult, int maxResult);
|
||||
/**
|
||||
* Returns a list of {@link PermissionTicket}, filtered by the given attributes.
|
||||
*
|
||||
* @param attributes a map of keys and values to filter on; possible filter options are given by {@link PermissionTicket.FilterOption}
|
||||
* @param resourceServerId an id of resource server that resulting tickets should belong to. Ignored if {@code null}
|
||||
* @param firstResult first result to return; Ignored if negative or zero
|
||||
* @param maxResult maximum number of results to return; Ignored if negative
|
||||
* @return a list of filtered and paginated permissions
|
||||
*
|
||||
* @throws IllegalArgumentException when there is an unknown attribute in the {@code attributes} map
|
||||
*
|
||||
*/
|
||||
List<PermissionTicket> find(Map<PermissionTicket.FilterOption, String> attributes, String resourceServerId, int firstResult, int maxResult);
|
||||
|
||||
/**
|
||||
* Returns a list of {@link PermissionTicket} granted to the given {@code userId}.
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
package org.keycloak.authorization.store;
|
||||
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
|
@ -79,11 +80,13 @@ public interface PolicyStore {
|
|||
/**
|
||||
* Returns a list of {@link Policy} associated with a {@link ResourceServer} with the given <code>resourceServerId</code>.
|
||||
*
|
||||
* @param attributes a map holding the attributes that will be used as a filter
|
||||
* @param attributes a map holding the attributes that will be used as a filter; possible filter options are given by {@link Policy.FilterOption}
|
||||
* @param resourceServerId the identifier of a resource server
|
||||
* @return a list of policies that belong to the given resource server
|
||||
*
|
||||
* @throws IllegalArgumentException when there is an unknown attribute in the {@code attributes} map
|
||||
*/
|
||||
List<Policy> findByResourceServer(Map<String, String[]> attributes, String resourceServerId, int firstResult, int maxResult);
|
||||
List<Policy> findByResourceServer(Map<Policy.FilterOption, String[]> attributes, String resourceServerId, int firstResult, int maxResult);
|
||||
|
||||
/**
|
||||
* Returns a list of {@link Policy} associated with a {@link org.keycloak.authorization.core.model.Resource} with the given <code>resourceId</code>.
|
||||
|
@ -92,7 +95,13 @@ public interface PolicyStore {
|
|||
* @param resourceServerId the resource server id
|
||||
* @return a list of policies associated with the given resource
|
||||
*/
|
||||
List<Policy> findByResource(String resourceId, String resourceServerId);
|
||||
default List<Policy> findByResource(String resourceId, String resourceServerId) {
|
||||
List<Policy> result = new LinkedList<>();
|
||||
|
||||
findByResource(resourceId, resourceServerId, result::add);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void findByResource(String resourceId, String resourceServerId, Consumer<Policy> consumer);
|
||||
|
||||
|
@ -103,7 +112,13 @@ public interface PolicyStore {
|
|||
* @param resourceServerId the resource server id
|
||||
* @return a list of policies associated with the given resource type
|
||||
*/
|
||||
List<Policy> findByResourceType(String resourceType, String resourceServerId);
|
||||
default List<Policy> findByResourceType(String resourceType, String resourceServerId) {
|
||||
List<Policy> result = new LinkedList<>();
|
||||
|
||||
findByResourceType(resourceType, resourceServerId, result::add);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of {@link Policy} associated with a {@link org.keycloak.authorization.core.model.Scope} with the given <code>scopeIds</code>.
|
||||
|
@ -118,12 +133,23 @@ public interface PolicyStore {
|
|||
* Returns a list of {@link Policy} associated with a {@link org.keycloak.authorization.core.model.Scope} with the given <code>resourceId</code> and <code>scopeIds</code>.
|
||||
*
|
||||
* @param scopeIds the id of the scopes
|
||||
* @param resourceId the id of the resource
|
||||
* @param resourceId the id of the resource. Ignored if {@code null}.
|
||||
* @param resourceServerId the resource server id
|
||||
* @return a list of policies associated with the given scopes
|
||||
*/
|
||||
List<Policy> findByScopeIds(List<String> scopeIds, String resourceId, String resourceServerId);
|
||||
default List<Policy> findByScopeIds(List<String> scopeIds, String resourceId, String resourceServerId) {
|
||||
List<Policy> result = new LinkedList<>();
|
||||
|
||||
findByScopeIds(scopeIds, resourceId, resourceServerId, result::add);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Effectively the same method as {@link #findByScopeIds(List, String, String)}, however in the end
|
||||
* the {@code consumer} is fed with the result.
|
||||
*
|
||||
*/
|
||||
void findByScopeIds(List<String> scopeIds, String resourceId, String resourceServerId, Consumer<Policy> consumer);
|
||||
|
||||
/**
|
||||
|
@ -144,5 +170,5 @@ public interface PolicyStore {
|
|||
*/
|
||||
List<Policy> findDependentPolicies(String id, String resourceServerId);
|
||||
|
||||
void findByResourceType(String type, String id, Consumer<Policy> policyConsumer);
|
||||
void findByResourceType(String type, String resourceServerId, Consumer<Policy> policyConsumer);
|
||||
}
|
||||
|
|
|
@ -20,6 +20,8 @@ package org.keycloak.authorization.store;
|
|||
import org.keycloak.authorization.model.Resource;
|
||||
import org.keycloak.authorization.model.ResourceServer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
|
@ -39,12 +41,14 @@ public interface ResourceStore {
|
|||
* @param owner the owner of this resource or null if the resource server is the owner
|
||||
* @return an instance backed by the underlying storage implementation
|
||||
*/
|
||||
Resource create(String name, ResourceServer resourceServer, String owner);
|
||||
default Resource create(String name, ResourceServer resourceServer, String owner) {
|
||||
return create(null, name, resourceServer, owner);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Creates a {@link Resource} instance backed by this persistent storage implementation.
|
||||
*
|
||||
* @param id the id of this resource. It must be unique.
|
||||
* @param id the id of this resource. It must be unique. Will be randomly generated if null.
|
||||
* @param name the name of this resource. It must be unique.
|
||||
* @param resourceServer the resource server to where the given resource belongs to
|
||||
* @param owner the owner of this resource or null if the resource server is the owner
|
||||
|
@ -73,7 +77,13 @@ public interface ResourceStore {
|
|||
* @param ownerId the identifier of the owner
|
||||
* @return a list with all resource instances owned by the given owner
|
||||
*/
|
||||
List<Resource> findByOwner(String ownerId, String resourceServerId);
|
||||
default List<Resource> findByOwner(String ownerId, String resourceServerId) {
|
||||
List<Resource> list = new LinkedList<>();
|
||||
|
||||
findByOwner(ownerId, resourceServerId, list::add);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
void findByOwner(String ownerId, String resourceServerId, Consumer<Resource> consumer);
|
||||
|
||||
|
@ -98,11 +108,13 @@ public interface ResourceStore {
|
|||
/**
|
||||
* Finds all {@link Resource} instances associated with a given resource server.
|
||||
*
|
||||
* @param attributes a map holding the attributes that will be used as a filter
|
||||
* @param attributes a map holding the attributes that will be used as a filter; possible filter options are given by {@link Resource.FilterOption}
|
||||
* @param resourceServerId the identifier of the resource server
|
||||
* @return a list with all resources associated with the given resource server
|
||||
*
|
||||
* @throws IllegalArgumentException when there is an unknown attribute in the {@code attributes} map
|
||||
*/
|
||||
List<Resource> findByResourceServer(Map<String, String[]> attributes, String resourceServerId, int firstResult, int maxResult);
|
||||
List<Resource> findByResourceServer(Map<Resource.FilterOption, String[]> attributes, String resourceServerId, int firstResult, int maxResult);
|
||||
|
||||
/**
|
||||
* Finds all {@link Resource} associated with a given scope.
|
||||
|
@ -110,7 +122,13 @@ public interface ResourceStore {
|
|||
* @param id one or more scope identifiers
|
||||
* @return a list of resources associated with the given scope(s)
|
||||
*/
|
||||
List<Resource> findByScope(List<String> id, String resourceServerId);
|
||||
default List<Resource> findByScope(List<String> id, String resourceServerId) {
|
||||
List<Resource> result = new ArrayList<>();
|
||||
|
||||
findByScope(id, resourceServerId, result::add);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void findByScope(List<String> scopes, String resourceServerId, Consumer<Resource> consumer);
|
||||
|
||||
|
@ -139,7 +157,13 @@ public interface ResourceStore {
|
|||
* @param type the type of the resource
|
||||
* @return a list of resources with the given type
|
||||
*/
|
||||
List<Resource> findByType(String type, String resourceServerId);
|
||||
default List<Resource> findByType(String type, String resourceServerId) {
|
||||
List<Resource> list = new LinkedList<>();
|
||||
|
||||
findByType(type, resourceServerId, list::add);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds all {@link Resource} with the given type.
|
||||
|
@ -148,7 +172,13 @@ public interface ResourceStore {
|
|||
* @param owner the resource owner or null for any resource with a given type
|
||||
* @return a list of resources with the given type
|
||||
*/
|
||||
List<Resource> findByType(String type, String owner, String resourceServerId);
|
||||
default List<Resource> findByType(String type, String owner, String resourceServerId) {
|
||||
List<Resource> list = new LinkedList<>();
|
||||
|
||||
findByType(type, owner, resourceServerId, list::add);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds all {@link Resource} with the given type.
|
||||
|
@ -171,7 +201,13 @@ public interface ResourceStore {
|
|||
*/
|
||||
void findByType(String type, String owner, String resourceServerId, Consumer<Resource> consumer);
|
||||
|
||||
List<Resource> findByTypeInstance(String type, String resourceServerId);
|
||||
default List<Resource> findByTypeInstance(String type, String resourceServerId) {
|
||||
List<Resource> list = new LinkedList<>();
|
||||
|
||||
findByTypeInstance(type, resourceServerId, list::add);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
void findByTypeInstance(String type, String resourceServerId, Consumer<Resource> consumer);
|
||||
}
|
||||
|
|
|
@ -40,13 +40,15 @@ public interface ScopeStore {
|
|||
*
|
||||
* @return a new instance of {@link Scope}
|
||||
*/
|
||||
Scope create(String name, ResourceServer resourceServer);
|
||||
default Scope create(String name, ResourceServer resourceServer) {
|
||||
return create(null, name, resourceServer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link Scope} instance. The new instance is not necessarily persisted though, which may require
|
||||
* a call to the {#save} method to actually make it persistent.
|
||||
*
|
||||
* @param id the id of the scope
|
||||
* @param id the id of the scope. Is generated randomly when null
|
||||
* @param name the name of the scope
|
||||
* @param resourceServer the resource server to which this scope belongs
|
||||
*
|
||||
|
@ -92,10 +94,12 @@ public interface ScopeStore {
|
|||
/**
|
||||
* Returns a list of {@link Scope} associated with a {@link ResourceServer} with the given <code>resourceServerId</code>.
|
||||
*
|
||||
* @param attributes a map holding the attributes that will be used as a filter
|
||||
* @param attributes a map holding the attributes that will be used as a filter; possible filter options are given by {@link Scope.FilterOption}
|
||||
* @param resourceServerId the identifier of a resource server
|
||||
*
|
||||
* @return a list of scopes that belong to the given resource server
|
||||
*
|
||||
* @throws IllegalArgumentException when there is an unknown attribute in the {@code attributes} map
|
||||
*
|
||||
*/
|
||||
List<Scope> findByResourceServer(Map<String, String[]> attributes, String resourceServerId, int firstResult, int maxResult);
|
||||
List<Scope> findByResourceServer(Map<Scope.FilterOption, String[]> attributes, String resourceServerId, int firstResult, int maxResult);
|
||||
}
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
package org.keycloak.authorization.store.syncronization;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.EnumMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
@ -56,10 +56,10 @@ public class ClientApplicationSynchronizer implements Synchronizer<ClientRemoved
|
|||
storeFactory.getResourceServerStore().delete(resourceServer.getId());
|
||||
}
|
||||
|
||||
Map<String, String[]> attributes = new HashMap<>();
|
||||
Map<Policy.FilterOption, String[]> attributes = new EnumMap<>(Policy.FilterOption.class);
|
||||
|
||||
attributes.put("type", new String[] {"client"});
|
||||
attributes.put("config:clients", new String[] {event.getClient().getId()});
|
||||
attributes.put(Policy.FilterOption.TYPE, new String[] {"client"});
|
||||
attributes.put(Policy.FilterOption.CONFIG, new String[] {"clients", event.getClient().getId()});
|
||||
|
||||
List<Policy> search = storeFactory.getPolicyStore().findByResourceServer(attributes, null, -1, -1);
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
package org.keycloak.authorization.store.syncronization;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.EnumMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
@ -45,10 +45,10 @@ public class GroupSynchronizer implements Synchronizer<GroupModel.GroupRemovedEv
|
|||
StoreFactory storeFactory = authorizationProvider.getStoreFactory();
|
||||
PolicyStore policyStore = storeFactory.getPolicyStore();
|
||||
GroupModel group = event.getGroup();
|
||||
Map<String, String[]> attributes = new HashMap<>();
|
||||
Map<Policy.FilterOption, String[]> attributes = new EnumMap<>(Policy.FilterOption.class);
|
||||
|
||||
attributes.put("type", new String[] {"group"});
|
||||
attributes.put("config:groups", new String[] {group.getId()});
|
||||
attributes.put(Policy.FilterOption.TYPE, new String[] {"group"});
|
||||
attributes.put(Policy.FilterOption.CONFIG, new String[] {"groups", group.getId()});
|
||||
|
||||
List<Policy> search = policyStore.findByResourceServer(attributes, null, -1, -1);
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
package org.keycloak.authorization.store.syncronization;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.EnumMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
@ -58,10 +58,10 @@ public class UserSynchronizer implements Synchronizer<UserRemovedEvent> {
|
|||
StoreFactory storeFactory = authorizationProvider.getStoreFactory();
|
||||
PolicyStore policyStore = storeFactory.getPolicyStore();
|
||||
UserModel userModel = event.getUser();
|
||||
Map<String, String[]> attributes = new HashMap<>();
|
||||
Map<Policy.FilterOption, String[]> attributes = new EnumMap<>(Policy.FilterOption.class);
|
||||
|
||||
attributes.put("type", new String[] {"user"});
|
||||
attributes.put("config:users", new String[] {userModel.getId()});
|
||||
attributes.put(Policy.FilterOption.TYPE, new String[] {"user"});
|
||||
attributes.put(Policy.FilterOption.CONFIG, new String[] {"users", userModel.getId()});
|
||||
|
||||
List<Policy> search = policyStore.findByResourceServer(attributes, null, -1, -1);
|
||||
|
||||
|
@ -112,17 +112,17 @@ public class UserSynchronizer implements Synchronizer<UserRemovedEvent> {
|
|||
StoreFactory storeFactory = authorizationProvider.getStoreFactory();
|
||||
PermissionTicketStore ticketStore = storeFactory.getPermissionTicketStore();
|
||||
UserModel userModel = event.getUser();
|
||||
Map<String, String> attributes = new HashMap<>();
|
||||
Map<PermissionTicket.FilterOption, String> attributes = new EnumMap<>(PermissionTicket.FilterOption.class);
|
||||
|
||||
attributes.put(PermissionTicket.OWNER, userModel.getId());
|
||||
attributes.put(PermissionTicket.FilterOption.OWNER, userModel.getId());
|
||||
|
||||
for (PermissionTicket ticket : ticketStore.find(attributes, null, -1, -1)) {
|
||||
ticketStore.delete(ticket.getId());
|
||||
}
|
||||
|
||||
attributes = new HashMap<>();
|
||||
attributes.clear();
|
||||
|
||||
attributes.put(PermissionTicket.REQUESTER, userModel.getId());
|
||||
attributes.put(PermissionTicket.FilterOption.REQUESTER, userModel.getId());
|
||||
|
||||
for (PermissionTicket ticket : ticketStore.find(attributes, null, -1, -1)) {
|
||||
ticketStore.delete(ticket.getId());
|
||||
|
|
|
@ -17,8 +17,12 @@
|
|||
|
||||
package org.keycloak.utils;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
import java.util.Spliterators;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
|
@ -72,4 +76,15 @@ public class StreamsUtil {
|
|||
|
||||
return originalStream;
|
||||
}
|
||||
|
||||
/**
|
||||
* distinctByKey is not supposed to be used with parallel streams
|
||||
*
|
||||
* To make this method synchronized use {@code ConcurrentHashMap<Object, Boolean>} instead of HashSet
|
||||
*
|
||||
*/
|
||||
public static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
|
||||
Set<Object> seen = new HashSet<>();
|
||||
return t -> seen.add(keyExtractor.apply(t));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,17 +45,17 @@ public class PermissionService extends PolicyService {
|
|||
protected PolicyTypeService doCreatePolicyTypeResource(String type) {
|
||||
return new PolicyTypeService(type, resourceServer, authorization, auth, adminEvent) {
|
||||
@Override
|
||||
protected List<Object> doSearch(Integer firstResult, Integer maxResult, String fields, Map<String, String[]> filters) {
|
||||
filters.put("permission", new String[] {Boolean.TRUE.toString()});
|
||||
filters.put("type", new String[] {type});
|
||||
protected List<Object> doSearch(Integer firstResult, Integer maxResult, String fields, Map<Policy.FilterOption, String[]> filters) {
|
||||
filters.put(Policy.FilterOption.PERMISSION, new String[] {Boolean.TRUE.toString()});
|
||||
filters.put(Policy.FilterOption.TYPE, new String[] {type});
|
||||
return super.doSearch(firstResult, maxResult, fields, filters);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Object> doSearch(Integer firstResult, Integer maxResult, String fields, Map<String, String[]> filters) {
|
||||
filters.put("permission", new String[] {Boolean.TRUE.toString()});
|
||||
protected List<Object> doSearch(Integer firstResult, Integer maxResult, String fields, Map<Policy.FilterOption, String[]> filters) {
|
||||
filters.put(Policy.FilterOption.PERMISSION, new String[] {Boolean.TRUE.toString()});
|
||||
return super.doSearch(firstResult, maxResult, fields, filters);
|
||||
}
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
package org.keycloak.authorization.admin;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.EnumMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
@ -184,22 +184,22 @@ public class PolicyService {
|
|||
this.auth.realm().requireViewAuthorization();
|
||||
}
|
||||
|
||||
Map<String, String[]> search = new HashMap<>();
|
||||
Map<Policy.FilterOption, String[]> search = new EnumMap<>(Policy.FilterOption.class);
|
||||
|
||||
if (id != null && !"".equals(id.trim())) {
|
||||
search.put("id", new String[] {id});
|
||||
search.put(Policy.FilterOption.ID, new String[] {id});
|
||||
}
|
||||
|
||||
if (name != null && !"".equals(name.trim())) {
|
||||
search.put("name", new String[] {name});
|
||||
search.put(Policy.FilterOption.NAME, new String[] {name});
|
||||
}
|
||||
|
||||
if (type != null && !"".equals(type.trim())) {
|
||||
search.put("type", new String[] {type});
|
||||
search.put(Policy.FilterOption.TYPE, new String[] {type});
|
||||
}
|
||||
|
||||
if (owner != null && !"".equals(owner.trim())) {
|
||||
search.put("owner", new String[] {owner});
|
||||
search.put(Policy.FilterOption.OWNER, new String[] {owner});
|
||||
}
|
||||
|
||||
StoreFactory storeFactory = authorization.getStoreFactory();
|
||||
|
@ -209,12 +209,12 @@ public class PolicyService {
|
|||
Resource resourceModel = resourceStore.findById(resource, resourceServer.getId());
|
||||
|
||||
if (resourceModel == null) {
|
||||
Map<String, String[]> resourceFilters = new HashMap<>();
|
||||
Map<Resource.FilterOption, String[]> resourceFilters = new EnumMap<>(Resource.FilterOption.class);
|
||||
|
||||
resourceFilters.put("name", new String[]{resource});
|
||||
resourceFilters.put(Resource.FilterOption.NAME, new String[]{resource});
|
||||
|
||||
if (owner != null) {
|
||||
resourceFilters.put("owner", new String[]{owner});
|
||||
resourceFilters.put(Resource.FilterOption.OWNER, new String[]{owner});
|
||||
}
|
||||
|
||||
Set<String> resources = resourceStore.findByResourceServer(resourceFilters, resourceServer.getId(), -1, 1).stream().map(Resource::getId).collect(Collectors.toSet());
|
||||
|
@ -223,9 +223,9 @@ public class PolicyService {
|
|||
return Response.noContent().build();
|
||||
}
|
||||
|
||||
search.put("resource", resources.toArray(new String[resources.size()]));
|
||||
search.put(Policy.FilterOption.RESOURCE_ID, resources.toArray(new String[resources.size()]));
|
||||
} else {
|
||||
search.put("resource", new String[] {resourceModel.getId()});
|
||||
search.put(Policy.FilterOption.RESOURCE_ID, new String[] {resourceModel.getId()});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -234,9 +234,9 @@ public class PolicyService {
|
|||
Scope scopeModel = scopeStore.findById(scope, resourceServer.getId());
|
||||
|
||||
if (scopeModel == null) {
|
||||
Map<String, String[]> scopeFilters = new HashMap<>();
|
||||
Map<Scope.FilterOption, String[]> scopeFilters = new EnumMap<>(Scope.FilterOption.class);
|
||||
|
||||
scopeFilters.put("name", new String[]{scope});
|
||||
scopeFilters.put(Scope.FilterOption.NAME, new String[]{scope});
|
||||
|
||||
Set<String> scopes = scopeStore.findByResourceServer(scopeFilters, resourceServer.getId(), -1, 1).stream().map(Scope::getId).collect(Collectors.toSet());
|
||||
|
||||
|
@ -244,14 +244,14 @@ public class PolicyService {
|
|||
return Response.noContent().build();
|
||||
}
|
||||
|
||||
search.put("scope", scopes.toArray(new String[scopes.size()]));
|
||||
search.put(Policy.FilterOption.SCOPE_ID, scopes.toArray(new String[scopes.size()]));
|
||||
} else {
|
||||
search.put("scope", new String[] {scopeModel.getId()});
|
||||
search.put(Policy.FilterOption.SCOPE_ID, new String[] {scopeModel.getId()});
|
||||
}
|
||||
}
|
||||
|
||||
if (permission != null) {
|
||||
search.put("permission", new String[] {permission.toString()});
|
||||
search.put(Policy.FilterOption.PERMISSION, new String[] {permission.toString()});
|
||||
}
|
||||
|
||||
return Response.ok(
|
||||
|
@ -263,7 +263,7 @@ public class PolicyService {
|
|||
return ModelToRepresentation.toRepresentation(model, authorization, true, false, fields != null && fields.equals("*"));
|
||||
}
|
||||
|
||||
protected List<Object> doSearch(Integer firstResult, Integer maxResult, String fields, Map<String, String[]> filters) {
|
||||
protected List<Object> doSearch(Integer firstResult, Integer maxResult, String fields, Map<Policy.FilterOption, String[]> filters) {
|
||||
PolicyStore policyStore = authorization.getStoreFactory().getPolicyStore();
|
||||
return policyStore.findByResourceServer(filters, resourceServer.getId(), firstResult != null ? firstResult : -1, maxResult != null ? maxResult : Constants.DEFAULT_MAX_RESULTS).stream()
|
||||
.map(policy -> toRepresentation(policy, fields, authorization))
|
||||
|
|
|
@ -92,8 +92,8 @@ public class PolicyTypeService extends PolicyService {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected List<Object> doSearch(Integer firstResult, Integer maxResult, String fields, Map<String, String[]> filters) {
|
||||
filters.put("type", new String[] {type});
|
||||
protected List<Object> doSearch(Integer firstResult, Integer maxResult, String fields, Map<Policy.FilterOption, String[]> filters) {
|
||||
filters.put(Policy.FilterOption.TYPE, new String[] {type});
|
||||
return super.doSearch(firstResult, maxResult, fields, filters);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ import static org.keycloak.models.utils.RepresentationToModel.toModel;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumMap;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
@ -256,10 +257,10 @@ public class ResourceSetService {
|
|||
if (model.getType() != null) {
|
||||
policies.addAll(policyStore.findByResourceType(model.getType(), resourceServer.getId()));
|
||||
|
||||
HashMap<String, String[]> resourceFilter = new HashMap<>();
|
||||
Map<Resource.FilterOption, String[]> resourceFilter = new EnumMap<>(Resource.FilterOption.class);
|
||||
|
||||
resourceFilter.put("owner", new String[]{resourceServer.getId()});
|
||||
resourceFilter.put("type", new String[]{model.getType()});
|
||||
resourceFilter.put(Resource.FilterOption.OWNER, new String[]{resourceServer.getId()});
|
||||
resourceFilter.put(Resource.FilterOption.TYPE, new String[]{model.getType()});
|
||||
|
||||
for (Resource resourceType : resourceStore.findByResourceServer(resourceFilter, resourceServer.getId(), -1, -1)) {
|
||||
policies.addAll(policyStore.findByResource(resourceType.getId(), resourceServer.getId()));
|
||||
|
@ -362,22 +363,18 @@ public class ResourceSetService {
|
|||
deep = true;
|
||||
}
|
||||
|
||||
Map<String, String[]> search = new HashMap<>();
|
||||
Map<Resource.FilterOption, String[]> search = new EnumMap<>(Resource.FilterOption.class);
|
||||
|
||||
if (id != null && !"".equals(id.trim())) {
|
||||
search.put("id", new String[] {id});
|
||||
search.put(Resource.FilterOption.ID, new String[] {id});
|
||||
}
|
||||
|
||||
if (name != null && !"".equals(name.trim())) {
|
||||
search.put("name", new String[] {name});
|
||||
|
||||
if (exactName != null && exactName) {
|
||||
search.put(Resource.EXACT_NAME, new String[] {Boolean.TRUE.toString()});
|
||||
}
|
||||
search.put(exactName != null && exactName ? Resource.FilterOption.EXACT_NAME : Resource.FilterOption.NAME, new String[] {name});
|
||||
}
|
||||
|
||||
if (uri != null && !"".equals(uri.trim())) {
|
||||
search.put("uri", new String[] {uri});
|
||||
search.put(Resource.FilterOption.URI, new String[] {uri});
|
||||
}
|
||||
|
||||
if (owner != null && !"".equals(owner.trim())) {
|
||||
|
@ -394,17 +391,17 @@ public class ResourceSetService {
|
|||
}
|
||||
}
|
||||
|
||||
search.put("owner", new String[] {owner});
|
||||
search.put(Resource.FilterOption.OWNER, new String[] {owner});
|
||||
}
|
||||
|
||||
if (type != null && !"".equals(type.trim())) {
|
||||
search.put("type", new String[] {type});
|
||||
search.put(Resource.FilterOption.TYPE, new String[] {type});
|
||||
}
|
||||
|
||||
if (scope != null && !"".equals(scope.trim())) {
|
||||
HashMap<String, String[]> scopeFilter = new HashMap<>();
|
||||
Map<Scope.FilterOption, String[]> scopeFilter = new EnumMap<>(Scope.FilterOption.class);
|
||||
|
||||
scopeFilter.put("name", new String[] {scope});
|
||||
scopeFilter.put(Scope.FilterOption.NAME, new String[] {scope});
|
||||
|
||||
List<Scope> scopes = authorization.getStoreFactory().getScopeStore().findByResourceServer(scopeFilter, resourceServer.getId(), -1, -1);
|
||||
|
||||
|
@ -412,16 +409,16 @@ public class ResourceSetService {
|
|||
return Response.ok(Collections.emptyList()).build();
|
||||
}
|
||||
|
||||
search.put("scope", scopes.stream().map(Scope::getId).toArray(String[]::new));
|
||||
search.put(Resource.FilterOption.SCOPE_ID, scopes.stream().map(Scope::getId).toArray(String[]::new));
|
||||
}
|
||||
|
||||
List<Resource> resources = storeFactory.getResourceStore().findByResourceServer(search, this.resourceServer.getId(), firstResult != null ? firstResult : -1, maxResult != null ? maxResult : Constants.DEFAULT_MAX_RESULTS);
|
||||
|
||||
if (matchingUri != null && matchingUri && resources.isEmpty()) {
|
||||
HashMap<String, String[]> attributes = new HashMap<>();
|
||||
Map<Resource.FilterOption, String[]> attributes = new EnumMap<>(Resource.FilterOption.class);
|
||||
|
||||
attributes.put("uri_not_null", new String[] {"true"});
|
||||
attributes.put("owner", new String[] {resourceServer.getId()});
|
||||
attributes.put(Resource.FilterOption.URI_NOT_NULL, new String[] {"true"});
|
||||
attributes.put(Resource.FilterOption.OWNER, new String[] {resourceServer.getId()});
|
||||
|
||||
List<Resource> serverResources = storeFactory.getResourceStore().findByResourceServer(attributes, this.resourceServer.getId(), firstResult != null ? firstResult : -1, maxResult != null ? maxResult : -1);
|
||||
|
||||
|
|
|
@ -50,7 +50,7 @@ import javax.ws.rs.core.Response;
|
|||
import javax.ws.rs.core.Response.Status;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.EnumMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
@ -242,14 +242,14 @@ public class ScopeService {
|
|||
@QueryParam("max") Integer maxResult) {
|
||||
this.auth.realm().requireViewAuthorization();
|
||||
|
||||
Map<String, String[]> search = new HashMap<>();
|
||||
Map<Scope.FilterOption, String[]> search = new EnumMap<>(Scope.FilterOption.class);
|
||||
|
||||
if (id != null && !"".equals(id.trim())) {
|
||||
search.put("id", new String[] {id});
|
||||
search.put(Scope.FilterOption.ID, new String[] {id});
|
||||
}
|
||||
|
||||
if (name != null && !"".equals(name.trim())) {
|
||||
search.put("name", new String[] {name});
|
||||
search.put(Scope.FilterOption.NAME, new String[] {name});
|
||||
}
|
||||
|
||||
return Response.ok(
|
||||
|
|
|
@ -41,6 +41,7 @@ import java.util.ArrayList;
|
|||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.EnumMap;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
@ -189,9 +190,9 @@ public class PolicyEvaluationResponseBuilder {
|
|||
representation.setDescription(policy.getDescription());
|
||||
|
||||
if ("uma".equals(representation.getType())) {
|
||||
Map<String, String> filters = new HashMap<>();
|
||||
Map<PermissionTicket.FilterOption, String> filters = new EnumMap<>(PermissionTicket.FilterOption.class);
|
||||
|
||||
filters.put(PermissionTicket.POLICY, policy.getId());
|
||||
filters.put(PermissionTicket.FilterOption.POLICY_ID, policy.getId());
|
||||
|
||||
List<PermissionTicket> tickets = authorization.getStoreFactory().getPermissionTicketStore().find(filters, policy.getResourceServer().getId(), -1, 1);
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@ import javax.ws.rs.PathParam;
|
|||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.util.HashMap;
|
||||
import java.util.EnumMap;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
@ -116,10 +116,10 @@ public class PermissionTicketService {
|
|||
if (!match)
|
||||
throw new ErrorResponseException("invalid_resource_id", "Resource set with id [" + representation.getResource() + "] does not have Scope [" + scope.getName() + "]", Response.Status.BAD_REQUEST);
|
||||
|
||||
Map<String, String> attributes = new HashMap<>();
|
||||
attributes.put(PermissionTicket.RESOURCE, resource.getId());
|
||||
attributes.put(PermissionTicket.SCOPE, scope.getId());
|
||||
attributes.put(PermissionTicket.REQUESTER, user.getId());
|
||||
Map<PermissionTicket.FilterOption, String> attributes = new EnumMap<>(PermissionTicket.FilterOption.class);
|
||||
attributes.put(PermissionTicket.FilterOption.RESOURCE_ID, resource.getId());
|
||||
attributes.put(PermissionTicket.FilterOption.SCOPE_ID, scope.getId());
|
||||
attributes.put(PermissionTicket.FilterOption.REQUESTER, user.getId());
|
||||
|
||||
if (!ticketStore.find(attributes, resourceServer.getId(), -1, -1).isEmpty())
|
||||
throw new ErrorResponseException("invalid_permission", "Permission already exists", Response.Status.BAD_REQUEST);
|
||||
|
@ -190,7 +190,7 @@ public class PermissionTicketService {
|
|||
StoreFactory storeFactory = authorization.getStoreFactory();
|
||||
PermissionTicketStore permissionTicketStore = storeFactory.getPermissionTicketStore();
|
||||
|
||||
Map<String, String> filters = getFilters(storeFactory, resourceId, scopeId, owner, requester, granted);
|
||||
Map<PermissionTicket.FilterOption, String> filters = getFilters(storeFactory, resourceId, scopeId, owner, requester, granted);
|
||||
|
||||
return Response.ok().entity(permissionTicketStore.find(filters, resourceServer.getId(), firstResult != null ? firstResult : -1, maxResult != null ? maxResult : Constants.DEFAULT_MAX_RESULTS)
|
||||
.stream()
|
||||
|
@ -210,22 +210,22 @@ public class PermissionTicketService {
|
|||
@QueryParam("returnNames") Boolean returnNames) {
|
||||
StoreFactory storeFactory = authorization.getStoreFactory();
|
||||
PermissionTicketStore permissionTicketStore = storeFactory.getPermissionTicketStore();
|
||||
Map<String, String> filters = getFilters(storeFactory, resourceId, scopeId, owner, requester, granted);
|
||||
Map<PermissionTicket.FilterOption, String> filters = getFilters(storeFactory, resourceId, scopeId, owner, requester, granted);
|
||||
long count = permissionTicketStore.count(filters, resourceServer.getId());
|
||||
|
||||
return Response.ok().entity(count).build();
|
||||
}
|
||||
|
||||
private Map<String, String> getFilters(StoreFactory storeFactory,
|
||||
private Map<PermissionTicket.FilterOption, String> getFilters(StoreFactory storeFactory,
|
||||
String resourceId,
|
||||
String scopeId,
|
||||
String owner,
|
||||
String requester,
|
||||
Boolean granted) {
|
||||
Map<String, String> filters = new HashMap<>();
|
||||
Map<PermissionTicket.FilterOption, String> filters = new EnumMap<>(PermissionTicket.FilterOption.class);
|
||||
|
||||
if (resourceId != null) {
|
||||
filters.put(PermissionTicket.RESOURCE, resourceId);
|
||||
filters.put(PermissionTicket.FilterOption.RESOURCE_ID, resourceId);
|
||||
}
|
||||
|
||||
if (scopeId != null) {
|
||||
|
@ -236,19 +236,19 @@ public class PermissionTicketService {
|
|||
scope = scopeStore.findByName(scopeId, resourceServer.getId());
|
||||
}
|
||||
|
||||
filters.put(PermissionTicket.SCOPE, scope != null ? scope.getId() : scopeId);
|
||||
filters.put(PermissionTicket.FilterOption.SCOPE_ID, scope != null ? scope.getId() : scopeId);
|
||||
}
|
||||
|
||||
if (owner != null) {
|
||||
filters.put(PermissionTicket.OWNER, getUserId(owner));
|
||||
filters.put(PermissionTicket.FilterOption.OWNER, getUserId(owner));
|
||||
}
|
||||
|
||||
if (requester != null) {
|
||||
filters.put(PermissionTicket.REQUESTER, getUserId(requester));
|
||||
filters.put(PermissionTicket.FilterOption.REQUESTER, getUserId(requester));
|
||||
}
|
||||
|
||||
if (granted != null) {
|
||||
filters.put(PermissionTicket.GRANTED, granted.toString());
|
||||
filters.put(PermissionTicket.FilterOption.GRANTED, granted.toString());
|
||||
}
|
||||
|
||||
return filters;
|
||||
|
|
|
@ -21,6 +21,7 @@ import java.util.ArrayList;
|
|||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.EnumMap;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
@ -77,10 +78,10 @@ public class AuthorizationBean {
|
|||
|
||||
public Collection<ResourceBean> getResourcesWaitingOthersApproval() {
|
||||
if (resourcesWaitingOthersApproval == null) {
|
||||
HashMap<String, String> filters = new HashMap<>();
|
||||
Map<PermissionTicket.FilterOption, String> filters = new EnumMap<>(PermissionTicket.FilterOption.class);
|
||||
|
||||
filters.put(PermissionTicket.REQUESTER, user.getId());
|
||||
filters.put(PermissionTicket.GRANTED, Boolean.FALSE.toString());
|
||||
filters.put(PermissionTicket.FilterOption.REQUESTER, user.getId());
|
||||
filters.put(PermissionTicket.FilterOption.GRANTED, Boolean.FALSE.toString());
|
||||
|
||||
resourcesWaitingOthersApproval = toResourceRepresentation(findPermissions(filters));
|
||||
}
|
||||
|
@ -90,10 +91,10 @@ public class AuthorizationBean {
|
|||
|
||||
public Collection<ResourceBean> getResourcesWaitingApproval() {
|
||||
if (requestsWaitingPermission == null) {
|
||||
HashMap<String, String> filters = new HashMap<>();
|
||||
Map<PermissionTicket.FilterOption, String> filters = new EnumMap<>(PermissionTicket.FilterOption.class);
|
||||
|
||||
filters.put(PermissionTicket.OWNER, user.getId());
|
||||
filters.put(PermissionTicket.GRANTED, Boolean.FALSE.toString());
|
||||
filters.put(PermissionTicket.FilterOption.OWNER, user.getId());
|
||||
filters.put(PermissionTicket.FilterOption.GRANTED, Boolean.FALSE.toString());
|
||||
|
||||
requestsWaitingPermission = toResourceRepresentation(findPermissions(filters));
|
||||
}
|
||||
|
@ -113,10 +114,10 @@ public class AuthorizationBean {
|
|||
|
||||
public Collection<ResourceBean> getSharedResources() {
|
||||
if (userSharedResources == null) {
|
||||
HashMap<String, String> filters = new HashMap<>();
|
||||
Map<PermissionTicket.FilterOption, String> filters = new EnumMap<>(PermissionTicket.FilterOption.class);
|
||||
|
||||
filters.put(PermissionTicket.REQUESTER, user.getId());
|
||||
filters.put(PermissionTicket.GRANTED, Boolean.TRUE.toString());
|
||||
filters.put(PermissionTicket.FilterOption.REQUESTER, user.getId());
|
||||
filters.put(PermissionTicket.FilterOption.GRANTED, Boolean.TRUE.toString());
|
||||
|
||||
PermissionTicketStore ticketStore = authorization.getStoreFactory().getPermissionTicketStore();
|
||||
|
||||
|
@ -281,10 +282,10 @@ public class AuthorizationBean {
|
|||
|
||||
public Collection<RequesterBean> getShares() {
|
||||
if (shares == null) {
|
||||
Map<String, String> filters = new HashMap<>();
|
||||
Map<PermissionTicket.FilterOption, String> filters = new EnumMap<>(PermissionTicket.FilterOption.class);
|
||||
|
||||
filters.put(PermissionTicket.RESOURCE, this.resource.getId());
|
||||
filters.put(PermissionTicket.GRANTED, Boolean.TRUE.toString());
|
||||
filters.put(PermissionTicket.FilterOption.RESOURCE_ID, this.resource.getId());
|
||||
filters.put(PermissionTicket.FilterOption.GRANTED, Boolean.TRUE.toString());
|
||||
|
||||
shares = toPermissionRepresentation(findPermissions(filters));
|
||||
}
|
||||
|
@ -293,14 +294,14 @@ public class AuthorizationBean {
|
|||
}
|
||||
|
||||
public Collection<ManagedPermissionBean> getPolicies() {
|
||||
Map<String, String[]> filters = new HashMap<>();
|
||||
Map<Policy.FilterOption, String[]> filters = new EnumMap<>(Policy.FilterOption.class);
|
||||
|
||||
filters.put("type", new String[] {"uma"});
|
||||
filters.put("resource", new String[] {this.resource.getId()});
|
||||
filters.put(Policy.FilterOption.TYPE, new String[] {"uma"});
|
||||
filters.put(Policy.FilterOption.RESOURCE_ID, new String[] {this.resource.getId()});
|
||||
if (getUserOwner() != null) {
|
||||
filters.put("owner", new String[] {getUserOwner().getId()});
|
||||
filters.put(Policy.FilterOption.OWNER, new String[] {getUserOwner().getId()});
|
||||
} else {
|
||||
filters.put("owner", new String[] {getClientOwner().getId()});
|
||||
filters.put(Policy.FilterOption.OWNER, new String[] {getClientOwner().getId()});
|
||||
}
|
||||
|
||||
List<Policy> policies = authorization.getStoreFactory().getPolicyStore().findByResourceServer(filters, getResourceServer().getId(), -1, -1);
|
||||
|
@ -311,9 +312,9 @@ public class AuthorizationBean {
|
|||
|
||||
return policies.stream()
|
||||
.filter(policy -> {
|
||||
Map<String, String> filters1 = new HashMap<>();
|
||||
Map<PermissionTicket.FilterOption, String> filters1 = new EnumMap<>(PermissionTicket.FilterOption.class);
|
||||
|
||||
filters1.put(PermissionTicket.POLICY, policy.getId());
|
||||
filters1.put(PermissionTicket.FilterOption.POLICY_ID, policy.getId());
|
||||
|
||||
return authorization.getStoreFactory().getPermissionTicketStore().find(filters1, resourceServer.getId(), -1, 1)
|
||||
.isEmpty();
|
||||
|
@ -366,7 +367,7 @@ public class AuthorizationBean {
|
|||
return requests.values();
|
||||
}
|
||||
|
||||
private List<PermissionTicket> findPermissions(Map<String, String> filters) {
|
||||
private List<PermissionTicket> findPermissions(Map<PermissionTicket.FilterOption, String> filters) {
|
||||
return authorization.getStoreFactory().getPermissionTicketStore().find(filters, null, -1, -1);
|
||||
}
|
||||
|
||||
|
|
|
@ -102,7 +102,7 @@ import java.nio.charset.StandardCharsets;
|
|||
import java.security.MessageDigest;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.EnumMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
@ -837,15 +837,15 @@ public class AccountFormService extends AbstractSecuredLocalService {
|
|||
policyStore.delete(policy.getId());
|
||||
}
|
||||
} else {
|
||||
Map<String, String> filters = new HashMap<>();
|
||||
Map<PermissionTicket.FilterOption, String> filters = new EnumMap<>(PermissionTicket.FilterOption.class);
|
||||
|
||||
filters.put(PermissionTicket.RESOURCE, resource.getId());
|
||||
filters.put(PermissionTicket.REQUESTER, session.users().getUserByUsername(realm, requester).getId());
|
||||
filters.put(PermissionTicket.FilterOption.RESOURCE_ID, resource.getId());
|
||||
filters.put(PermissionTicket.FilterOption.REQUESTER, session.users().getUserByUsername(realm, requester).getId());
|
||||
|
||||
if (isRevoke) {
|
||||
filters.put(PermissionTicket.GRANTED, Boolean.TRUE.toString());
|
||||
filters.put(PermissionTicket.FilterOption.GRANTED, Boolean.TRUE.toString());
|
||||
} else {
|
||||
filters.put(PermissionTicket.GRANTED, Boolean.FALSE.toString());
|
||||
filters.put(PermissionTicket.FilterOption.GRANTED, Boolean.FALSE.toString());
|
||||
}
|
||||
|
||||
List<PermissionTicket> tickets = ticketStore.find(filters, resource.getResourceServer(), -1, -1);
|
||||
|
@ -931,11 +931,11 @@ public class AccountFormService extends AbstractSecuredLocalService {
|
|||
return account.setError(Status.BAD_REQUEST, Messages.INVALID_USER).createResponse(AccountPages.RESOURCE_DETAIL);
|
||||
}
|
||||
|
||||
Map<String, String> filters = new HashMap<>();
|
||||
Map<PermissionTicket.FilterOption, String> filters = new EnumMap<>(PermissionTicket.FilterOption.class);
|
||||
|
||||
filters.put(PermissionTicket.RESOURCE, resource.getId());
|
||||
filters.put(PermissionTicket.OWNER, auth.getUser().getId());
|
||||
filters.put(PermissionTicket.REQUESTER, user.getId());
|
||||
filters.put(PermissionTicket.FilterOption.RESOURCE_ID, resource.getId());
|
||||
filters.put(PermissionTicket.FilterOption.OWNER, auth.getUser().getId());
|
||||
filters.put(PermissionTicket.FilterOption.REQUESTER, user.getId());
|
||||
|
||||
List<PermissionTicket> tickets = ticketStore.find(filters, resource.getResourceServer(), -1, -1);
|
||||
|
||||
|
@ -1003,15 +1003,15 @@ public class AccountFormService extends AbstractSecuredLocalService {
|
|||
return ErrorResponse.error("Invalid resource", Response.Status.BAD_REQUEST);
|
||||
}
|
||||
|
||||
HashMap<String, String> filters = new HashMap<>();
|
||||
Map<PermissionTicket.FilterOption, String> filters = new EnumMap<>(PermissionTicket.FilterOption.class);
|
||||
|
||||
filters.put(PermissionTicket.REQUESTER, auth.getUser().getId());
|
||||
filters.put(PermissionTicket.RESOURCE, resource.getId());
|
||||
filters.put(PermissionTicket.FilterOption.REQUESTER, auth.getUser().getId());
|
||||
filters.put(PermissionTicket.FilterOption.RESOURCE_ID, resource.getId());
|
||||
|
||||
if ("cancel".equals(action)) {
|
||||
filters.put(PermissionTicket.GRANTED, Boolean.TRUE.toString());
|
||||
filters.put(PermissionTicket.FilterOption.GRANTED, Boolean.TRUE.toString());
|
||||
} else if ("cancelRequest".equals(action)) {
|
||||
filters.put(PermissionTicket.GRANTED, Boolean.FALSE.toString());
|
||||
filters.put(PermissionTicket.FilterOption.GRANTED, Boolean.FALSE.toString());
|
||||
}
|
||||
|
||||
for (PermissionTicket ticket : ticketStore.find(filters, resource.getResourceServer(), -1, -1)) {
|
||||
|
|
|
@ -28,6 +28,7 @@ import javax.ws.rs.core.Response;
|
|||
import java.util.Calendar;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumMap;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
@ -80,11 +81,11 @@ public class ResourceService extends AbstractResourceService {
|
|||
@Path("permissions")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Collection<Permission> toPermissions() {
|
||||
Map<String, String> filters = new HashMap<>();
|
||||
Map<PermissionTicket.FilterOption, String> filters = new EnumMap<>(PermissionTicket.FilterOption.class);
|
||||
|
||||
filters.put(PermissionTicket.OWNER, user.getId());
|
||||
filters.put(PermissionTicket.GRANTED, Boolean.TRUE.toString());
|
||||
filters.put(PermissionTicket.RESOURCE, resource.getId());
|
||||
filters.put(PermissionTicket.FilterOption.OWNER, user.getId());
|
||||
filters.put(PermissionTicket.FilterOption.GRANTED, Boolean.TRUE.toString());
|
||||
filters.put(PermissionTicket.FilterOption.RESOURCE_ID, resource.getId());
|
||||
|
||||
Collection<ResourcePermission> resources = toPermissions(ticketStore.find(filters, null, -1, -1));
|
||||
Collection<Permission> permissions = Collections.EMPTY_LIST;
|
||||
|
@ -125,14 +126,14 @@ public class ResourceService extends AbstractResourceService {
|
|||
throw new BadRequestException("invalid_permissions");
|
||||
}
|
||||
|
||||
Map<String, String> filters = new HashMap<>();
|
||||
Map<PermissionTicket.FilterOption, String> filters = new EnumMap<>(PermissionTicket.FilterOption.class);
|
||||
|
||||
filters.put(PermissionTicket.RESOURCE, resource.getId());
|
||||
filters.put(PermissionTicket.FilterOption.RESOURCE_ID, resource.getId());
|
||||
|
||||
for (Permission permission : permissions) {
|
||||
UserModel user = getUser(permission.getUsername());
|
||||
|
||||
filters.put(PermissionTicket.REQUESTER, user.getId());
|
||||
filters.put(PermissionTicket.FilterOption.REQUESTER, user.getId());
|
||||
|
||||
List<PermissionTicket> tickets = ticketStore.find(filters, resource.getResourceServer(), -1, -1);
|
||||
|
||||
|
@ -187,11 +188,11 @@ public class ResourceService extends AbstractResourceService {
|
|||
@Path("permissions/requests")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Collection<Permission> getPermissionRequests() {
|
||||
Map<String, String> filters = new HashMap<>();
|
||||
Map<PermissionTicket.FilterOption, String> filters = new EnumMap<>(PermissionTicket.FilterOption.class);
|
||||
|
||||
filters.put(PermissionTicket.OWNER, user.getId());
|
||||
filters.put(PermissionTicket.GRANTED, Boolean.FALSE.toString());
|
||||
filters.put(PermissionTicket.RESOURCE, resource.getId());
|
||||
filters.put(PermissionTicket.FilterOption.OWNER, user.getId());
|
||||
filters.put(PermissionTicket.FilterOption.GRANTED, Boolean.FALSE.toString());
|
||||
filters.put(PermissionTicket.FilterOption.RESOURCE_ID, resource.getId());
|
||||
|
||||
Map<String, Permission> requests = new HashMap<>();
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ import javax.ws.rs.core.Link;
|
|||
import javax.ws.rs.core.Response;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.EnumMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiFunction;
|
||||
|
@ -64,12 +64,13 @@ public class ResourcesService extends AbstractResourceService {
|
|||
public Response getResources(@QueryParam("name") String name,
|
||||
@QueryParam("first") Integer first,
|
||||
@QueryParam("max") Integer max) {
|
||||
Map<String, String[]> filters = new HashMap<>();
|
||||
Map<org.keycloak.authorization.model.Resource.FilterOption, String[]> filters =
|
||||
new EnumMap<>(org.keycloak.authorization.model.Resource.FilterOption.class);
|
||||
|
||||
filters.put("owner", new String[] { user.getId() });
|
||||
filters.put(org.keycloak.authorization.model.Resource.FilterOption.OWNER, new String[] { user.getId() });
|
||||
|
||||
if (name != null) {
|
||||
filters.put("name", new String[] { name });
|
||||
filters.put(org.keycloak.authorization.model.Resource.FilterOption.NAME, new String[] { name });
|
||||
}
|
||||
|
||||
return queryResponse((f, m) -> resourceStore.findByResourceServer(filters, null, f, m).stream()
|
||||
|
@ -117,10 +118,10 @@ public class ResourcesService extends AbstractResourceService {
|
|||
@Path("pending-requests")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Response getPendingRequests() {
|
||||
Map<String, String> filters = new HashMap<>();
|
||||
Map<PermissionTicket.FilterOption, String> filters = new EnumMap<>(PermissionTicket.FilterOption.class);
|
||||
|
||||
filters.put(PermissionTicket.REQUESTER, user.getId());
|
||||
filters.put(PermissionTicket.GRANTED, Boolean.FALSE.toString());
|
||||
filters.put(PermissionTicket.FilterOption.REQUESTER, user.getId());
|
||||
filters.put(PermissionTicket.FilterOption.GRANTED, Boolean.FALSE.toString());
|
||||
|
||||
final List<PermissionTicket> permissionTickets = ticketStore.find(filters, null, -1, -1);
|
||||
|
||||
|
@ -160,11 +161,11 @@ public class ResourcesService extends AbstractResourceService {
|
|||
List<PermissionTicket> tickets;
|
||||
|
||||
if (withRequesters) {
|
||||
Map<String, String> filters = new HashMap<>();
|
||||
Map<PermissionTicket.FilterOption, String> filters = new EnumMap<>(PermissionTicket.FilterOption.class);
|
||||
|
||||
filters.put(PermissionTicket.OWNER, user.getId());
|
||||
filters.put(PermissionTicket.GRANTED, Boolean.TRUE.toString());
|
||||
filters.put(PermissionTicket.RESOURCE, resource.getId());
|
||||
filters.put(PermissionTicket.FilterOption.OWNER, user.getId());
|
||||
filters.put(PermissionTicket.FilterOption.GRANTED, Boolean.TRUE.toString());
|
||||
filters.put(PermissionTicket.FilterOption.RESOURCE_ID, resource.getId());
|
||||
|
||||
tickets = ticketStore.find(filters, null, -1, -1);
|
||||
} else {
|
||||
|
|
|
@ -48,16 +48,14 @@ import org.keycloak.storage.user.UserLookupProvider;
|
|||
import org.keycloak.storage.user.UserQueryProvider;
|
||||
import org.keycloak.storage.user.UserRegistrationProvider;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.keycloak.models.utils.KeycloakModelUtils.runJobInTransaction;
|
||||
import static org.keycloak.utils.StreamsUtil.distinctByKey;
|
||||
import static org.keycloak.utils.StreamsUtil.paginatedStream;
|
||||
|
||||
/**
|
||||
|
@ -175,17 +173,6 @@ public class UserStorageManager extends AbstractStorageManager<UserStorageProvid
|
|||
return paginatedStream(providersStream.flatMap(pagedQuery::query), firstResult, maxResults);
|
||||
}
|
||||
|
||||
/**
|
||||
* distinctByKey is not supposed to be used with parallel streams
|
||||
*
|
||||
* To make this method synchronized use {@code ConcurrentHashMap<Object, Boolean>} instead of HashSet
|
||||
*
|
||||
*/
|
||||
private static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
|
||||
Set<Object> seen = new HashSet<>();
|
||||
return t -> seen.add(keyExtractor.apply(t));
|
||||
}
|
||||
|
||||
// removeDuplicates method may cause concurrent issues, it should not be used on parallel streams
|
||||
private static Stream<UserModel> removeDuplicates(Stream<UserModel> withDuplicates) {
|
||||
return withDuplicates.filter(distinctByKey(UserModel::getId));
|
||||
|
|
|
@ -58,7 +58,7 @@
|
|||
},
|
||||
|
||||
"authorizationPersister": {
|
||||
"provider": "${keycloak.authorization.provider:}"
|
||||
"provider": "${keycloak.authorization.provider:jpa}"
|
||||
},
|
||||
|
||||
"theme": {
|
||||
|
|
Loading…
Reference in a new issue