KEYCLOAK-16932 Authorization map storage

This commit is contained in:
Michal Hajas 2021-03-22 15:49:23 +01:00 committed by Hynek Mlnařík
parent 8b3e77bf81
commit 1e2db74d86
72 changed files with 3840 additions and 433 deletions

View file

@ -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"

View file

@ -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

View file

@ -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;

View file

@ -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);
}

View file

@ -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(
{

View file

@ -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);
}

View file

@ -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

View file

@ -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);

View file

@ -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 + "]");
}
});

View file

@ -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);
}
}

View file

@ -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();

View file

@ -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() {
}
}

View file

@ -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";
}
}

View file

@ -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());
}
}

View file

@ -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());
}
}

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -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());
}
}

View file

@ -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();
}
}

View file

@ -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();
}
}

View file

@ -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();
}
}

View file

@ -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();
}
}

View file

@ -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();
}
}

View file

@ -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));
}
}

View file

@ -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));
}
}

View file

@ -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));
}
}

View file

@ -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));
}
}

View file

@ -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));
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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));
}
}

View file

@ -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));
}
}

View file

@ -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));
}
}

View file

@ -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));
}
}

View file

@ -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));
}
}

View file

@ -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)) {

View file

@ -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)
);
}

View file

@ -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();
});

View file

@ -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

View file

@ -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);
}

View file

@ -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);

View file

@ -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;
}

View file

@ -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.

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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.
*

View file

@ -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.
*

View file

@ -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);

View file

@ -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}.

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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);

View file

@ -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);

View file

@ -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());

View file

@ -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));
}
}

View file

@ -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);
}

View file

@ -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))

View file

@ -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);
}
}

View file

@ -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);

View file

@ -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(

View file

@ -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);

View file

@ -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;

View file

@ -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);
}

View file

@ -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)) {

View file

@ -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<>();

View file

@ -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 {

View file

@ -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));

View file

@ -58,7 +58,7 @@
},
"authorizationPersister": {
"provider": "${keycloak.authorization.provider:}"
"provider": "${keycloak.authorization.provider:jpa}"
},
"theme": {