Merge pull request #3662 from pedroigor/KEYCLOAK-4034
[KEYCLOAK-4034] - Improvements to UI, performance and some code cleanup
This commit is contained in:
commit
40591cff25
88 changed files with 2667 additions and 1553 deletions
|
@ -17,6 +17,8 @@
|
|||
*/
|
||||
package org.keycloak.authorization.policy.provider.aggregated;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.keycloak.authorization.AuthorizationProvider;
|
||||
import org.keycloak.authorization.model.Policy;
|
||||
import org.keycloak.authorization.policy.evaluation.DecisionResultCollector;
|
||||
|
@ -26,21 +28,11 @@ import org.keycloak.authorization.policy.evaluation.Result;
|
|||
import org.keycloak.authorization.policy.provider.PolicyProvider;
|
||||
import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
public class AggregatePolicyProvider implements PolicyProvider {
|
||||
|
||||
private final Policy policy;
|
||||
private final AuthorizationProvider authorization;
|
||||
|
||||
public AggregatePolicyProvider(Policy policy, AuthorizationProvider authorization) {
|
||||
this.policy = policy;
|
||||
this.authorization = authorization;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void evaluate(Evaluation evaluation) {
|
||||
//TODO: need to detect deep recursions
|
||||
|
@ -59,10 +51,12 @@ public class AggregatePolicyProvider implements PolicyProvider {
|
|||
}
|
||||
};
|
||||
|
||||
this.policy.getAssociatedPolicies().forEach(associatedPolicy -> {
|
||||
PolicyProviderFactory providerFactory = authorization.getProviderFactory(associatedPolicy.getType());
|
||||
PolicyProvider policyProvider = providerFactory.create(associatedPolicy, authorization);
|
||||
policyProvider.evaluate(new DefaultEvaluation(evaluation.getPermission(), evaluation.getContext(), policy, associatedPolicy, decision));
|
||||
Policy policy = evaluation.getPolicy();
|
||||
AuthorizationProvider authorization = evaluation.getAuthorizationProvider();
|
||||
|
||||
policy.getAssociatedPolicies().forEach(associatedPolicy -> {
|
||||
PolicyProvider policyProvider = authorization.getProvider(associatedPolicy.getType());
|
||||
policyProvider.evaluate(new DefaultEvaluation(evaluation.getPermission(), evaluation.getContext(), policy, associatedPolicy, decision, authorization));
|
||||
});
|
||||
|
||||
decision.onComplete();
|
||||
|
|
|
@ -19,7 +19,6 @@ package org.keycloak.authorization.policy.provider.aggregated;
|
|||
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.authorization.AuthorizationProvider;
|
||||
import org.keycloak.authorization.model.Policy;
|
||||
import org.keycloak.authorization.model.ResourceServer;
|
||||
import org.keycloak.authorization.policy.provider.PolicyProvider;
|
||||
import org.keycloak.authorization.policy.provider.PolicyProviderAdminService;
|
||||
|
@ -32,6 +31,8 @@ import org.keycloak.models.KeycloakSessionFactory;
|
|||
*/
|
||||
public class AggregatePolicyProviderFactory implements PolicyProviderFactory {
|
||||
|
||||
private AggregatePolicyProvider provider = new AggregatePolicyProvider();
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Aggregated";
|
||||
|
@ -43,8 +44,8 @@ public class AggregatePolicyProviderFactory implements PolicyProviderFactory {
|
|||
}
|
||||
|
||||
@Override
|
||||
public PolicyProvider create(Policy policy, AuthorizationProvider authorization) {
|
||||
return new AggregatePolicyProvider(policy, authorization);
|
||||
public PolicyProvider create(AuthorizationProvider authorization) {
|
||||
return provider;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package org.keycloak.authorization.policy.provider.client;
|
||||
|
||||
import static org.keycloak.authorization.policy.provider.client.ClientPolicyProviderFactory.getClients;
|
||||
|
||||
import org.keycloak.authorization.AuthorizationProvider;
|
||||
import org.keycloak.authorization.model.Policy;
|
||||
import org.keycloak.authorization.policy.evaluation.Evaluation;
|
||||
|
@ -8,26 +10,19 @@ import org.keycloak.authorization.policy.provider.PolicyProvider;
|
|||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
|
||||
import static org.keycloak.authorization.policy.provider.client.ClientPolicyProviderFactory.getClients;
|
||||
|
||||
public class ClientPolicyProvider implements PolicyProvider {
|
||||
|
||||
private final Policy policy;
|
||||
private final AuthorizationProvider authorization;
|
||||
|
||||
public ClientPolicyProvider(Policy policy, AuthorizationProvider authorization) {
|
||||
this.policy = policy;
|
||||
this.authorization = authorization;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void evaluate(Evaluation evaluation) {
|
||||
Policy policy = evaluation.getPolicy();
|
||||
EvaluationContext context = evaluation.getContext();
|
||||
String[] clients = getClients(this.policy);
|
||||
String[] clients = getClients(policy);
|
||||
AuthorizationProvider authorizationProvider = evaluation.getAuthorizationProvider();
|
||||
RealmModel realm = authorizationProvider.getKeycloakSession().getContext().getRealm();
|
||||
|
||||
if (clients.length > 0) {
|
||||
for (String client : clients) {
|
||||
ClientModel clientModel = getCurrentRealm().getClientById(client);
|
||||
ClientModel clientModel = realm.getClientById(client);
|
||||
if (context.getAttributes().containsValue("kc.client.id", clientModel.getClientId())) {
|
||||
evaluation.grant();
|
||||
return;
|
||||
|
@ -40,8 +35,4 @@ public class ClientPolicyProvider implements PolicyProvider {
|
|||
public void close() {
|
||||
|
||||
}
|
||||
|
||||
private RealmModel getCurrentRealm() {
|
||||
return this.authorization.getKeycloakSession().getContext().getRealm();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
package org.keycloak.authorization.policy.provider.client;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.authorization.AuthorizationProvider;
|
||||
import org.keycloak.authorization.model.Policy;
|
||||
|
@ -8,18 +12,18 @@ import org.keycloak.authorization.policy.provider.PolicyProvider;
|
|||
import org.keycloak.authorization.policy.provider.PolicyProviderAdminService;
|
||||
import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
|
||||
import org.keycloak.authorization.store.PolicyStore;
|
||||
import org.keycloak.authorization.store.ResourceServerStore;
|
||||
import org.keycloak.authorization.store.StoreFactory;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.RealmModel.ClientRemovedEvent;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class ClientPolicyProviderFactory implements PolicyProviderFactory {
|
||||
|
||||
private ClientPolicyProvider provider = new ClientPolicyProvider();
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Client";
|
||||
|
@ -31,8 +35,8 @@ public class ClientPolicyProviderFactory implements PolicyProviderFactory {
|
|||
}
|
||||
|
||||
@Override
|
||||
public PolicyProvider create(Policy policy, AuthorizationProvider authorization) {
|
||||
return new ClientPolicyProvider(policy, authorization);
|
||||
public PolicyProvider create(AuthorizationProvider authorization) {
|
||||
return provider;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -56,10 +60,14 @@ public class ClientPolicyProviderFactory implements PolicyProviderFactory {
|
|||
if (event instanceof ClientRemovedEvent) {
|
||||
KeycloakSession keycloakSession = ((ClientRemovedEvent) event).getKeycloakSession();
|
||||
AuthorizationProvider provider = keycloakSession.getProvider(AuthorizationProvider.class);
|
||||
PolicyStore policyStore = provider.getStoreFactory().getPolicyStore();
|
||||
StoreFactory storeFactory = provider.getStoreFactory();
|
||||
PolicyStore policyStore = storeFactory.getPolicyStore();
|
||||
ClientModel removedClient = ((ClientRemovedEvent) event).getClient();
|
||||
ResourceServerStore resourceServerStore = storeFactory.getResourceServerStore();
|
||||
ResourceServer resourceServer = resourceServerStore.findByClient(removedClient.getId());
|
||||
|
||||
policyStore.findByType(getId()).forEach(policy -> {
|
||||
if (resourceServer != null) {
|
||||
policyStore.findByType(getId(), resourceServer.getId()).forEach(policy -> {
|
||||
List<String> clients = new ArrayList<>();
|
||||
|
||||
for (String clientId : getClients(policy)) {
|
||||
|
@ -70,7 +78,7 @@ public class ClientPolicyProviderFactory implements PolicyProviderFactory {
|
|||
|
||||
try {
|
||||
if (clients.isEmpty()) {
|
||||
policyStore.findDependentPolicies(policy.getId()).forEach(dependentPolicy -> {
|
||||
policyStore.findDependentPolicies(policy.getId(), resourceServer.getId()).forEach(dependentPolicy -> {
|
||||
dependentPolicy.removeAssociatedPolicy(policy);
|
||||
});
|
||||
policyStore.delete(policy.getId());
|
||||
|
@ -82,6 +90,7 @@ public class ClientPolicyProviderFactory implements PolicyProviderFactory {
|
|||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -17,32 +17,34 @@
|
|||
*/
|
||||
package org.keycloak.authorization.policy.provider.js;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import javax.script.ScriptEngine;
|
||||
import javax.script.ScriptException;
|
||||
|
||||
import org.keycloak.authorization.model.Policy;
|
||||
import org.keycloak.authorization.policy.evaluation.Evaluation;
|
||||
import org.keycloak.authorization.policy.provider.PolicyProvider;
|
||||
|
||||
import javax.script.ScriptEngine;
|
||||
import javax.script.ScriptEngineManager;
|
||||
import javax.script.ScriptException;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
public class JSPolicyProvider implements PolicyProvider {
|
||||
|
||||
private final Policy policy;
|
||||
private Supplier<ScriptEngine> engineProvider;
|
||||
|
||||
public JSPolicyProvider(Policy policy) {
|
||||
this.policy = policy;
|
||||
public JSPolicyProvider(Supplier<ScriptEngine> engineProvider) {
|
||||
this.engineProvider = engineProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void evaluate(Evaluation evaluation) {
|
||||
ScriptEngineManager manager = new ScriptEngineManager();
|
||||
ScriptEngine engine = manager.getEngineByName("nashorn");
|
||||
ScriptEngine engine = engineProvider.get();
|
||||
|
||||
engine.put("$evaluation", evaluation);
|
||||
|
||||
Policy policy = evaluation.getPolicy();
|
||||
|
||||
try {
|
||||
engine.eval(policy.getConfig().get("code"));
|
||||
} catch (ScriptException e) {
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
package org.keycloak.authorization.policy.provider.js;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import javax.script.ScriptEngine;
|
||||
import javax.script.ScriptEngineManager;
|
||||
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.authorization.AuthorizationProvider;
|
||||
import org.keycloak.authorization.model.Policy;
|
||||
import org.keycloak.authorization.model.ResourceServer;
|
||||
import org.keycloak.authorization.policy.provider.PolicyProvider;
|
||||
import org.keycloak.authorization.policy.provider.PolicyProviderAdminService;
|
||||
|
@ -15,6 +19,13 @@ import org.keycloak.models.KeycloakSessionFactory;
|
|||
*/
|
||||
public class JSPolicyProviderFactory implements PolicyProviderFactory {
|
||||
|
||||
private JSPolicyProvider provider = new JSPolicyProvider(new Supplier<ScriptEngine>() {
|
||||
@Override
|
||||
public ScriptEngine get() {
|
||||
return new ScriptEngineManager().getEngineByName("nashorn");
|
||||
}
|
||||
});
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "JavaScript";
|
||||
|
@ -26,8 +37,8 @@ public class JSPolicyProviderFactory implements PolicyProviderFactory {
|
|||
}
|
||||
|
||||
@Override
|
||||
public PolicyProvider create(Policy policy, AuthorizationProvider authorization) {
|
||||
return new JSPolicyProvider(policy);
|
||||
public PolicyProvider create(AuthorizationProvider authorization) {
|
||||
return provider;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
*/
|
||||
package org.keycloak.authorization.policy.provider.resource;
|
||||
|
||||
import org.keycloak.authorization.model.Policy;
|
||||
import org.keycloak.authorization.policy.evaluation.Evaluation;
|
||||
import org.keycloak.authorization.policy.provider.PolicyProvider;
|
||||
|
||||
|
@ -26,7 +25,7 @@ import org.keycloak.authorization.policy.provider.PolicyProvider;
|
|||
*/
|
||||
public class ResourcePolicyProvider implements PolicyProvider {
|
||||
|
||||
public ResourcePolicyProvider(Policy policy) {
|
||||
public ResourcePolicyProvider() {
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@ package org.keycloak.authorization.policy.provider.resource;
|
|||
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.authorization.AuthorizationProvider;
|
||||
import org.keycloak.authorization.model.Policy;
|
||||
import org.keycloak.authorization.model.ResourceServer;
|
||||
import org.keycloak.authorization.policy.provider.PolicyProvider;
|
||||
import org.keycloak.authorization.policy.provider.PolicyProviderAdminService;
|
||||
|
@ -15,6 +14,8 @@ import org.keycloak.models.KeycloakSessionFactory;
|
|||
*/
|
||||
public class ResourcePolicyProviderFactory implements PolicyProviderFactory {
|
||||
|
||||
private ResourcePolicyProvider provider = new ResourcePolicyProvider();
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Resource-Based";
|
||||
|
@ -26,8 +27,8 @@ public class ResourcePolicyProviderFactory implements PolicyProviderFactory {
|
|||
}
|
||||
|
||||
@Override
|
||||
public PolicyProvider create(Policy policy, AuthorizationProvider authorization) {
|
||||
return new ResourcePolicyProvider(policy);
|
||||
public PolicyProvider create(AuthorizationProvider authorization) {
|
||||
return provider;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -17,6 +17,10 @@
|
|||
*/
|
||||
package org.keycloak.authorization.policy.provider.role;
|
||||
|
||||
import static org.keycloak.authorization.policy.provider.role.RolePolicyProviderFactory.getRoles;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.keycloak.authorization.AuthorizationProvider;
|
||||
import org.keycloak.authorization.identity.Identity;
|
||||
import org.keycloak.authorization.model.Policy;
|
||||
|
@ -26,39 +30,26 @@ import org.keycloak.models.ClientModel;
|
|||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static org.keycloak.authorization.policy.provider.role.RolePolicyProviderFactory.getRoles;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
public class RolePolicyProvider implements PolicyProvider {
|
||||
|
||||
private final Policy policy;
|
||||
private final AuthorizationProvider authorization;
|
||||
|
||||
public RolePolicyProvider(Policy policy, AuthorizationProvider authorization) {
|
||||
this.policy = policy;
|
||||
this.authorization = authorization;
|
||||
}
|
||||
|
||||
public RolePolicyProvider() {
|
||||
this(null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void evaluate(Evaluation evaluation) {
|
||||
Map<String, Object>[] roleIds = getRoles(this.policy);
|
||||
Policy policy = evaluation.getPolicy();
|
||||
Map<String, Object>[] roleIds = getRoles(policy);
|
||||
AuthorizationProvider authorizationProvider = evaluation.getAuthorizationProvider();
|
||||
RealmModel realm = authorizationProvider.getKeycloakSession().getContext().getRealm();
|
||||
|
||||
if (roleIds.length > 0) {
|
||||
Identity identity = evaluation.getContext().getIdentity();
|
||||
|
||||
for (Map<String, Object> current : roleIds) {
|
||||
RoleModel role = getCurrentRealm().getRoleById((String) current.get("id"));
|
||||
RoleModel role = realm.getRoleById((String) current.get("id"));
|
||||
|
||||
if (role != null) {
|
||||
boolean hasRole = hasRole(identity, role);
|
||||
boolean hasRole = hasRole(identity, role, realm);
|
||||
|
||||
if (!hasRole && Boolean.valueOf(isRequired(current))) {
|
||||
evaluation.deny();
|
||||
|
@ -75,19 +66,15 @@ public class RolePolicyProvider implements PolicyProvider {
|
|||
return (boolean) current.getOrDefault("required", false);
|
||||
}
|
||||
|
||||
private boolean hasRole(Identity identity, RoleModel role) {
|
||||
private boolean hasRole(Identity identity, RoleModel role, RealmModel realm) {
|
||||
String roleName = role.getName();
|
||||
if (role.isClientRole()) {
|
||||
ClientModel clientModel = getCurrentRealm().getClientById(role.getContainerId());
|
||||
ClientModel clientModel = realm.getClientById(role.getContainerId());
|
||||
return identity.hasClientRole(clientModel.getClientId(), roleName);
|
||||
}
|
||||
return identity.hasRealmRole(roleName);
|
||||
}
|
||||
|
||||
private RealmModel getCurrentRealm() {
|
||||
return this.authorization.getKeycloakSession().getContext().getRealm();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
|
||||
|
|
|
@ -26,8 +26,13 @@ import org.keycloak.authorization.policy.provider.PolicyProvider;
|
|||
import org.keycloak.authorization.policy.provider.PolicyProviderAdminService;
|
||||
import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
|
||||
import org.keycloak.authorization.store.PolicyStore;
|
||||
import org.keycloak.authorization.store.ResourceServerStore;
|
||||
import org.keycloak.authorization.store.StoreFactory;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleContainerModel;
|
||||
import org.keycloak.models.RoleContainerModel.RoleRemovedEvent;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
@ -37,12 +42,15 @@ import java.util.ArrayList;
|
|||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
public class RolePolicyProviderFactory implements PolicyProviderFactory {
|
||||
|
||||
private RolePolicyProvider provider = new RolePolicyProvider();
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Role";
|
||||
|
@ -54,8 +62,8 @@ public class RolePolicyProviderFactory implements PolicyProviderFactory {
|
|||
}
|
||||
|
||||
@Override
|
||||
public PolicyProvider create(Policy policy, AuthorizationProvider authorization) {
|
||||
return new RolePolicyProvider(policy, authorization);
|
||||
public PolicyProvider create(AuthorizationProvider authorization) {
|
||||
return provider;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -79,10 +87,28 @@ public class RolePolicyProviderFactory implements PolicyProviderFactory {
|
|||
if (event instanceof RoleRemovedEvent) {
|
||||
KeycloakSession keycloakSession = ((RoleRemovedEvent) event).getKeycloakSession();
|
||||
AuthorizationProvider provider = keycloakSession.getProvider(AuthorizationProvider.class);
|
||||
PolicyStore policyStore = provider.getStoreFactory().getPolicyStore();
|
||||
StoreFactory storeFactory = provider.getStoreFactory();
|
||||
PolicyStore policyStore = storeFactory.getPolicyStore();
|
||||
RoleModel removedRole = ((RoleRemovedEvent) event).getRole();
|
||||
RoleContainerModel container = removedRole.getContainer();
|
||||
ResourceServerStore resourceServerStore = storeFactory.getResourceServerStore();
|
||||
|
||||
policyStore.findByType(getId()).forEach(policy -> {
|
||||
if (container instanceof RealmModel) {
|
||||
RealmModel realm = (RealmModel) container;
|
||||
realm.getClients().forEach(clientModel -> updateResourceServer(clientModel, removedRole, resourceServerStore, policyStore));
|
||||
} else {
|
||||
ClientModel clientModel = (ClientModel) container;
|
||||
updateResourceServer(clientModel, removedRole, resourceServerStore, policyStore);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void updateResourceServer(ClientModel clientModel, RoleModel removedRole, ResourceServerStore resourceServerStore, PolicyStore policyStore) {
|
||||
ResourceServer resourceServer = resourceServerStore.findByClient(clientModel.getId());
|
||||
|
||||
if (resourceServer != null) {
|
||||
policyStore.findByType(getId(), resourceServer.getId()).forEach(policy -> {
|
||||
List<Map> roles = new ArrayList<>();
|
||||
|
||||
for (Map<String,Object> role : getRoles(policy)) {
|
||||
|
@ -99,8 +125,11 @@ public class RolePolicyProviderFactory implements PolicyProviderFactory {
|
|||
|
||||
try {
|
||||
if (roles.isEmpty()) {
|
||||
policyStore.findDependentPolicies(policy.getId()).forEach(dependentPolicy -> {
|
||||
policyStore.findDependentPolicies(policy.getId(), resourceServer.getId()).forEach(dependentPolicy -> {
|
||||
dependentPolicy.removeAssociatedPolicy(policy);
|
||||
if (dependentPolicy.getAssociatedPolicies().isEmpty()) {
|
||||
policyStore.delete(dependentPolicy.getId());
|
||||
}
|
||||
});
|
||||
policyStore.delete(policy.getId());
|
||||
} else {
|
||||
|
@ -113,7 +142,6 @@ public class RolePolicyProviderFactory implements PolicyProviderFactory {
|
|||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
*/
|
||||
package org.keycloak.authorization.policy.provider.scope;
|
||||
|
||||
import org.keycloak.authorization.model.Policy;
|
||||
import org.keycloak.authorization.policy.evaluation.Evaluation;
|
||||
import org.keycloak.authorization.policy.provider.PolicyProvider;
|
||||
|
||||
|
@ -26,12 +25,6 @@ import org.keycloak.authorization.policy.provider.PolicyProvider;
|
|||
*/
|
||||
public class ScopePolicyProvider implements PolicyProvider {
|
||||
|
||||
private final Policy policy;
|
||||
|
||||
public ScopePolicyProvider(Policy policy) {
|
||||
this.policy = policy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void evaluate(Evaluation evaluation) {
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ package org.keycloak.authorization.policy.provider.scope;
|
|||
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.authorization.AuthorizationProvider;
|
||||
import org.keycloak.authorization.model.Policy;
|
||||
import org.keycloak.authorization.model.ResourceServer;
|
||||
import org.keycloak.authorization.policy.provider.PolicyProvider;
|
||||
import org.keycloak.authorization.policy.provider.PolicyProviderAdminService;
|
||||
|
@ -15,6 +14,8 @@ import org.keycloak.models.KeycloakSessionFactory;
|
|||
*/
|
||||
public class ScopePolicyProviderFactory implements PolicyProviderFactory {
|
||||
|
||||
private ScopePolicyProvider provider = new ScopePolicyProvider();
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Scope-Based";
|
||||
|
@ -26,8 +27,8 @@ public class ScopePolicyProviderFactory implements PolicyProviderFactory {
|
|||
}
|
||||
|
||||
@Override
|
||||
public PolicyProvider create(Policy policy, AuthorizationProvider authorization) {
|
||||
return new ScopePolicyProvider(policy);
|
||||
public PolicyProvider create(AuthorizationProvider authorization) {
|
||||
return provider;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -17,14 +17,14 @@
|
|||
*/
|
||||
package org.keycloak.authorization.policy.provider.time;
|
||||
|
||||
import org.keycloak.authorization.model.Policy;
|
||||
import org.keycloak.authorization.policy.evaluation.Evaluation;
|
||||
import org.keycloak.authorization.policy.provider.PolicyProvider;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
|
||||
import org.keycloak.authorization.model.Policy;
|
||||
import org.keycloak.authorization.policy.evaluation.Evaluation;
|
||||
import org.keycloak.authorization.policy.provider.PolicyProvider;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
|
@ -32,51 +32,51 @@ public class TimePolicyProvider implements PolicyProvider {
|
|||
|
||||
static String DEFAULT_DATE_PATTERN = "yyyy-MM-dd hh:mm:ss";
|
||||
|
||||
private final Policy policy;
|
||||
private final SimpleDateFormat dateFormat;
|
||||
private final Date currentDate;
|
||||
|
||||
public TimePolicyProvider(Policy policy) {
|
||||
this.policy = policy;
|
||||
public TimePolicyProvider() {
|
||||
this.dateFormat = new SimpleDateFormat(DEFAULT_DATE_PATTERN);
|
||||
this.currentDate = new Date();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void evaluate(Evaluation evaluation) {
|
||||
Policy policy = evaluation.getPolicy();
|
||||
|
||||
try {
|
||||
String notBefore = this.policy.getConfig().get("nbf");
|
||||
if (notBefore != null) {
|
||||
String notBefore = policy.getConfig().get("nbf");
|
||||
if (notBefore != null && !"".equals(notBefore)) {
|
||||
if (this.currentDate.before(this.dateFormat.parse(format(notBefore)))) {
|
||||
evaluation.deny();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
String notOnOrAfter = this.policy.getConfig().get("noa");
|
||||
if (notOnOrAfter != null) {
|
||||
String notOnOrAfter = policy.getConfig().get("noa");
|
||||
if (notOnOrAfter != null && !"".equals(notOnOrAfter)) {
|
||||
if (this.currentDate.after(this.dateFormat.parse(format(notOnOrAfter)))) {
|
||||
evaluation.deny();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (isInvalid(Calendar.DAY_OF_MONTH, "dayMonth")
|
||||
|| isInvalid(Calendar.MONTH, "month")
|
||||
|| isInvalid(Calendar.YEAR, "year")
|
||||
|| isInvalid(Calendar.HOUR_OF_DAY, "hour")
|
||||
|| isInvalid(Calendar.MINUTE, "minute")) {
|
||||
if (isInvalid(Calendar.DAY_OF_MONTH, "dayMonth", policy)
|
||||
|| isInvalid(Calendar.MONTH, "month", policy)
|
||||
|| isInvalid(Calendar.YEAR, "year", policy)
|
||||
|| isInvalid(Calendar.HOUR_OF_DAY, "hour", policy)
|
||||
|| isInvalid(Calendar.MINUTE, "minute", policy)) {
|
||||
evaluation.deny();
|
||||
return;
|
||||
}
|
||||
|
||||
evaluation.grant();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Could not evaluate time-based policy [" + this.policy.getName() + "].", e);
|
||||
throw new RuntimeException("Could not evaluate time-based policy [" + policy.getName() + "].", e);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isInvalid(int timeConstant, String configName) {
|
||||
private boolean isInvalid(int timeConstant, String configName, Policy policy) {
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
|
||||
calendar.setTime(this.currentDate);
|
||||
|
@ -87,9 +87,9 @@ public class TimePolicyProvider implements PolicyProvider {
|
|||
dateField++;
|
||||
}
|
||||
|
||||
String start = this.policy.getConfig().get(configName);
|
||||
String start = policy.getConfig().get(configName);
|
||||
if (start != null) {
|
||||
String end = this.policy.getConfig().get(configName + "End");
|
||||
String end = policy.getConfig().get(configName + "End");
|
||||
if (end != null) {
|
||||
if (dateField < Integer.parseInt(start) || dateField > Integer.parseInt(end)) {
|
||||
return true;
|
||||
|
|
|
@ -2,7 +2,6 @@ package org.keycloak.authorization.policy.provider.time;
|
|||
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.authorization.AuthorizationProvider;
|
||||
import org.keycloak.authorization.model.Policy;
|
||||
import org.keycloak.authorization.model.ResourceServer;
|
||||
import org.keycloak.authorization.policy.provider.PolicyProvider;
|
||||
import org.keycloak.authorization.policy.provider.PolicyProviderAdminService;
|
||||
|
@ -15,6 +14,8 @@ import org.keycloak.models.KeycloakSessionFactory;
|
|||
*/
|
||||
public class TimePolicyProviderFactory implements PolicyProviderFactory {
|
||||
|
||||
private TimePolicyProvider provider = new TimePolicyProvider();
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Time";
|
||||
|
@ -26,8 +27,8 @@ public class TimePolicyProviderFactory implements PolicyProviderFactory {
|
|||
}
|
||||
|
||||
@Override
|
||||
public PolicyProvider create(Policy policy, AuthorizationProvider authorization) {
|
||||
return new TimePolicyProvider(policy);
|
||||
public PolicyProvider create(AuthorizationProvider authorization) {
|
||||
return provider;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -29,16 +29,11 @@ import static org.keycloak.authorization.policy.provider.user.UserPolicyProvider
|
|||
*/
|
||||
public class UserPolicyProvider implements PolicyProvider {
|
||||
|
||||
private final Policy policy;
|
||||
|
||||
public UserPolicyProvider(Policy policy) {
|
||||
this.policy = policy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void evaluate(Evaluation evaluation) {
|
||||
Policy policy = evaluation.getPolicy();
|
||||
EvaluationContext context = evaluation.getContext();
|
||||
String[] userIds = getUsers(this.policy);
|
||||
String[] userIds = getUsers(policy);
|
||||
|
||||
if (userIds.length > 0) {
|
||||
for (String userId : userIds) {
|
||||
|
|
|
@ -18,6 +18,10 @@
|
|||
|
||||
package org.keycloak.authorization.policy.provider.user;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.authorization.AuthorizationProvider;
|
||||
import org.keycloak.authorization.model.Policy;
|
||||
|
@ -26,21 +30,22 @@ import org.keycloak.authorization.policy.provider.PolicyProvider;
|
|||
import org.keycloak.authorization.policy.provider.PolicyProviderAdminService;
|
||||
import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
|
||||
import org.keycloak.authorization.store.PolicyStore;
|
||||
import org.keycloak.authorization.store.ResourceServerStore;
|
||||
import org.keycloak.authorization.store.StoreFactory;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserModel.UserRemovedEvent;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
public class UserPolicyProviderFactory implements PolicyProviderFactory {
|
||||
|
||||
private UserPolicyProvider provider = new UserPolicyProvider();
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "User";
|
||||
|
@ -52,8 +57,8 @@ public class UserPolicyProviderFactory implements PolicyProviderFactory {
|
|||
}
|
||||
|
||||
@Override
|
||||
public PolicyProvider create(Policy policy, AuthorizationProvider authorization) {
|
||||
return new UserPolicyProvider(policy);
|
||||
public PolicyProvider create(AuthorizationProvider authorization) {
|
||||
return provider;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -77,10 +82,16 @@ public class UserPolicyProviderFactory implements PolicyProviderFactory {
|
|||
if (event instanceof UserRemovedEvent) {
|
||||
KeycloakSession keycloakSession = ((UserRemovedEvent) event).getKeycloakSession();
|
||||
AuthorizationProvider provider = keycloakSession.getProvider(AuthorizationProvider.class);
|
||||
PolicyStore policyStore = provider.getStoreFactory().getPolicyStore();
|
||||
StoreFactory storeFactory = provider.getStoreFactory();
|
||||
PolicyStore policyStore = storeFactory.getPolicyStore();
|
||||
UserModel removedUser = ((UserRemovedEvent) event).getUser();
|
||||
RealmModel realm = ((UserRemovedEvent) event).getRealm();
|
||||
ResourceServerStore resourceServerStore = storeFactory.getResourceServerStore();
|
||||
realm.getClients().forEach(clientModel -> {
|
||||
ResourceServer resourceServer = resourceServerStore.findByClient(clientModel.getId());
|
||||
|
||||
policyStore.findByType(getId()).forEach(policy -> {
|
||||
if (resourceServer != null) {
|
||||
policyStore.findByType(getId(), resourceServer.getId()).forEach(policy -> {
|
||||
List<String> users = new ArrayList<>();
|
||||
|
||||
for (String userId : getUsers(policy)) {
|
||||
|
@ -91,8 +102,11 @@ public class UserPolicyProviderFactory implements PolicyProviderFactory {
|
|||
|
||||
try {
|
||||
if (users.isEmpty()) {
|
||||
policyStore.findDependentPolicies(policy.getId()).forEach(dependentPolicy -> {
|
||||
policyStore.findDependentPolicies(policy.getId(), resourceServer.getId()).forEach(dependentPolicy -> {
|
||||
dependentPolicy.removeAssociatedPolicy(policy);
|
||||
if (dependentPolicy.getAssociatedPolicies().isEmpty()) {
|
||||
policyStore.delete(dependentPolicy.getId());
|
||||
}
|
||||
});
|
||||
policyStore.delete(policy.getId());
|
||||
} else {
|
||||
|
@ -105,6 +119,8 @@ public class UserPolicyProviderFactory implements PolicyProviderFactory {
|
|||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
|
|
|
@ -17,6 +17,9 @@
|
|||
*/
|
||||
package org.keycloak.authorization.policy.provider.drools;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.keycloak.authorization.model.Policy;
|
||||
import org.keycloak.authorization.policy.evaluation.Evaluation;
|
||||
import org.keycloak.authorization.policy.provider.PolicyProvider;
|
||||
|
||||
|
@ -25,15 +28,15 @@ import org.keycloak.authorization.policy.provider.PolicyProvider;
|
|||
*/
|
||||
public class DroolsPolicyProvider implements PolicyProvider {
|
||||
|
||||
private final DroolsPolicy policy;
|
||||
private final Function<Policy, DroolsPolicy> policy;
|
||||
|
||||
public DroolsPolicyProvider(DroolsPolicy policy) {
|
||||
this.policy = policy;
|
||||
public DroolsPolicyProvider(Function<Policy, DroolsPolicy> policyProvider) {
|
||||
this.policy = policyProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void evaluate(Evaluation evaluationt) {
|
||||
this.policy.evaluate(evaluationt);
|
||||
public void evaluate(Evaluation evaluation) {
|
||||
policy.apply(evaluation.getPolicy()).evaluate(evaluation);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
package org.keycloak.authorization.policy.provider.drools;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.authorization.AuthorizationProvider;
|
||||
import org.keycloak.authorization.model.Policy;
|
||||
|
@ -9,24 +13,25 @@ import org.keycloak.authorization.policy.provider.PolicyProviderAdminService;
|
|||
import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.utils.PostMigrationEvent;
|
||||
import org.keycloak.provider.ProviderEvent;
|
||||
import org.keycloak.provider.ProviderEventListener;
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
import org.kie.api.KieServices;
|
||||
import org.kie.api.KieServices.Factory;
|
||||
import org.kie.api.runtime.KieContainer;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
public class DroolsPolicyProviderFactory implements PolicyProviderFactory {
|
||||
|
||||
private KieServices ks;
|
||||
private final Map<String, DroolsPolicy> containers = new HashMap<>();
|
||||
private final Map<String, DroolsPolicy> containers = Collections.synchronizedMap(new HashMap<>());
|
||||
private DroolsPolicyProvider provider = new DroolsPolicyProvider(policy -> {
|
||||
if (!containers.containsKey(policy.getId())) {
|
||||
synchronized (containers) {
|
||||
update(policy);
|
||||
}
|
||||
}
|
||||
return containers.get(policy.getId());
|
||||
});
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
|
@ -39,12 +44,8 @@ public class DroolsPolicyProviderFactory implements PolicyProviderFactory {
|
|||
}
|
||||
|
||||
@Override
|
||||
public PolicyProvider create(Policy policy, AuthorizationProvider authorization) {
|
||||
if (!this.containers.containsKey(policy.getId())) {
|
||||
update(policy);
|
||||
}
|
||||
|
||||
return new DroolsPolicyProvider(this.containers.get(policy.getId()));
|
||||
public PolicyProvider create(AuthorizationProvider authorization) {
|
||||
return provider;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -64,19 +65,6 @@ public class DroolsPolicyProviderFactory implements PolicyProviderFactory {
|
|||
|
||||
@Override
|
||||
public void postInit(KeycloakSessionFactory factory) {
|
||||
factory.register(new ProviderEventListener() {
|
||||
|
||||
@Override
|
||||
public void onEvent(ProviderEvent event) {
|
||||
// Ensure the initialization is done after DB upgrade is finished
|
||||
if (event instanceof PostMigrationEvent) {
|
||||
ProviderFactory<AuthorizationProvider> providerFactory = factory.getProviderFactory(AuthorizationProvider.class);
|
||||
AuthorizationProvider authorization = providerFactory.create(factory.create());
|
||||
authorization.getStoreFactory().getPolicyStore().findByType(getId()).forEach(DroolsPolicyProviderFactory.this::update);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -16,11 +16,7 @@
|
|||
*/
|
||||
package org.keycloak.representations.idm.authorization;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
|
@ -36,9 +32,6 @@ public class PolicyRepresentation {
|
|||
private Logic logic = Logic.POSITIVE;
|
||||
private DecisionStrategy decisionStrategy = DecisionStrategy.UNANIMOUS;
|
||||
private Map<String, String> config = new HashMap();
|
||||
private List<PolicyRepresentation> dependentPolicies;
|
||||
@JsonInclude(JsonInclude.Include.NON_EMPTY)
|
||||
private List<PolicyRepresentation> associatedPolicies = new ArrayList<>();
|
||||
|
||||
public String getId() {
|
||||
return this.id;
|
||||
|
@ -96,14 +89,6 @@ public class PolicyRepresentation {
|
|||
this.description = description;
|
||||
}
|
||||
|
||||
public List<PolicyRepresentation> getAssociatedPolicies() {
|
||||
return associatedPolicies;
|
||||
}
|
||||
|
||||
public void setAssociatedPolicies(List<PolicyRepresentation> associatedPolicies) {
|
||||
this.associatedPolicies = associatedPolicies;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
|
@ -116,12 +101,4 @@ public class PolicyRepresentation {
|
|||
public int hashCode() {
|
||||
return Objects.hash(getId());
|
||||
}
|
||||
|
||||
public void setDependentPolicies(List<PolicyRepresentation> dependentPolicies) {
|
||||
this.dependentPolicies = dependentPolicies;
|
||||
}
|
||||
|
||||
public List<PolicyRepresentation> getDependentPolicies() {
|
||||
return this.dependentPolicies;
|
||||
}
|
||||
}
|
|
@ -16,14 +16,14 @@
|
|||
*/
|
||||
package org.keycloak.representations.idm.authorization;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
/**
|
||||
* <p>One or more resources that the resource server manages as a set of protected resources.
|
||||
|
@ -159,18 +159,6 @@ public class ResourceRepresentation {
|
|||
this.owner = owner;
|
||||
}
|
||||
|
||||
public List<PolicyRepresentation> getPolicies() {
|
||||
return this.policies;
|
||||
}
|
||||
|
||||
public void setPolicies(List<PolicyRepresentation> policies) {
|
||||
this.policies = policies;
|
||||
}
|
||||
|
||||
<T> T test(Predicate<T> t) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setTypedScopes(List<ScopeRepresentation> typedScopes) {
|
||||
this.typedScopes = typedScopes;
|
||||
}
|
||||
|
@ -178,4 +166,15 @@ public class ResourceRepresentation {
|
|||
public List<ScopeRepresentation> getTypedScopes() {
|
||||
return typedScopes;
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
ResourceRepresentation scope = (ResourceRepresentation) o;
|
||||
return Objects.equals(getName(), scope.getName());
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return Objects.hash(getName());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,16 +16,21 @@
|
|||
*/
|
||||
package org.keycloak.admin.client.resource;
|
||||
|
||||
import org.jboss.resteasy.annotations.cache.NoCache;
|
||||
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
|
||||
import java.util.List;
|
||||
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.DELETE;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.PUT;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
|
||||
import org.jboss.resteasy.annotations.cache.NoCache;
|
||||
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
|
@ -42,4 +47,28 @@ public interface PolicyResource {
|
|||
|
||||
@DELETE
|
||||
void remove();
|
||||
|
||||
@Path("/associatedPolicies")
|
||||
@GET
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@NoCache
|
||||
List<PolicyRepresentation> associatedPolicies();
|
||||
|
||||
@Path("/dependentPolicies")
|
||||
@GET
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@NoCache
|
||||
List<PolicyRepresentation> dependentPolicies();
|
||||
|
||||
@Path("/scopes")
|
||||
@GET
|
||||
@Produces("application/json")
|
||||
@NoCache
|
||||
List<ScopeRepresentation> scopes();
|
||||
|
||||
@Path("/resources")
|
||||
@GET
|
||||
@Produces("application/json")
|
||||
@NoCache
|
||||
List<ResourceRepresentation> resources();
|
||||
}
|
||||
|
|
|
@ -16,13 +16,17 @@
|
|||
*/
|
||||
package org.keycloak.admin.client.resource;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.jboss.resteasy.annotations.cache.NoCache;
|
||||
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
|
||||
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.DELETE;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.PUT;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
|
||||
|
@ -42,4 +46,10 @@ public interface ResourceResource {
|
|||
|
||||
@DELETE
|
||||
void remove();
|
||||
|
||||
@Path("permissions")
|
||||
@GET
|
||||
@NoCache
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
List<PolicyRepresentation> permissions();
|
||||
}
|
||||
|
|
|
@ -16,13 +16,17 @@
|
|||
*/
|
||||
package org.keycloak.admin.client.resource;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.jboss.resteasy.annotations.cache.NoCache;
|
||||
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
|
||||
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.DELETE;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.PUT;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
|
||||
|
@ -42,4 +46,9 @@ public interface ResourceScopeResource {
|
|||
|
||||
@DELETE
|
||||
void remove();
|
||||
|
||||
@Path("/permissions")
|
||||
@GET
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
List<PolicyRepresentation> permissions();
|
||||
}
|
||||
|
|
|
@ -18,6 +18,17 @@
|
|||
|
||||
package org.keycloak.models.authorization.infinispan;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.infinispan.Cache;
|
||||
import org.keycloak.authorization.model.Policy;
|
||||
import org.keycloak.authorization.model.Resource;
|
||||
|
@ -29,17 +40,10 @@ import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
|
|||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.authorization.infinispan.InfinispanStoreFactoryProvider.CacheTransaction;
|
||||
import org.keycloak.models.authorization.infinispan.entities.CachedPolicy;
|
||||
import org.keycloak.models.cache.authorization.CachedStoreFactoryProvider;
|
||||
import org.keycloak.representations.idm.authorization.DecisionStrategy;
|
||||
import org.keycloak.representations.idm.authorization.Logic;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
|
@ -47,41 +51,58 @@ public class CachedPolicyStore implements PolicyStore {
|
|||
|
||||
private static final String POLICY_ID_CACHE_PREFIX = "policy-id-";
|
||||
|
||||
private final Cache<String, List> cache;
|
||||
private final Cache<String, List<CachedPolicy>> cache;
|
||||
private final KeycloakSession session;
|
||||
private final CacheTransaction transaction;
|
||||
private final List<String> cacheKeys;
|
||||
private StoreFactory storeFactory;
|
||||
private PolicyStore delegate;
|
||||
private CachedStoreFactoryProvider cachedStoreFactory;
|
||||
|
||||
public CachedPolicyStore(KeycloakSession session, CacheTransaction transaction) {
|
||||
public CachedPolicyStore(KeycloakSession session, CacheTransaction transaction, StoreFactory storeFactory) {
|
||||
this.session = session;
|
||||
this.transaction = transaction;
|
||||
InfinispanConnectionProvider provider = session.getProvider(InfinispanConnectionProvider.class);
|
||||
this.cache = provider.getCache(InfinispanConnectionProvider.AUTHORIZATION_CACHE_NAME);
|
||||
cacheKeys = new ArrayList<>();
|
||||
cacheKeys.add("findByResource");
|
||||
cacheKeys.add("findByResourceType");
|
||||
cacheKeys.add("findByScopeIds");
|
||||
cacheKeys.add("findByType");
|
||||
this.storeFactory = storeFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Policy create(String name, String type, ResourceServer resourceServer) {
|
||||
Policy policy = getDelegate().create(name, type, getStoreFactory().getResourceServerStore().findById(resourceServer.getId()));
|
||||
String id = policy.getId();
|
||||
|
||||
this.transaction.whenRollback(() -> cache.remove(getCacheKeyForPolicy(policy.getId())));
|
||||
this.transaction.whenCommit(() -> {
|
||||
cache.remove(getCacheKeyForPolicy(id));
|
||||
invalidateCache(resourceServer.getId());
|
||||
});
|
||||
|
||||
return createAdapter(new CachedPolicy(policy));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(String id) {
|
||||
Policy policy = findById(id, null);
|
||||
ResourceServer resourceServer = policy.getResourceServer();
|
||||
getDelegate().delete(id);
|
||||
this.transaction.whenCommit(() -> cache.remove(getCacheKeyForPolicy(id)));
|
||||
this.transaction.whenCommit(() -> {
|
||||
cache.remove(getCacheKeyForPolicy(id));
|
||||
invalidateCache(resourceServer.getId());
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Policy findById(String id) {
|
||||
public Policy findById(String id, String resourceServerId) {
|
||||
String cacheKeyForPolicy = getCacheKeyForPolicy(id);
|
||||
List<CachedPolicy> cached = this.cache.get(cacheKeyForPolicy);
|
||||
|
||||
if (cached == null) {
|
||||
Policy policy = getDelegate().findById(id);
|
||||
Policy policy = getDelegate().findById(id, resourceServerId);
|
||||
|
||||
if (policy != null) {
|
||||
return createAdapter(updatePolicyCache(policy));
|
||||
|
@ -100,7 +121,7 @@ public class CachedPolicyStore implements PolicyStore {
|
|||
|
||||
@Override
|
||||
public List<Policy> findByResourceServer(String resourceServerId) {
|
||||
return getDelegate().findByResourceServer(resourceServerId).stream().map(policy -> findById(policy.getId())).collect(Collectors.toList());
|
||||
return getDelegate().findByResourceServer(resourceServerId);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -109,88 +130,49 @@ public class CachedPolicyStore implements PolicyStore {
|
|||
}
|
||||
|
||||
@Override
|
||||
public List<Policy> findByResource(String resourceId) {
|
||||
List<Policy> cache = new ArrayList<>();
|
||||
|
||||
for (Entry entry : this.cache.entrySet()) {
|
||||
String cacheKey = (String) entry.getKey();
|
||||
|
||||
if (cacheKey.startsWith(POLICY_ID_CACHE_PREFIX)) {
|
||||
List<CachedPolicy> value = (List<CachedPolicy>) entry.getValue();
|
||||
CachedPolicy policy = value.get(0);
|
||||
|
||||
if (policy.getResourcesIds().contains(resourceId)) {
|
||||
cache.add(findById(policy.getId()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (cache.isEmpty()) {
|
||||
getDelegate().findByResource(resourceId).forEach(policy -> cache.add(findById(updatePolicyCache(policy).getId())));
|
||||
}
|
||||
|
||||
return cache;
|
||||
public List<Policy> findByResource(String resourceId, String resourceServerId) {
|
||||
return cacheResult(new StringBuilder("findByResource").append(resourceServerId).append(resourceId).toString(), () -> getDelegate().findByResource(resourceId, resourceServerId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Policy> findByResourceType(String resourceType, String resourceServerId) {
|
||||
List<Policy> cache = new ArrayList<>();
|
||||
|
||||
for (Entry entry : this.cache.entrySet()) {
|
||||
String cacheKey = (String) entry.getKey();
|
||||
|
||||
if (cacheKey.startsWith(POLICY_ID_CACHE_PREFIX)) {
|
||||
List<CachedPolicy> value = (List<CachedPolicy>) entry.getValue();
|
||||
CachedPolicy policy = value.get(0);
|
||||
|
||||
if (policy.getResourceServerId().equals(resourceServerId) && policy.getConfig().getOrDefault("defaultResourceType", "").equals(resourceType)) {
|
||||
cache.add(findById(policy.getId()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (cache.isEmpty()) {
|
||||
getDelegate().findByResourceType(resourceType, resourceServerId).forEach(policy -> cache.add(findById(updatePolicyCache(policy).getId())));
|
||||
}
|
||||
|
||||
return cache;
|
||||
return cacheResult(new StringBuilder("findByResourceType").append(resourceServerId).append(resourceType).toString(), () -> getDelegate().findByResourceType(resourceType, resourceServerId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Policy> findByScopeIds(List<String> scopeIds, String resourceServerId) {
|
||||
List<Policy> cache = new ArrayList<>();
|
||||
List<Policy> policies = new ArrayList<>();
|
||||
|
||||
for (Entry entry : this.cache.entrySet()) {
|
||||
String cacheKey = (String) entry.getKey();
|
||||
|
||||
if (cacheKey.startsWith(POLICY_ID_CACHE_PREFIX)) {
|
||||
List<CachedPolicy> value = (List<CachedPolicy>) entry.getValue();
|
||||
CachedPolicy policy = value.get(0);
|
||||
|
||||
for (String scopeId : policy.getScopesIds()) {
|
||||
if (scopeIds.contains(scopeId)) {
|
||||
cache.add(findById(policy.getId()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (String scopeId : scopeIds) {
|
||||
policies.addAll(cacheResult(new StringBuilder("findByScopeIds").append(resourceServerId).append(scopeId).toString(), () -> getDelegate().findByScopeIds(Arrays.asList(scopeId), resourceServerId)));
|
||||
}
|
||||
|
||||
if (cache.isEmpty()) {
|
||||
getDelegate().findByScopeIds(scopeIds, resourceServerId).forEach(policy -> cache.add(findById(updatePolicyCache(policy).getId())));
|
||||
}
|
||||
|
||||
return cache;
|
||||
return policies;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Policy> findByType(String type) {
|
||||
return getDelegate().findByType(type).stream().map(policy -> findById(policy.getId())).collect(Collectors.toList());
|
||||
public List<Policy> findByType(String type, String resourceServerId) {
|
||||
return cacheResult(new StringBuilder("findByType").append(resourceServerId).append(type).toString(), () -> getDelegate().findByType(type, resourceServerId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Policy> findDependentPolicies(String id) {
|
||||
return getDelegate().findDependentPolicies(id).stream().map(policy -> findById(policy.getId())).collect(Collectors.toList());
|
||||
public List<Policy> findDependentPolicies(String id, String resourceServerId) {
|
||||
return getDelegate().findDependentPolicies(id, resourceServerId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyChange(Object cached) {
|
||||
String resourceServerId;
|
||||
|
||||
if (Resource.class.isInstance(cached)) {
|
||||
resourceServerId = ((Resource) cached).getResourceServer().getId();
|
||||
} else if (Scope.class.isInstance(cached)){
|
||||
resourceServerId = ((Scope) cached).getResourceServer().getId();
|
||||
} else {
|
||||
throw new RuntimeException("Unexpected notification [" + cached + "]");
|
||||
}
|
||||
|
||||
invalidateCache(resourceServerId);
|
||||
}
|
||||
|
||||
private String getCacheKeyForPolicy(String policyId) {
|
||||
|
@ -198,10 +180,6 @@ public class CachedPolicyStore implements PolicyStore {
|
|||
}
|
||||
|
||||
private StoreFactory getStoreFactory() {
|
||||
if (this.storeFactory == null) {
|
||||
this.storeFactory = this.session.getProvider(StoreFactory.class);
|
||||
}
|
||||
|
||||
return this.storeFactory;
|
||||
}
|
||||
|
||||
|
@ -216,6 +194,9 @@ public class CachedPolicyStore implements PolicyStore {
|
|||
private Policy createAdapter(CachedPolicy cached) {
|
||||
return new Policy() {
|
||||
|
||||
private Set<Scope> scopes;
|
||||
private Set<Resource> resources;
|
||||
private Set<Policy> associatedPolicies;
|
||||
private Policy updated;
|
||||
|
||||
@Override
|
||||
|
@ -285,54 +266,56 @@ public class CachedPolicyStore implements PolicyStore {
|
|||
|
||||
@Override
|
||||
public ResourceServer getResourceServer() {
|
||||
return getStoreFactory().getResourceServerStore().findById(cached.getResourceServerId());
|
||||
return getCachedStoreFactory().getResourceServerStore().findById(cached.getResourceServerId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addScope(Scope scope) {
|
||||
getDelegateForUpdate().addScope(getStoreFactory().getScopeStore().findById(scope.getId()));
|
||||
getDelegateForUpdate().addScope(getStoreFactory().getScopeStore().findById(scope.getId(), cached.getResourceServerId()));
|
||||
cached.addScope(scope);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeScope(Scope scope) {
|
||||
getDelegateForUpdate().removeScope(getStoreFactory().getScopeStore().findById(scope.getId()));
|
||||
getDelegateForUpdate().removeScope(scope);
|
||||
cached.removeScope(scope);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAssociatedPolicy(Policy associatedPolicy) {
|
||||
getDelegateForUpdate().addAssociatedPolicy(getStoreFactory().getPolicyStore().findById(associatedPolicy.getId()));
|
||||
getDelegateForUpdate().addAssociatedPolicy(getStoreFactory().getPolicyStore().findById(associatedPolicy.getId(), cached.getResourceServerId()));
|
||||
cached.addAssociatedPolicy(associatedPolicy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAssociatedPolicy(Policy associatedPolicy) {
|
||||
getDelegateForUpdate().removeAssociatedPolicy(getStoreFactory().getPolicyStore().findById(associatedPolicy.getId()));
|
||||
getDelegateForUpdate().removeAssociatedPolicy(getStoreFactory().getPolicyStore().findById(associatedPolicy.getId(), cached.getResourceServerId()));
|
||||
cached.removeAssociatedPolicy(associatedPolicy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addResource(Resource resource) {
|
||||
getDelegateForUpdate().addResource(getStoreFactory().getResourceStore().findById(resource.getId()));
|
||||
getDelegateForUpdate().addResource(getStoreFactory().getResourceStore().findById(resource.getId(), cached.getResourceServerId()));
|
||||
cached.addResource(resource);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeResource(Resource resource) {
|
||||
getDelegateForUpdate().removeResource(getStoreFactory().getResourceStore().findById(resource.getId()));
|
||||
getDelegateForUpdate().removeResource(getStoreFactory().getResourceStore().findById(resource.getId(), cached.getResourceServerId()));
|
||||
cached.removeResource(resource);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Policy> getAssociatedPolicies() {
|
||||
Set<Policy> associatedPolicies = new HashSet<>();
|
||||
if (associatedPolicies == null) {
|
||||
associatedPolicies = new HashSet<>();
|
||||
|
||||
for (String id : cached.getAssociatedPoliciesIds()) {
|
||||
Policy cached = findById(id);
|
||||
Policy policy = findById(id, cached.getResourceServerId());
|
||||
|
||||
if (cached != null) {
|
||||
associatedPolicies.add(cached);
|
||||
if (policy != null) {
|
||||
associatedPolicies.add(policy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -341,13 +324,15 @@ public class CachedPolicyStore implements PolicyStore {
|
|||
|
||||
@Override
|
||||
public Set<Resource> getResources() {
|
||||
Set<Resource> resources = new HashSet<>();
|
||||
if (resources == null) {
|
||||
resources = new HashSet<>();
|
||||
|
||||
for (String id : cached.getResourcesIds()) {
|
||||
Resource cached = getStoreFactory().getResourceStore().findById(id);
|
||||
Resource resource = getCachedStoreFactory().getResourceStore().findById(id, cached.getResourceServerId());
|
||||
|
||||
if (cached != null) {
|
||||
resources.add(cached);
|
||||
if (resource != null) {
|
||||
resources.add(resource);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -356,13 +341,15 @@ public class CachedPolicyStore implements PolicyStore {
|
|||
|
||||
@Override
|
||||
public Set<Scope> getScopes() {
|
||||
Set<Scope> scopes = new HashSet<>();
|
||||
if (scopes == null) {
|
||||
scopes = new HashSet<>();
|
||||
|
||||
for (String id : cached.getScopesIds()) {
|
||||
Scope cached = getStoreFactory().getScopeStore().findById(id);
|
||||
Scope scope = getCachedStoreFactory().getScopeStore().findById(id, cached.getResourceServerId());
|
||||
|
||||
if (cached != null) {
|
||||
scopes.add(cached);
|
||||
if (scope != null) {
|
||||
scopes.add(scope);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -392,9 +379,12 @@ public class CachedPolicyStore implements PolicyStore {
|
|||
|
||||
private Policy getDelegateForUpdate() {
|
||||
if (this.updated == null) {
|
||||
this.updated = getDelegate().findById(getId());
|
||||
this.updated = getDelegate().findById(getId(), cached.getResourceServerId());
|
||||
if (this.updated == null) throw new IllegalStateException("Not found in database");
|
||||
transaction.whenCommit(() -> cache.remove(getCacheKeyForPolicy(getId())));
|
||||
transaction.whenCommit(() -> {
|
||||
cache.remove(getCacheKeyForPolicy(getId()));
|
||||
invalidateCache(cached.getResourceServerId());
|
||||
});
|
||||
}
|
||||
|
||||
return this.updated;
|
||||
|
@ -402,9 +392,16 @@ public class CachedPolicyStore implements PolicyStore {
|
|||
};
|
||||
}
|
||||
|
||||
private CachedStoreFactoryProvider getCachedStoreFactory() {
|
||||
if (cachedStoreFactory == null) {
|
||||
cachedStoreFactory = session.getProvider(CachedStoreFactoryProvider.class);
|
||||
}
|
||||
return cachedStoreFactory;
|
||||
}
|
||||
|
||||
private CachedPolicy updatePolicyCache(Policy policy) {
|
||||
CachedPolicy cached = new CachedPolicy(policy);
|
||||
List<Policy> cache = new ArrayList<>();
|
||||
List<CachedPolicy> cache = new ArrayList<>();
|
||||
|
||||
cache.add(cached);
|
||||
|
||||
|
@ -413,4 +410,25 @@ public class CachedPolicyStore implements PolicyStore {
|
|||
return cached;
|
||||
}
|
||||
|
||||
private void invalidateCache(String resourceServerId) {
|
||||
cacheKeys.forEach(cacheKey -> cache.keySet().stream().filter(key -> key.startsWith(cacheKey + resourceServerId)).forEach(cache::remove));
|
||||
}
|
||||
|
||||
private List<Policy> cacheResult(String key, Supplier<List<Policy>> provider) {
|
||||
List<CachedPolicy> cached = cache.computeIfAbsent(key, (Function<String, List<CachedPolicy>>) o -> {
|
||||
List<Policy> result = provider.get();
|
||||
|
||||
if (result.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return result.stream().map(policy -> new CachedPolicy(policy)).collect(Collectors.toList());
|
||||
});
|
||||
|
||||
if (cached == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return cached.stream().map(cachedPolicy -> createAdapter(cachedPolicy)).collect(Collectors.toList());
|
||||
}
|
||||
}
|
|
@ -18,6 +18,10 @@
|
|||
|
||||
package org.keycloak.models.authorization.infinispan;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.infinispan.Cache;
|
||||
import org.keycloak.authorization.model.ResourceServer;
|
||||
import org.keycloak.authorization.store.ResourceServerStore;
|
||||
|
@ -28,16 +32,13 @@ import org.keycloak.models.authorization.infinispan.InfinispanStoreFactoryProvid
|
|||
import org.keycloak.models.authorization.infinispan.entities.CachedResourceServer;
|
||||
import org.keycloak.representations.idm.authorization.PolicyEnforcementMode;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
public class CachedResourceServerStore implements ResourceServerStore {
|
||||
|
||||
private static final String RS_ID_CACHE_PREFIX = "rs-id-";
|
||||
private static final String RS_CLIENT_ID_CACHE_PREFIX = "rs-client-id-";
|
||||
|
||||
private final KeycloakSession session;
|
||||
private final CacheTransaction transaction;
|
||||
|
@ -45,11 +46,12 @@ public class CachedResourceServerStore implements ResourceServerStore {
|
|||
private ResourceServerStore delegate;
|
||||
private final Cache<String, List> cache;
|
||||
|
||||
public CachedResourceServerStore(KeycloakSession session, CacheTransaction transaction) {
|
||||
public CachedResourceServerStore(KeycloakSession session, CacheTransaction transaction, StoreFactory storeFactory) {
|
||||
this.session = session;
|
||||
this.transaction = transaction;
|
||||
InfinispanConnectionProvider provider = session.getProvider(InfinispanConnectionProvider.class);
|
||||
this.cache = provider.getCache(InfinispanConnectionProvider.AUTHORIZATION_CACHE_NAME);
|
||||
this.storeFactory = storeFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -64,7 +66,14 @@ public class CachedResourceServerStore implements ResourceServerStore {
|
|||
@Override
|
||||
public void delete(String id) {
|
||||
getDelegate().delete(id);
|
||||
this.transaction.whenCommit(() -> this.cache.remove(getCacheKeyForResourceServer(id)));
|
||||
this.transaction.whenCommit(() -> {
|
||||
List<CachedResourceServer> servers = cache.remove(getCacheKeyForResourceServer(id));
|
||||
|
||||
if (servers != null) {
|
||||
CachedResourceServer entry = servers.get(0);
|
||||
cache.remove(getCacheKeyForResourceServerClientId(entry.getClientId()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -87,32 +96,31 @@ public class CachedResourceServerStore implements ResourceServerStore {
|
|||
|
||||
@Override
|
||||
public ResourceServer findByClient(String id) {
|
||||
for (Map.Entry entry : this.cache.entrySet()) {
|
||||
String cacheKey = (String) entry.getKey();
|
||||
|
||||
if (cacheKey.startsWith(RS_ID_CACHE_PREFIX)) {
|
||||
List<ResourceServer> cache = (List<ResourceServer>) entry.getValue();
|
||||
ResourceServer resourceServer = cache.get(0);
|
||||
|
||||
if (resourceServer.getClientId().equals(id)) {
|
||||
return findById(resourceServer.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
String cacheKeyForResourceServer = getCacheKeyForResourceServerClientId(id);
|
||||
List<String> cached = this.cache.get(cacheKeyForResourceServer);
|
||||
|
||||
if (cached == null) {
|
||||
ResourceServer resourceServer = getDelegate().findByClient(id);
|
||||
|
||||
if (resourceServer != null) {
|
||||
return findById(updateResourceServerCache(resourceServer).getId());
|
||||
cache.put(cacheKeyForResourceServer, Arrays.asList(resourceServer.getId()));
|
||||
return findById(resourceServer.getId());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return findById(cached.get(0));
|
||||
}
|
||||
|
||||
private String getCacheKeyForResourceServer(String id) {
|
||||
return RS_ID_CACHE_PREFIX + id;
|
||||
}
|
||||
|
||||
private String getCacheKeyForResourceServerClientId(String id) {
|
||||
return RS_CLIENT_ID_CACHE_PREFIX + id;
|
||||
}
|
||||
|
||||
private ResourceServerStore getDelegate() {
|
||||
if (this.delegate == null) {
|
||||
this.delegate = getStoreFactory().getResourceServerStore();
|
||||
|
@ -122,10 +130,6 @@ public class CachedResourceServerStore implements ResourceServerStore {
|
|||
}
|
||||
|
||||
private StoreFactory getStoreFactory() {
|
||||
if (this.storeFactory == null) {
|
||||
this.storeFactory = session.getProvider(StoreFactory.class);
|
||||
}
|
||||
|
||||
return this.storeFactory;
|
||||
}
|
||||
private ResourceServer createAdapter(ResourceServer cached) {
|
||||
|
|
|
@ -18,6 +18,16 @@
|
|||
|
||||
package org.keycloak.models.authorization.infinispan;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.infinispan.Cache;
|
||||
import org.keycloak.authorization.model.Resource;
|
||||
import org.keycloak.authorization.model.ResourceServer;
|
||||
|
@ -28,14 +38,7 @@ import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
|
|||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.authorization.infinispan.InfinispanStoreFactoryProvider.CacheTransaction;
|
||||
import org.keycloak.models.authorization.infinispan.entities.CachedResource;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import org.keycloak.models.cache.authorization.CachedStoreFactoryProvider;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
|
@ -43,60 +46,63 @@ import java.util.stream.Collectors;
|
|||
public class CachedResourceStore implements ResourceStore {
|
||||
|
||||
private static final String RESOURCE_ID_CACHE_PREFIX = "rsc-id-";
|
||||
private static final String RESOURCE_OWNER_CACHE_PREFIX = "rsc-owner-";
|
||||
private static final String RESOURCE_NAME_CACHE_PREFIX = "rsc-name-";
|
||||
|
||||
private final KeycloakSession session;
|
||||
private final CacheTransaction transaction;
|
||||
private final List<String> cacheKeys;
|
||||
private StoreFactory storeFactory;
|
||||
private ResourceStore delegate;
|
||||
private final Cache<String, List> cache;
|
||||
private final Cache<String, List<CachedResource>> cache;
|
||||
|
||||
public CachedResourceStore(KeycloakSession session, CacheTransaction transaction) {
|
||||
public CachedResourceStore(KeycloakSession session, CacheTransaction transaction, StoreFactory storeFactory) {
|
||||
this.session = session;
|
||||
InfinispanConnectionProvider provider = session.getProvider(InfinispanConnectionProvider.class);
|
||||
this.cache = provider.getCache(InfinispanConnectionProvider.AUTHORIZATION_CACHE_NAME);
|
||||
this.transaction = transaction;
|
||||
cacheKeys = new ArrayList<>();
|
||||
cacheKeys.add("findByOwner");
|
||||
this.storeFactory = storeFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Resource create(String name, ResourceServer resourceServer, String owner) {
|
||||
Resource resource = getDelegate().create(name, getStoreFactory().getResourceServerStore().findById(resourceServer.getId()), owner);
|
||||
|
||||
this.transaction.whenRollback(() -> cache.remove(getCacheKeyForResource(resource.getId())));
|
||||
this.transaction.whenRollback(() -> {
|
||||
cache.remove(getCacheKeyForResource(resource.getId()));
|
||||
invalidateCache(resourceServer.getId());
|
||||
});
|
||||
|
||||
return createAdapter(new CachedResource(resource));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(String id) {
|
||||
List<CachedResource> removed = this.cache.remove(getCacheKeyForResource(id));
|
||||
|
||||
if (removed != null) {
|
||||
CachedResource cachedResource = removed.get(0);
|
||||
List<String> byOwner = this.cache.get(getResourceOwnerCacheKey(cachedResource.getOwner()));
|
||||
|
||||
if (byOwner != null) {
|
||||
byOwner.remove(id);
|
||||
|
||||
if (byOwner.isEmpty()) {
|
||||
this.cache.remove(getResourceOwnerCacheKey(cachedResource.getOwner()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Resource resource = findById(id, null);
|
||||
ResourceServer resourceServer = resource.getResourceServer();
|
||||
getDelegate().delete(id);
|
||||
this.transaction.whenCommit(() -> {
|
||||
List<CachedResource> resources = cache.remove(getCacheKeyForResource(id));
|
||||
|
||||
if (resources != null) {
|
||||
CachedResource entry = resources.get(0);
|
||||
cache.remove(getCacheKeyForResourceName(entry.getName(), entry.getResourceServerId()));
|
||||
}
|
||||
|
||||
invalidateCache(resourceServer.getId());
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Resource findById(String id) {
|
||||
public Resource findById(String id, String resourceServerId) {
|
||||
String cacheKeyForResource = getCacheKeyForResource(id);
|
||||
List<CachedResource> cached = this.cache.get(cacheKeyForResource);
|
||||
|
||||
if (cached == null) {
|
||||
Resource resource = getDelegate().findById(id);
|
||||
Resource resource = getDelegate().findById(id, resourceServerId);
|
||||
|
||||
if (resource != null) {
|
||||
updateCachedIds(getResourceOwnerCacheKey(resource.getOwner()), resource, false);
|
||||
return createAdapter(updateResourceCache(resource));
|
||||
}
|
||||
|
||||
|
@ -107,20 +113,13 @@ public class CachedResourceStore implements ResourceStore {
|
|||
}
|
||||
|
||||
@Override
|
||||
public List<Resource> findByOwner(String ownerId) {
|
||||
|
||||
for (Resource resource : getDelegate().findByOwner(ownerId)) {
|
||||
updateCachedIds(getResourceOwnerCacheKey(ownerId), resource, true);
|
||||
}
|
||||
|
||||
return ((List<String>) this.cache.getOrDefault(getResourceOwnerCacheKey(ownerId), Collections.emptyList())).stream().map(this::findById)
|
||||
.filter(resource -> resource != null)
|
||||
.collect(Collectors.toList());
|
||||
public List<Resource> findByOwner(String ownerId, String resourceServerId) {
|
||||
return cacheResult(new StringBuilder("findByOwner").append(resourceServerId).append(ownerId).toString(), () -> getDelegate().findByOwner(ownerId, resourceServerId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Resource> findByResourceServer(String resourceServerId) {
|
||||
return getDelegate().findByResourceServer(resourceServerId).stream().map(resource -> findById(resource.getId())).collect(Collectors.toList());
|
||||
return getDelegate().findByResourceServer(resourceServerId);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -129,43 +128,42 @@ public class CachedResourceStore implements ResourceStore {
|
|||
}
|
||||
|
||||
@Override
|
||||
public List<Resource> findByScope(String... id) {
|
||||
return getDelegate().findByScope(id).stream().map(resource -> findById(resource.getId())).collect(Collectors.toList());
|
||||
public List<Resource> findByScope(List<String> id, String resourceServerId) {
|
||||
return getDelegate().findByScope(id, resourceServerId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Resource findByName(String name, String resourceServerId) {
|
||||
for (Entry entry : this.cache.entrySet()) {
|
||||
String cacheKey = (String) entry.getKey();
|
||||
|
||||
if (cacheKey.startsWith(RESOURCE_ID_CACHE_PREFIX)) {
|
||||
List<CachedResource> value = (List<CachedResource>) entry.getValue();
|
||||
CachedResource resource = value.get(0);
|
||||
|
||||
if (resource.getResourceServerId().equals(resourceServerId) && resource.getName().equals(name)) {
|
||||
return findById(resource.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
String cacheKeyForResource = getCacheKeyForResourceName(name, resourceServerId);
|
||||
List<CachedResource> cached = this.cache.get(cacheKeyForResource);
|
||||
|
||||
if (cached == null) {
|
||||
Resource resource = getDelegate().findByName(name, resourceServerId);
|
||||
|
||||
if (resource != null) {
|
||||
return findById(updateResourceCache(resource).getId());
|
||||
cache.put(cacheKeyForResource, Arrays.asList(new CachedResource(resource)));
|
||||
return findById(resource.getId(), resourceServerId);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return createAdapter(cached.get(0));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Resource> findByType(String type) {
|
||||
return getDelegate().findByType(type).stream().map(resource -> findById(resource.getId())).collect(Collectors.toList());
|
||||
public List<Resource> findByType(String type, String resourceServerId) {
|
||||
return getDelegate().findByType(type, resourceServerId);
|
||||
}
|
||||
|
||||
private String getCacheKeyForResource(String id) {
|
||||
return RESOURCE_ID_CACHE_PREFIX + id;
|
||||
}
|
||||
|
||||
private String getCacheKeyForResourceName(String name, String resourceServerId) {
|
||||
return RESOURCE_NAME_CACHE_PREFIX + name + "-" + resourceServerId;
|
||||
}
|
||||
|
||||
private ResourceStore getDelegate() {
|
||||
if (this.delegate == null) {
|
||||
this.delegate = getStoreFactory().getResourceStore();
|
||||
|
@ -175,10 +173,6 @@ public class CachedResourceStore implements ResourceStore {
|
|||
}
|
||||
|
||||
private StoreFactory getStoreFactory() {
|
||||
if (this.storeFactory == null) {
|
||||
this.storeFactory = session.getProvider(StoreFactory.class);
|
||||
}
|
||||
|
||||
return this.storeFactory;
|
||||
}
|
||||
|
||||
|
@ -228,13 +222,15 @@ public class CachedResourceStore implements ResourceStore {
|
|||
|
||||
@Override
|
||||
public List<Scope> getScopes() {
|
||||
List<Scope> scopes = new ArrayList<>();
|
||||
if (scopes == null) {
|
||||
scopes = new ArrayList<>();
|
||||
|
||||
for (String id : cached.getScopesIds()) {
|
||||
Scope cached = getStoreFactory().getScopeStore().findById(id);
|
||||
Scope scope = getCachedStoreFactory().getScopeStore().findById(id, cached.getResourceServerId());
|
||||
|
||||
if (cached != null) {
|
||||
scopes.add(cached);
|
||||
if (scope != null) {
|
||||
scopes.add(scope);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -254,7 +250,7 @@ public class CachedResourceStore implements ResourceStore {
|
|||
|
||||
@Override
|
||||
public ResourceServer getResourceServer() {
|
||||
return getStoreFactory().getResourceServerStore().findById(cached.getResourceServerId());
|
||||
return getCachedStoreFactory().getResourceServerStore().findById(cached.getResourceServerId());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -264,15 +260,19 @@ public class CachedResourceStore implements ResourceStore {
|
|||
|
||||
@Override
|
||||
public void updateScopes(Set<Scope> scopes) {
|
||||
getDelegateForUpdate().updateScopes(scopes.stream().map(scope -> getStoreFactory().getScopeStore().findById(scope.getId())).collect(Collectors.toSet()));
|
||||
getDelegateForUpdate().updateScopes(scopes.stream().map(scope -> getStoreFactory().getScopeStore().findById(scope.getId(), cached.getResourceServerId())).collect(Collectors.toSet()));
|
||||
cached.updateScopes(scopes);
|
||||
}
|
||||
|
||||
private Resource getDelegateForUpdate() {
|
||||
if (this.updated == null) {
|
||||
this.updated = getDelegate().findById(getId());
|
||||
this.updated = getDelegate().findById(getId(), cached.getResourceServerId());
|
||||
if (this.updated == null) throw new IllegalStateException("Not found in database");
|
||||
transaction.whenCommit(() -> cache.remove(getCacheKeyForResource(getId())));
|
||||
transaction.whenRollback(() -> {
|
||||
cache.remove(getCacheKeyForResource(cached.getId()));
|
||||
invalidateCache(cached.getResourceServerId());
|
||||
getCachedStoreFactory().getPolicyStore().notifyChange(cached);
|
||||
});
|
||||
}
|
||||
|
||||
return this.updated;
|
||||
|
@ -280,6 +280,10 @@ public class CachedResourceStore implements ResourceStore {
|
|||
};
|
||||
}
|
||||
|
||||
private CachedStoreFactoryProvider getCachedStoreFactory() {
|
||||
return session.getProvider(CachedStoreFactoryProvider.class);
|
||||
}
|
||||
|
||||
private CachedResource updateResourceCache(Resource resource) {
|
||||
CachedResource cached = new CachedResource(resource);
|
||||
List cache = new ArrayList<>();
|
||||
|
@ -291,23 +295,25 @@ public class CachedResourceStore implements ResourceStore {
|
|||
return cached;
|
||||
}
|
||||
|
||||
private void updateCachedIds(String cacheKey, Resource resource, boolean create) {
|
||||
List<String> cached = this.cache.get(cacheKey);
|
||||
private List<Resource> cacheResult(String key, Supplier<List<Resource>> provider) {
|
||||
List<CachedResource> cached = cache.computeIfAbsent(key, (Function<String, List<CachedResource>>) o -> {
|
||||
List<Resource> result = provider.get();
|
||||
|
||||
if (result.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return result.stream().map(resource -> new CachedResource(resource)).collect(Collectors.toList());
|
||||
});
|
||||
|
||||
if (cached == null) {
|
||||
if (!create) {
|
||||
return;
|
||||
}
|
||||
cached = new ArrayList<>();
|
||||
this.cache.put(cacheKey, cached);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
if (cached != null && !cached.contains(resource.getId())) {
|
||||
cached.add(resource.getId());
|
||||
}
|
||||
return cached.stream().map(this::createAdapter).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private String getResourceOwnerCacheKey(String ownerId) {
|
||||
return RESOURCE_OWNER_CACHE_PREFIX + ownerId;
|
||||
private void invalidateCache(String resourceServerId) {
|
||||
cacheKeys.forEach(cacheKey -> cache.keySet().stream().filter(key -> key.startsWith(cacheKey + resourceServerId)).forEach(cache::remove));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,11 @@
|
|||
|
||||
package org.keycloak.models.authorization.infinispan;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.infinispan.Cache;
|
||||
import org.keycloak.authorization.model.ResourceServer;
|
||||
import org.keycloak.authorization.model.Scope;
|
||||
|
@ -27,11 +32,7 @@ import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
|
|||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.authorization.infinispan.InfinispanStoreFactoryProvider.CacheTransaction;
|
||||
import org.keycloak.models.authorization.infinispan.entities.CachedScope;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import org.keycloak.models.cache.authorization.CachedStoreFactoryProvider;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
|
@ -39,6 +40,7 @@ import java.util.Map.Entry;
|
|||
public class CachedScopeStore implements ScopeStore {
|
||||
|
||||
private static final String SCOPE_ID_CACHE_PREFIX = "scp-id-";
|
||||
private static final String SCOPE_NAME_CACHE_PREFIX = "scp-name-";
|
||||
|
||||
private final Cache<String, List> cache;
|
||||
private final KeycloakSession session;
|
||||
|
@ -46,11 +48,12 @@ public class CachedScopeStore implements ScopeStore {
|
|||
private ScopeStore delegate;
|
||||
private StoreFactory storeFactory;
|
||||
|
||||
public CachedScopeStore(KeycloakSession session, CacheTransaction transaction) {
|
||||
public CachedScopeStore(KeycloakSession session, CacheTransaction transaction, StoreFactory storeFactory) {
|
||||
this.session = session;
|
||||
this.transaction = transaction;
|
||||
InfinispanConnectionProvider provider = session.getProvider(InfinispanConnectionProvider.class);
|
||||
this.cache = provider.getCache(InfinispanConnectionProvider.AUTHORIZATION_CACHE_NAME);
|
||||
this.storeFactory = storeFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -65,16 +68,23 @@ public class CachedScopeStore implements ScopeStore {
|
|||
@Override
|
||||
public void delete(String id) {
|
||||
getDelegate().delete(id);
|
||||
this.transaction.whenCommit(() -> cache.remove(getCacheKeyForScope(id)));
|
||||
this.transaction.whenCommit(() -> {
|
||||
List<CachedScope> scopes = cache.remove(getCacheKeyForScope(id));
|
||||
|
||||
if (scopes != null) {
|
||||
CachedScope entry = scopes.get(0);
|
||||
cache.remove(getCacheKeyForScopeName(entry.getName(), entry.getResourceServerId()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Scope findById(String id) {
|
||||
public Scope findById(String id, String resourceServerId) {
|
||||
String cacheKeyForScope = getCacheKeyForScope(id);
|
||||
List<CachedScope> cached = this.cache.get(cacheKeyForScope);
|
||||
|
||||
if (cached == null) {
|
||||
Scope scope = getDelegate().findById(id);
|
||||
Scope scope = getDelegate().findById(id, resourceServerId);
|
||||
|
||||
if (scope != null) {
|
||||
return createAdapter(updateScopeCache(scope));
|
||||
|
@ -88,28 +98,23 @@ public class CachedScopeStore implements ScopeStore {
|
|||
|
||||
@Override
|
||||
public Scope findByName(String name, String resourceServerId) {
|
||||
for (Entry entry : this.cache.entrySet()) {
|
||||
String cacheKey = (String) entry.getKey();
|
||||
|
||||
if (cacheKey.startsWith(SCOPE_ID_CACHE_PREFIX)) {
|
||||
List<CachedScope> cache = (List<CachedScope>) entry.getValue();
|
||||
CachedScope scope = cache.get(0);
|
||||
|
||||
if (scope.getResourceServerId().equals(resourceServerId) && scope.getName().equals(name)) {
|
||||
return findById(scope.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
String cacheKeyForScope = getCacheKeyForScopeName(name, resourceServerId);
|
||||
List<String> cached = this.cache.get(cacheKeyForScope);
|
||||
|
||||
if (cached == null) {
|
||||
Scope scope = getDelegate().findByName(name, resourceServerId);
|
||||
|
||||
if (scope != null) {
|
||||
return findById(updateScopeCache(scope).getId());
|
||||
cache.put(cacheKeyForScope, Arrays.asList(scope.getId()));
|
||||
return findById(scope.getId(), resourceServerId);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return findById(cached.get(0), resourceServerId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Scope> findByResourceServer(String id) {
|
||||
return getDelegate().findByResourceServer(id);
|
||||
|
@ -124,6 +129,10 @@ public class CachedScopeStore implements ScopeStore {
|
|||
return SCOPE_ID_CACHE_PREFIX + id;
|
||||
}
|
||||
|
||||
private String getCacheKeyForScopeName(String name, String resourceServerId) {
|
||||
return SCOPE_NAME_CACHE_PREFIX + name + "-" + resourceServerId;
|
||||
}
|
||||
|
||||
private ScopeStore getDelegate() {
|
||||
if (this.delegate == null) {
|
||||
this.delegate = getStoreFactory().getScopeStore();
|
||||
|
@ -133,10 +142,6 @@ public class CachedScopeStore implements ScopeStore {
|
|||
}
|
||||
|
||||
private StoreFactory getStoreFactory() {
|
||||
if (this.storeFactory == null) {
|
||||
this.storeFactory = session.getProvider(StoreFactory.class);
|
||||
}
|
||||
|
||||
return this.storeFactory;
|
||||
}
|
||||
|
||||
|
@ -174,14 +179,17 @@ public class CachedScopeStore implements ScopeStore {
|
|||
|
||||
@Override
|
||||
public ResourceServer getResourceServer() {
|
||||
return getStoreFactory().getResourceServerStore().findById(cached.getResourceServerId());
|
||||
return getCachedStoreFactory().getResourceServerStore().findById(cached.getResourceServerId());
|
||||
}
|
||||
|
||||
private Scope getDelegateForUpdate() {
|
||||
if (this.updated == null) {
|
||||
this.updated = getDelegate().findById(getId());
|
||||
this.updated = getDelegate().findById(getId(), cached.getResourceServerId());
|
||||
if (this.updated == null) throw new IllegalStateException("Not found in database");
|
||||
transaction.whenCommit(() -> cache.remove(getCacheKeyForScope(getId())));
|
||||
transaction.whenCommit(() -> {
|
||||
cache.remove(getCacheKeyForScope(getId()));
|
||||
getCachedStoreFactory().getPolicyStore().notifyChange(updated);
|
||||
});
|
||||
}
|
||||
|
||||
return this.updated;
|
||||
|
@ -189,6 +197,10 @@ public class CachedScopeStore implements ScopeStore {
|
|||
};
|
||||
}
|
||||
|
||||
private CachedStoreFactoryProvider getCachedStoreFactory() {
|
||||
return session.getProvider(CachedStoreFactoryProvider.class);
|
||||
}
|
||||
|
||||
private CachedScope updateScopeCache(Scope scope) {
|
||||
CachedScope cached = new CachedScope(scope);
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ 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.KeycloakTransaction;
|
||||
import org.keycloak.models.cache.authorization.CachedStoreFactoryProvider;
|
||||
|
@ -36,31 +37,41 @@ public class InfinispanStoreFactoryProvider implements CachedStoreFactoryProvide
|
|||
|
||||
private final KeycloakSession session;
|
||||
private final CacheTransaction transaction;
|
||||
private final StoreFactory storeFactory;
|
||||
private final CachedResourceStore resourceStore;
|
||||
private final CachedScopeStore scopeStore;
|
||||
private final CachedPolicyStore policyStore;
|
||||
private ResourceServerStore resourceServerStore;
|
||||
|
||||
InfinispanStoreFactoryProvider(KeycloakSession delegate) {
|
||||
this.session = delegate;
|
||||
this.transaction = new CacheTransaction();
|
||||
this.session.getTransactionManager().enlistAfterCompletion(transaction);
|
||||
storeFactory = this.session.getProvider(StoreFactory.class);
|
||||
resourceStore = new CachedResourceStore(this.session, this.transaction, storeFactory);
|
||||
resourceServerStore = new CachedResourceServerStore(this.session, this.transaction, storeFactory);
|
||||
scopeStore = new CachedScopeStore(this.session, this.transaction, storeFactory);
|
||||
policyStore = new CachedPolicyStore(this.session, this.transaction, storeFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceStore getResourceStore() {
|
||||
return new CachedResourceStore(this.session, this.transaction);
|
||||
return resourceStore;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceServerStore getResourceServerStore() {
|
||||
return new CachedResourceServerStore(this.session, this.transaction);
|
||||
return resourceServerStore;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScopeStore getScopeStore() {
|
||||
return new CachedScopeStore(this.session, this.transaction);
|
||||
return scopeStore;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PolicyStore getPolicyStore() {
|
||||
return new CachedPolicyStore(this.session, this.transaction);
|
||||
return policyStore;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -18,11 +18,10 @@
|
|||
|
||||
package org.keycloak.authorization.jpa.entities;
|
||||
|
||||
import org.keycloak.authorization.model.Policy;
|
||||
import org.keycloak.authorization.model.Resource;
|
||||
import org.keycloak.authorization.model.Scope;
|
||||
import org.keycloak.representations.idm.authorization.DecisionStrategy;
|
||||
import org.keycloak.representations.idm.authorization.Logic;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.persistence.Access;
|
||||
import javax.persistence.AccessType;
|
||||
|
@ -34,15 +33,17 @@ import javax.persistence.FetchType;
|
|||
import javax.persistence.Id;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.JoinTable;
|
||||
import javax.persistence.ManyToMany;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.MapKeyColumn;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.UniqueConstraint;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.keycloak.authorization.model.Policy;
|
||||
import org.keycloak.authorization.model.Resource;
|
||||
import org.keycloak.authorization.model.Scope;
|
||||
import org.keycloak.representations.idm.authorization.DecisionStrategy;
|
||||
import org.keycloak.representations.idm.authorization.Logic;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
|
@ -79,19 +80,19 @@ public class PolicyEntity implements Policy {
|
|||
@CollectionTable(name="POLICY_CONFIG", joinColumns={ @JoinColumn(name="POLICY_ID") })
|
||||
private Map<String, String> config = new HashMap();
|
||||
|
||||
@ManyToOne(optional = false)
|
||||
@ManyToOne(optional = false, fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "RESOURCE_SERVER_ID")
|
||||
private ResourceServerEntity resourceServer;
|
||||
|
||||
@ManyToMany(fetch = FetchType.LAZY, cascade = {})
|
||||
@OneToMany(fetch = FetchType.LAZY, cascade = {})
|
||||
@JoinTable(name = "ASSOCIATED_POLICY", joinColumns = @JoinColumn(name = "POLICY_ID"), inverseJoinColumns = @JoinColumn(name = "ASSOCIATED_POLICY_ID"))
|
||||
private Set<PolicyEntity> associatedPolicies = new HashSet<>();
|
||||
|
||||
@ManyToMany(fetch = FetchType.LAZY, cascade = {})
|
||||
@OneToMany(fetch = FetchType.LAZY, cascade = {})
|
||||
@JoinTable(name = "RESOURCE_POLICY", joinColumns = @JoinColumn(name = "POLICY_ID"), inverseJoinColumns = @JoinColumn(name = "RESOURCE_ID"))
|
||||
private Set<ResourceEntity> resources = new HashSet<>();
|
||||
|
||||
@ManyToMany(fetch = FetchType.EAGER, cascade = {})
|
||||
@OneToMany(fetch = FetchType.EAGER, cascade = {})
|
||||
@JoinTable(name = "SCOPE_POLICY", joinColumns = @JoinColumn(name = "POLICY_ID"), inverseJoinColumns = @JoinColumn(name = "SCOPE_ID"))
|
||||
private Set<ScopeEntity> scopes = new HashSet<>();
|
||||
|
||||
|
|
|
@ -67,7 +67,7 @@ public class ResourceEntity implements Resource {
|
|||
@Column(name = "OWNER")
|
||||
private String owner;
|
||||
|
||||
@ManyToOne(optional = false)
|
||||
@ManyToOne(optional = false, fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "RESOURCE_SERVER_ID")
|
||||
private ResourceServerEntity resourceServer;
|
||||
|
||||
|
|
|
@ -57,7 +57,7 @@ public class ScopeEntity implements Scope {
|
|||
@Column(name = "ICON_URI")
|
||||
private String iconUri;
|
||||
|
||||
@ManyToOne(optional = false)
|
||||
@ManyToOne(optional = false, fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "RESOURCE_SERVER_ID")
|
||||
private ResourceServerEntity resourceServer;
|
||||
|
||||
|
|
|
@ -19,8 +19,10 @@ package org.keycloak.authorization.jpa.store;
|
|||
|
||||
import org.keycloak.authorization.jpa.entities.PolicyEntity;
|
||||
import org.keycloak.authorization.jpa.entities.ResourceServerEntity;
|
||||
import org.keycloak.authorization.jpa.entities.ScopeEntity;
|
||||
import org.keycloak.authorization.model.Policy;
|
||||
import org.keycloak.authorization.model.ResourceServer;
|
||||
import org.keycloak.authorization.model.Scope;
|
||||
import org.keycloak.authorization.store.PolicyStore;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
|
||||
|
@ -68,17 +70,30 @@ public class JPAPolicyStore implements PolicyStore {
|
|||
|
||||
@Override
|
||||
public void delete(String id) {
|
||||
Policy policy = findById(id);
|
||||
Policy policy = entityManager.find(PolicyEntity.class, id);
|
||||
|
||||
if (policy != null) {
|
||||
getEntityManager().remove(policy);
|
||||
this.entityManager.remove(policy);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Policy findById(String id) {
|
||||
return getEntityManager().find(PolicyEntity.class, id);
|
||||
public Policy findById(String id, String resourceServerId) {
|
||||
if (id == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (resourceServerId == null) {
|
||||
return entityManager.find(PolicyEntity.class, id);
|
||||
}
|
||||
|
||||
Query query = entityManager.createQuery("from PolicyEntity where resourceServer.id = :serverId and id = :id");
|
||||
|
||||
query.setParameter("serverId", resourceServerId);
|
||||
query.setParameter("id", id);
|
||||
|
||||
return entityManager.find(PolicyEntity.class, id);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -142,32 +157,23 @@ public class JPAPolicyStore implements PolicyStore {
|
|||
}
|
||||
|
||||
@Override
|
||||
public List<Policy> findByResource(final String resourceId) {
|
||||
Query query = getEntityManager().createQuery("select p from PolicyEntity p inner join p.resources r where r.id = :resourceId");
|
||||
public List<Policy> findByResource(final String resourceId, String resourceServerId) {
|
||||
Query query = getEntityManager().createQuery("select p from PolicyEntity p inner join p.resources r where p.resourceServer.id = :serverId and (r.resourceServer.id = :serverId and r.id = :resourceId)");
|
||||
|
||||
query.setParameter("resourceId", resourceId);
|
||||
query.setParameter("serverId", resourceServerId);
|
||||
|
||||
return query.getResultList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Policy> findByResourceType(final String resourceType, String resourceServerId) {
|
||||
List<Policy> policies = new ArrayList<>();
|
||||
Query query = getEntityManager().createQuery("from PolicyEntity where resourceServer.id = :serverId");
|
||||
Query query = getEntityManager().createQuery("select p from PolicyEntity p inner join p.config c where p.resourceServer.id = :serverId and KEY(c) = 'defaultResourceType' and c = :type");
|
||||
|
||||
query.setParameter("serverId", resourceServerId);
|
||||
query.setParameter("type", resourceType);
|
||||
|
||||
List<Policy> models = query.getResultList();
|
||||
|
||||
for (Policy policy : models) {
|
||||
String defaultType = policy.getConfig().get("defaultResourceType");
|
||||
|
||||
if (defaultType != null && defaultType.equals(resourceType) && policy.getResources().isEmpty()) {
|
||||
policies.add(policy);
|
||||
}
|
||||
}
|
||||
|
||||
return policies;
|
||||
return query.getResultList();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -177,7 +183,7 @@ public class JPAPolicyStore implements PolicyStore {
|
|||
}
|
||||
|
||||
// Use separate subquery to handle DB2 and MSSSQL
|
||||
Query query = getEntityManager().createQuery("select pe from PolicyEntity pe where pe.id IN (select p.id from PolicyEntity p inner join p.scopes s where p.resourceServer.id = :serverId and s.id in (:scopeIds) and p.resources is empty group by p.id) order by pe.name");
|
||||
Query query = getEntityManager().createQuery("select pe from PolicyEntity pe where pe.resourceServer.id = :serverId and pe.id IN (select p.id from ScopeEntity s inner join s.policies p where s.resourceServer.id = :serverId and (p.resourceServer.id = :serverId and p.type = 'scope' and s.id in (:scopeIds)))");
|
||||
|
||||
query.setParameter("serverId", resourceServerId);
|
||||
query.setParameter("scopeIds", scopeIds);
|
||||
|
@ -186,19 +192,21 @@ public class JPAPolicyStore implements PolicyStore {
|
|||
}
|
||||
|
||||
@Override
|
||||
public List<Policy> findByType(String type) {
|
||||
Query query = getEntityManager().createQuery("select p from PolicyEntity p where p.type = :type");
|
||||
public List<Policy> findByType(String type, String resourceServerId) {
|
||||
Query query = getEntityManager().createQuery("select p from PolicyEntity p where p.resourceServer.id = :serverId and p.type = :type");
|
||||
|
||||
query.setParameter("serverId", resourceServerId);
|
||||
query.setParameter("type", type);
|
||||
|
||||
return query.getResultList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Policy> findDependentPolicies(String policyId) {
|
||||
Query query = getEntityManager().createQuery("select p from PolicyEntity p inner join p.associatedPolicies ap where ap.id in (:policyId)");
|
||||
public List<Policy> findDependentPolicies(String policyId, String resourceServerId) {
|
||||
Query query = getEntityManager().createQuery("select p from PolicyEntity p inner join p.associatedPolicies ap where p.resourceServer.id = :serverId and (ap.resourceServer.id = :serverId and ap.id = :policyId)");
|
||||
|
||||
query.setParameter("policyId", Arrays.asList(policyId));
|
||||
query.setParameter("serverId", resourceServerId);
|
||||
query.setParameter("policyId", policyId);
|
||||
|
||||
return query.getResultList();
|
||||
}
|
||||
|
|
|
@ -66,7 +66,7 @@ public class JPAResourceStore implements ResourceStore {
|
|||
|
||||
@Override
|
||||
public void delete(String id) {
|
||||
Resource resource = findById(id);
|
||||
Resource resource = entityManager.find(ResourceEntity.class, id);
|
||||
|
||||
resource.getScopes().clear();
|
||||
|
||||
|
@ -76,19 +76,29 @@ public class JPAResourceStore implements ResourceStore {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Resource findById(String id) {
|
||||
public Resource findById(String id, String resourceServerId) {
|
||||
if (id == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (resourceServerId == null) {
|
||||
return entityManager.find(ResourceEntity.class, id);
|
||||
}
|
||||
|
||||
Query query = entityManager.createQuery("from ResourceEntity where resourceServer.id = :serverId and id = :id");
|
||||
|
||||
query.setParameter("serverId", resourceServerId);
|
||||
query.setParameter("id", id);
|
||||
|
||||
return entityManager.find(ResourceEntity.class, id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Resource> findByOwner(String ownerId) {
|
||||
Query query = entityManager.createQuery("from ResourceEntity where owner = :ownerId");
|
||||
public List<Resource> findByOwner(String ownerId, String resourceServerId) {
|
||||
Query query = entityManager.createQuery("from ResourceEntity where resourceServer.id = :serverId and owner = :ownerId");
|
||||
|
||||
query.setParameter("ownerId", ownerId);
|
||||
query.setParameter("serverId", resourceServerId);
|
||||
|
||||
return query.getResultList();
|
||||
}
|
||||
|
@ -112,7 +122,9 @@ public class JPAResourceStore implements ResourceStore {
|
|||
predicates.add(builder.equal(root.get("resourceServer").get("id"), resourceServerId));
|
||||
|
||||
attributes.forEach((name, value) -> {
|
||||
if ("scope".equals(name)) {
|
||||
if ("id".equals(name)) {
|
||||
predicates.add(root.get(name).in(value));
|
||||
} else if ("scope".equals(name)) {
|
||||
predicates.add(root.join("scopes").get("id").in(value));
|
||||
} else {
|
||||
predicates.add(builder.like(builder.lower(root.get(name)), "%" + value[0].toLowerCase() + "%"));
|
||||
|
@ -134,10 +146,11 @@ public class JPAResourceStore implements ResourceStore {
|
|||
}
|
||||
|
||||
@Override
|
||||
public List<Resource> findByScope(String... id) {
|
||||
Query query = entityManager.createQuery("select r from ResourceEntity r inner join r.scopes s where s.id in (:scopeIds)");
|
||||
public List<Resource> findByScope(List<String> id, String resourceServerId) {
|
||||
Query query = entityManager.createQuery("select r from ResourceEntity r inner join r.scopes s where r.resourceServer.id = :serverId and (s.resourceServer.id = :serverId and s.id in (:scopeIds))");
|
||||
|
||||
query.setParameter("scopeIds", Arrays.asList(id));
|
||||
query.setParameter("scopeIds", id);
|
||||
query.setParameter("serverId", resourceServerId);
|
||||
|
||||
return query.getResultList();
|
||||
}
|
||||
|
@ -159,10 +172,11 @@ public class JPAResourceStore implements ResourceStore {
|
|||
}
|
||||
|
||||
@Override
|
||||
public List<Resource> findByType(String type) {
|
||||
Query query = entityManager.createQuery("from ResourceEntity where type = :type");
|
||||
public List<Resource> findByType(String type, String resourceServerId) {
|
||||
Query query = entityManager.createQuery("from ResourceEntity r where r.resourceServer.id = :serverId and type = :type");
|
||||
|
||||
query.setParameter("type", type);
|
||||
query.setParameter("serverId", resourceServerId);
|
||||
|
||||
return query.getResultList();
|
||||
}
|
||||
|
|
|
@ -17,12 +17,9 @@
|
|||
*/
|
||||
package org.keycloak.authorization.jpa.store;
|
||||
|
||||
import org.keycloak.authorization.jpa.entities.ResourceServerEntity;
|
||||
import org.keycloak.authorization.jpa.entities.ScopeEntity;
|
||||
import org.keycloak.authorization.model.ResourceServer;
|
||||
import org.keycloak.authorization.model.Scope;
|
||||
import org.keycloak.authorization.store.ScopeStore;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.NoResultException;
|
||||
|
@ -31,9 +28,13 @@ import javax.persistence.criteria.CriteriaBuilder;
|
|||
import javax.persistence.criteria.CriteriaQuery;
|
||||
import javax.persistence.criteria.Predicate;
|
||||
import javax.persistence.criteria.Root;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.keycloak.authorization.jpa.entities.ResourceServerEntity;
|
||||
import org.keycloak.authorization.jpa.entities.ScopeEntity;
|
||||
import org.keycloak.authorization.model.ResourceServer;
|
||||
import org.keycloak.authorization.model.Scope;
|
||||
import org.keycloak.authorization.store.ScopeStore;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
|
@ -61,21 +62,39 @@ public class JPAScopeStore implements ScopeStore {
|
|||
|
||||
@Override
|
||||
public void delete(String id) {
|
||||
this.entityManager.remove(findById(id));
|
||||
Scope scope = entityManager.find(ScopeEntity.class, id);
|
||||
|
||||
if (scope != null) {
|
||||
this.entityManager.remove(scope);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Scope findById(String id) {
|
||||
public Scope findById(String id, String resourceServerId) {
|
||||
if (id == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (true) {
|
||||
return entityManager.find(ScopeEntity.class, id);
|
||||
}
|
||||
|
||||
Query query = entityManager.createQuery("from ScopeEntity where resourceServer.id = :serverId and id = :id");
|
||||
|
||||
query.setParameter("serverId", resourceServerId);
|
||||
query.setParameter("id", id);
|
||||
|
||||
return entityManager.find(ScopeEntity.class, id);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Scope findByName(String name, String resourceServerId) {
|
||||
try {
|
||||
Query query = entityManager.createQuery("select s from ScopeEntity s inner join s.resourceServer rs where rs.id = :resourceServerId and name = :name");
|
||||
|
||||
query.setParameter("name", name);
|
||||
query.setParameter("resourceServerId", resourceServerId);
|
||||
query.setParameter("name", name);
|
||||
|
||||
return (Scope) query.getSingleResult();
|
||||
} catch (NoResultException nre) {
|
||||
|
@ -102,7 +121,11 @@ 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() + "%"));
|
||||
}
|
||||
});
|
||||
|
||||
querybuilder.where(predicates.toArray(new Predicate[predicates.size()])).orderBy(builder.asc(root.get("name")));
|
||||
|
|
|
@ -124,21 +124,21 @@ public class PolicyAdapter extends AbstractMongoAdapter<PolicyEntity> implements
|
|||
@Override
|
||||
public Set<Policy> getAssociatedPolicies() {
|
||||
return getMongoEntity().getAssociatedPolicies().stream()
|
||||
.map((Function<String, Policy>) id -> authorizationProvider.getStoreFactory().getPolicyStore().findById(id))
|
||||
.map((Function<String, Policy>) id -> authorizationProvider.getStoreFactory().getPolicyStore().findById(id, getMongoEntity().getResourceServerId()))
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Resource> getResources() {
|
||||
return getMongoEntity().getResources().stream()
|
||||
.map((Function<String, Resource>) id -> authorizationProvider.getStoreFactory().getResourceStore().findById(id))
|
||||
.map((Function<String, Resource>) id -> authorizationProvider.getStoreFactory().getResourceStore().findById(id, getMongoEntity().getResourceServerId()))
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Scope> getScopes() {
|
||||
return getMongoEntity().getScopes().stream()
|
||||
.map((Function<String, Scope>) id -> authorizationProvider.getStoreFactory().getScopeStore().findById(id))
|
||||
.map((Function<String, Scope>) id -> authorizationProvider.getStoreFactory().getScopeStore().findById(id, getMongoEntity().getResourceServerId()))
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
|
|
|
@ -68,7 +68,7 @@ public class ResourceAdapter extends AbstractMongoAdapter<ResourceEntity> implem
|
|||
@Override
|
||||
public List<Scope> getScopes() {
|
||||
return getMongoEntity().getScopes().stream()
|
||||
.map(id -> authorizationProvider.getStoreFactory().getScopeStore().findById(id))
|
||||
.map(id -> authorizationProvider.getStoreFactory().getScopeStore().findById(id, getResourceServer().getId()))
|
||||
.collect(toList());
|
||||
}
|
||||
|
||||
|
|
|
@ -70,7 +70,7 @@ public class MongoPolicyStore implements PolicyStore {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Policy findById(String id) {
|
||||
public Policy findById(String id, String resourceServerId) {
|
||||
PolicyEntity entity = getMongoStore().loadEntity(PolicyEntity.class, id, getInvocationContext());
|
||||
|
||||
if (entity == null) {
|
||||
|
@ -89,7 +89,7 @@ public class MongoPolicyStore implements PolicyStore {
|
|||
.get();
|
||||
|
||||
return getMongoStore().loadEntities(PolicyEntity.class, query, getInvocationContext()).stream()
|
||||
.map(policyEntity -> findById(policyEntity.getId())).findFirst().orElse(null);
|
||||
.map(policyEntity -> findById(policyEntity.getId(), resourceServerId)).findFirst().orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -99,7 +99,7 @@ public class MongoPolicyStore implements PolicyStore {
|
|||
.get();
|
||||
|
||||
return getMongoStore().loadEntities(PolicyEntity.class, query, getInvocationContext()).stream()
|
||||
.map(policyEntity -> findById(policyEntity.getId()))
|
||||
.map(policyEntity -> findById(policyEntity.getId(), resourceServerId))
|
||||
.collect(toList());
|
||||
}
|
||||
|
||||
|
@ -125,17 +125,18 @@ public class MongoPolicyStore implements PolicyStore {
|
|||
DBObject sort = new BasicDBObject("name", 1);
|
||||
|
||||
return getMongoStore().loadEntities(PolicyEntity.class, queryBuilder.get(), sort, firstResult, maxResult, invocationContext).stream()
|
||||
.map(policy -> findById(policy.getId())).collect(toList());
|
||||
.map(policy -> findById(policy.getId(), resourceServerId)).collect(toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Policy> findByResource(String resourceId) {
|
||||
public List<Policy> findByResource(String resourceId, String resourceServerId) {
|
||||
DBObject query = new QueryBuilder()
|
||||
.and("resourceServerId").is(resourceServerId)
|
||||
.and("resources").is(resourceId)
|
||||
.get();
|
||||
|
||||
return getMongoStore().loadEntities(PolicyEntity.class, query, getInvocationContext()).stream()
|
||||
.map(policyEntity -> findById(policyEntity.getId()))
|
||||
.map(policyEntity -> findById(policyEntity.getId(), resourceServerId))
|
||||
.collect(toList());
|
||||
}
|
||||
|
||||
|
@ -150,7 +151,7 @@ public class MongoPolicyStore implements PolicyStore {
|
|||
String defaultResourceType = policyEntity.getConfig().get("defaultResourceType");
|
||||
return defaultResourceType != null && defaultResourceType.equals(resourceType);
|
||||
})
|
||||
.map(policyEntity -> findById(policyEntity.getId()))
|
||||
.map(policyEntity -> findById(policyEntity.getId(), resourceServerId))
|
||||
.collect(toList());
|
||||
}
|
||||
|
||||
|
@ -162,29 +163,31 @@ public class MongoPolicyStore implements PolicyStore {
|
|||
.get();
|
||||
|
||||
return getMongoStore().loadEntities(PolicyEntity.class, query, getInvocationContext()).stream()
|
||||
.map(policyEntity -> findById(policyEntity.getId()))
|
||||
.map(policyEntity -> findById(policyEntity.getId(), resourceServerId))
|
||||
.collect(toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Policy> findByType(String type) {
|
||||
public List<Policy> findByType(String type, String resourceServerId) {
|
||||
DBObject query = new QueryBuilder()
|
||||
.and("resourceServerId").is(resourceServerId)
|
||||
.and("type").is(type)
|
||||
.get();
|
||||
|
||||
return getMongoStore().loadEntities(PolicyEntity.class, query, getInvocationContext()).stream()
|
||||
.map(policyEntity -> findById(policyEntity.getId()))
|
||||
.map(policyEntity -> findById(policyEntity.getId(), resourceServerId))
|
||||
.collect(toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Policy> findDependentPolicies(String policyId) {
|
||||
public List<Policy> findDependentPolicies(String policyId, String resourceServerId) {
|
||||
DBObject query = new QueryBuilder()
|
||||
.and("resourceServerId").is(resourceServerId)
|
||||
.and("associatedPolicies").is(policyId)
|
||||
.get();
|
||||
|
||||
return getMongoStore().loadEntities(PolicyEntity.class, query, getInvocationContext()).stream()
|
||||
.map(policyEntity -> findById(policyEntity.getId()))
|
||||
.map(policyEntity -> findById(policyEntity.getId(), resourceServerId))
|
||||
.collect(toList());
|
||||
}
|
||||
|
||||
|
|
|
@ -70,7 +70,7 @@ public class MongoResourceStore implements ResourceStore {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Resource findById(String id) {
|
||||
public Resource findById(String id, String resourceServerId) {
|
||||
ResourceEntity entity = getMongoStore().loadEntity(ResourceEntity.class, id, getInvocationContext());
|
||||
|
||||
if (entity == null) {
|
||||
|
@ -81,13 +81,14 @@ public class MongoResourceStore implements ResourceStore {
|
|||
}
|
||||
|
||||
@Override
|
||||
public List<Resource> findByOwner(String ownerId) {
|
||||
public List<Resource> findByOwner(String ownerId, String resourceServerId) {
|
||||
DBObject query = new QueryBuilder()
|
||||
.and("resourceServerId").is(resourceServerId)
|
||||
.and("owner").is(ownerId)
|
||||
.get();
|
||||
|
||||
return getMongoStore().loadEntities(ResourceEntity.class, query, getInvocationContext()).stream()
|
||||
.map(scope -> findById(scope.getId())).collect(toList());
|
||||
.map(scope -> findById(scope.getId(), resourceServerId)).collect(toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -97,7 +98,7 @@ public class MongoResourceStore implements ResourceStore {
|
|||
.get();
|
||||
|
||||
return getMongoStore().loadEntities(ResourceEntity.class, query, getInvocationContext()).stream()
|
||||
.map(scope -> findById(scope.getId())).collect(toList());
|
||||
.map(scope -> findById(scope.getId(), resourceServerId)).collect(toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -116,39 +117,41 @@ public class MongoResourceStore implements ResourceStore {
|
|||
DBObject sort = new BasicDBObject("name", 1);
|
||||
|
||||
return getMongoStore().loadEntities(ResourceEntity.class, queryBuilder.get(), sort, firstResult, maxResult, invocationContext).stream()
|
||||
.map(scope -> findById(scope.getId())).collect(toList());
|
||||
.map(scope -> findById(scope.getId(), resourceServerId)).collect(toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Resource> findByScope(String... id) {
|
||||
public List<Resource> findByScope(List<String> id, String resourceServerId) {
|
||||
DBObject query = new QueryBuilder()
|
||||
.and("resourceServerId").is(resourceServerId)
|
||||
.and("scopes").in(id)
|
||||
.get();
|
||||
|
||||
return getMongoStore().loadEntities(ResourceEntity.class, query, getInvocationContext()).stream()
|
||||
.map(policyEntity -> findById(policyEntity.getId()))
|
||||
.map(policyEntity -> findById(policyEntity.getId(), resourceServerId))
|
||||
.collect(toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Resource findByName(String name, String resourceServerId) {
|
||||
DBObject query = new QueryBuilder()
|
||||
.and("name").is(name)
|
||||
.and("resourceServerId").is(resourceServerId)
|
||||
.and("name").is(name)
|
||||
.get();
|
||||
|
||||
return getMongoStore().loadEntities(ResourceEntity.class, query, getInvocationContext()).stream()
|
||||
.map(policyEntity -> findById(policyEntity.getId())).findFirst().orElse(null);
|
||||
.map(policyEntity -> findById(policyEntity.getId(), resourceServerId)).findFirst().orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Resource> findByType(String type) {
|
||||
public List<Resource> findByType(String type, String resourceServerId) {
|
||||
DBObject query = new QueryBuilder()
|
||||
.and("resourceServerId").is(resourceServerId)
|
||||
.and("type").is(type)
|
||||
.get();
|
||||
|
||||
return getMongoStore().loadEntities(ResourceEntity.class, query, getInvocationContext()).stream()
|
||||
.map(policyEntity -> findById(policyEntity.getId()))
|
||||
.map(policyEntity -> findById(policyEntity.getId(), resourceServerId))
|
||||
.collect(toList());
|
||||
}
|
||||
|
||||
|
|
|
@ -69,7 +69,7 @@ public class MongoScopeStore implements ScopeStore {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Scope findById(String id) {
|
||||
public Scope findById(String id, String resourceServerId) {
|
||||
ScopeEntity entity = getMongoStore().loadEntity(ScopeEntity.class, id, getInvocationContext());
|
||||
|
||||
if (entity == null) {
|
||||
|
@ -87,7 +87,7 @@ public class MongoScopeStore implements ScopeStore {
|
|||
.get();
|
||||
|
||||
return getMongoStore().loadEntities(ScopeEntity.class, query, getInvocationContext()).stream()
|
||||
.map(scope -> findById(scope.getId())).findFirst().orElse(null);
|
||||
.map(scope -> findById(scope.getId(), scope.getResourceServerId())).findFirst().orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -97,7 +97,7 @@ public class MongoScopeStore implements ScopeStore {
|
|||
.get();
|
||||
|
||||
return getMongoStore().loadEntities(ScopeEntity.class, query, getInvocationContext()).stream()
|
||||
.map(policyEntity -> findById(policyEntity.getId()))
|
||||
.map(scope -> findById(scope.getId(), scope.getResourceServerId()))
|
||||
.collect(toList());
|
||||
}
|
||||
|
||||
|
@ -113,7 +113,7 @@ public class MongoScopeStore implements ScopeStore {
|
|||
DBObject sort = new BasicDBObject("name", 1);
|
||||
|
||||
return getMongoStore().loadEntities(ScopeEntity.class, queryBuilder.get(), sort, firstResult, maxResult, invocationContext).stream()
|
||||
.map(scope -> findById(scope.getId())).collect(toList());
|
||||
.map(scope -> findById(scope.getId(), scope.getResourceServerId())).collect(toList());
|
||||
}
|
||||
|
||||
private MongoStoreInvocationContext getInvocationContext() {
|
||||
|
|
|
@ -18,6 +18,12 @@
|
|||
|
||||
package org.keycloak.authorization;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.keycloak.authorization.permission.evaluator.Evaluators;
|
||||
import org.keycloak.authorization.policy.evaluation.DefaultPolicyEvaluator;
|
||||
import org.keycloak.authorization.policy.provider.PolicyProvider;
|
||||
|
@ -26,11 +32,6 @@ import org.keycloak.authorization.store.StoreFactory;
|
|||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.provider.Provider;
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* <p>The main contract here is the creation of {@link org.keycloak.authorization.permission.evaluator.PermissionEvaluator} instances. Usually
|
||||
|
@ -62,22 +63,22 @@ public final class AuthorizationProvider implements Provider {
|
|||
|
||||
private final DefaultPolicyEvaluator policyEvaluator;
|
||||
private final Executor scheduller;
|
||||
private final StoreFactory storeFactory;
|
||||
private final List<PolicyProviderFactory> policyProviderFactories;
|
||||
private final Supplier<StoreFactory> storeFactory;
|
||||
private final Map<String, PolicyProviderFactory> policyProviderFactories;
|
||||
private final KeycloakSession keycloakSession;
|
||||
private final RealmModel realm;
|
||||
|
||||
public AuthorizationProvider(KeycloakSession session, RealmModel realm, StoreFactory storeFactory, Executor scheduller) {
|
||||
public AuthorizationProvider(KeycloakSession session, RealmModel realm, Supplier<StoreFactory> storeFactory, Map<String, PolicyProviderFactory> policyProviderFactories, Executor scheduller) {
|
||||
this.keycloakSession = session;
|
||||
this.realm = realm;
|
||||
this.storeFactory = storeFactory;
|
||||
this.scheduller = scheduller;
|
||||
this.policyProviderFactories = configurePolicyProviderFactories(session);
|
||||
this.policyEvaluator = new DefaultPolicyEvaluator(this, this.policyProviderFactories);
|
||||
this.policyProviderFactories = policyProviderFactories;
|
||||
this.policyEvaluator = new DefaultPolicyEvaluator(this);
|
||||
}
|
||||
|
||||
public AuthorizationProvider(KeycloakSession session, RealmModel realm, StoreFactory storeFactory) {
|
||||
this(session, realm, storeFactory, Runnable::run);
|
||||
public AuthorizationProvider(KeycloakSession session, RealmModel realm, StoreFactory storeFactory, Map<String, PolicyProviderFactory> policyProviderFactories) {
|
||||
this(session, realm, () -> storeFactory, policyProviderFactories, Runnable::run);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -87,7 +88,7 @@ public final class AuthorizationProvider implements Provider {
|
|||
* @return a {@link Evaluators} instance
|
||||
*/
|
||||
public Evaluators evaluators() {
|
||||
return new Evaluators(this.policyProviderFactories, this.policyEvaluator, this.scheduller);
|
||||
return new Evaluators(this.policyEvaluator, this.scheduller);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -96,7 +97,7 @@ public final class AuthorizationProvider implements Provider {
|
|||
* @return the {@link StoreFactory}
|
||||
*/
|
||||
public StoreFactory getStoreFactory() {
|
||||
return this.storeFactory;
|
||||
return this.storeFactory.get();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -104,8 +105,8 @@ public final class AuthorizationProvider implements Provider {
|
|||
*
|
||||
* @return a {@link List} containing all registered {@link PolicyProviderFactory}
|
||||
*/
|
||||
public List<PolicyProviderFactory> getProviderFactories() {
|
||||
return this.policyProviderFactories;
|
||||
public Collection<PolicyProviderFactory> getProviderFactories() {
|
||||
return this.policyProviderFactories.values();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -116,7 +117,24 @@ public final class AuthorizationProvider implements Provider {
|
|||
* @return a {@link PolicyProviderFactory} with the given <code>type</code>
|
||||
*/
|
||||
public <F extends PolicyProviderFactory> F getProviderFactory(String type) {
|
||||
return (F) getProviderFactories().stream().filter(policyProviderFactory -> policyProviderFactory.getId().equals(type)).findFirst().orElse(null);
|
||||
return (F) policyProviderFactories.get(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link PolicyProviderFactory} given a <code>type</code>.
|
||||
*
|
||||
* @param type the type of the policy provider
|
||||
* @param <F> the expected type of the provider
|
||||
* @return a {@link PolicyProvider} with the given <code>type</code>
|
||||
*/
|
||||
public <P extends PolicyProvider> P getProvider(String type) {
|
||||
PolicyProviderFactory policyProviderFactory = policyProviderFactories.get(type);
|
||||
|
||||
if (policyProviderFactory == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (P) policyProviderFactory.create(this);
|
||||
}
|
||||
|
||||
public KeycloakSession getKeycloakSession() {
|
||||
|
@ -127,16 +145,6 @@ public final class AuthorizationProvider implements Provider {
|
|||
return realm;
|
||||
}
|
||||
|
||||
private List<PolicyProviderFactory> configurePolicyProviderFactories(KeycloakSession session) {
|
||||
List<ProviderFactory> providerFactories = session.getKeycloakSessionFactory().getProviderFactories(PolicyProvider.class);
|
||||
|
||||
if (providerFactories.isEmpty()) {
|
||||
throw new RuntimeException("Could not find any policy provider.");
|
||||
}
|
||||
|
||||
return providerFactories.stream().map(providerFactory -> (PolicyProviderFactory) providerFactory).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
|
||||
|
|
|
@ -18,13 +18,12 @@
|
|||
|
||||
package org.keycloak.authorization.permission.evaluator;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import org.keycloak.authorization.permission.ResourcePermission;
|
||||
import org.keycloak.authorization.policy.evaluation.DefaultPolicyEvaluator;
|
||||
import org.keycloak.authorization.policy.evaluation.EvaluationContext;
|
||||
import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/**
|
||||
* A factory for the different {@link PermissionEvaluator} implementations.
|
||||
|
@ -33,12 +32,10 @@ import java.util.concurrent.Executor;
|
|||
*/
|
||||
public final class Evaluators {
|
||||
|
||||
private final List<PolicyProviderFactory> policyProviderFactories;
|
||||
private final DefaultPolicyEvaluator policyEvaluator;
|
||||
private final Executor scheduler;
|
||||
|
||||
public Evaluators(List<PolicyProviderFactory> policyProviderFactories, DefaultPolicyEvaluator policyEvaluator, Executor scheduler) {
|
||||
this.policyProviderFactories = policyProviderFactories;
|
||||
public Evaluators(DefaultPolicyEvaluator policyEvaluator, Executor scheduler) {
|
||||
this.policyEvaluator = policyEvaluator;
|
||||
this.scheduler = scheduler;
|
||||
}
|
||||
|
|
|
@ -17,11 +17,10 @@
|
|||
*/
|
||||
package org.keycloak.authorization.permission.evaluator;
|
||||
|
||||
import org.keycloak.authorization.Decision;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import org.keycloak.authorization.Decision;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
* @see PermissionEvaluator
|
||||
|
@ -38,6 +37,6 @@ class ScheduledPermissionEvaluator implements PermissionEvaluator {
|
|||
|
||||
@Override
|
||||
public void evaluate(Decision decision) {
|
||||
CompletableFuture.runAsync(() -> publisher.evaluate(decision), scheduler);
|
||||
publisher.evaluate(decision);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,8 +37,10 @@ public abstract class DecisionResultCollector implements Decision<DefaultEvaluat
|
|||
|
||||
@Override
|
||||
public void onDecision(DefaultEvaluation evaluation) {
|
||||
if (evaluation.getParentPolicy() != null) {
|
||||
results.computeIfAbsent(evaluation.getPermission(), Result::new).policy(evaluation.getParentPolicy()).policy(evaluation.getPolicy()).setStatus(evaluation.getEffect());
|
||||
Policy parentPolicy = evaluation.getParentPolicy();
|
||||
|
||||
if (parentPolicy != null) {
|
||||
results.computeIfAbsent(evaluation.getPermission(), Result::new).policy(parentPolicy).policy(evaluation.getPolicy()).setStatus(evaluation.getEffect());
|
||||
} else {
|
||||
results.computeIfAbsent(evaluation.getPermission(), Result::new).setStatus(evaluation.getEffect());
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
package org.keycloak.authorization.policy.evaluation;
|
||||
|
||||
import org.keycloak.authorization.AuthorizationProvider;
|
||||
import org.keycloak.authorization.Decision;
|
||||
import org.keycloak.authorization.Decision.Effect;
|
||||
import org.keycloak.authorization.model.Policy;
|
||||
|
@ -34,37 +35,29 @@ public class DefaultEvaluation implements Evaluation {
|
|||
private final Decision decision;
|
||||
private final Policy policy;
|
||||
private final Policy parentPolicy;
|
||||
private final AuthorizationProvider authorizationProvider;
|
||||
private Effect effect;
|
||||
|
||||
public DefaultEvaluation(ResourcePermission permission, EvaluationContext executionContext, Policy parentPolicy, Policy policy, Decision decision) {
|
||||
public DefaultEvaluation(ResourcePermission permission, EvaluationContext executionContext, Policy parentPolicy, Policy policy, Decision decision, AuthorizationProvider authorizationProvider) {
|
||||
this.permission = permission;
|
||||
this.executionContext = executionContext;
|
||||
this.parentPolicy = parentPolicy;
|
||||
this.policy = policy;
|
||||
this.decision = decision;
|
||||
this.authorizationProvider = authorizationProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link ResourcePermission} to be evaluated.
|
||||
*
|
||||
* @return the permission to be evaluated
|
||||
*/
|
||||
@Override
|
||||
public ResourcePermission getPermission() {
|
||||
return this.permission;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link org.keycloak.authorization.permission.evaluator.PermissionEvaluator}. Which provides access to the whole evaluation runtime context.
|
||||
*
|
||||
* @return the evaluation context
|
||||
*/
|
||||
@Override
|
||||
public EvaluationContext getContext() {
|
||||
return this.executionContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Grants all the requested permissions to the caller.
|
||||
*/
|
||||
@Override
|
||||
public void grant() {
|
||||
if (policy != null && Logic.NEGATIVE.equals(policy.getLogic())) {
|
||||
this.effect = Effect.DENY;
|
||||
|
@ -75,6 +68,7 @@ public class DefaultEvaluation implements Evaluation {
|
|||
this.decision.onDecision(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deny() {
|
||||
if (policy != null && Logic.NEGATIVE.equals(policy.getLogic())) {
|
||||
this.effect = Effect.PERMIT;
|
||||
|
@ -85,10 +79,16 @@ public class DefaultEvaluation implements Evaluation {
|
|||
this.decision.onDecision(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Policy getPolicy() {
|
||||
return this.policy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthorizationProvider getAuthorizationProvider() {
|
||||
return authorizationProvider;
|
||||
}
|
||||
|
||||
public Policy getParentPolicy() {
|
||||
return this.parentPolicy;
|
||||
}
|
||||
|
|
|
@ -18,6 +18,14 @@
|
|||
|
||||
package org.keycloak.authorization.policy.evaluation;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.keycloak.authorization.AuthorizationProvider;
|
||||
import org.keycloak.authorization.Decision;
|
||||
import org.keycloak.authorization.model.Policy;
|
||||
|
@ -26,87 +34,78 @@ import org.keycloak.authorization.model.ResourceServer;
|
|||
import org.keycloak.authorization.model.Scope;
|
||||
import org.keycloak.authorization.permission.ResourcePermission;
|
||||
import org.keycloak.authorization.policy.provider.PolicyProvider;
|
||||
import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
|
||||
import org.keycloak.authorization.store.PolicyStore;
|
||||
import org.keycloak.authorization.store.StoreFactory;
|
||||
import org.keycloak.representations.idm.authorization.PolicyEnforcementMode;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
public class DefaultPolicyEvaluator implements PolicyEvaluator {
|
||||
|
||||
private final AuthorizationProvider authorization;
|
||||
private Map<String, PolicyProviderFactory> policyProviders = new HashMap<>();
|
||||
private final StoreFactory storeFactory;
|
||||
private final PolicyStore policyStore;
|
||||
|
||||
public DefaultPolicyEvaluator(AuthorizationProvider authorization, List<PolicyProviderFactory> policyProviderFactories) {
|
||||
public DefaultPolicyEvaluator(AuthorizationProvider authorization) {
|
||||
this.authorization = authorization;
|
||||
|
||||
for (PolicyProviderFactory providerFactory : policyProviderFactories) {
|
||||
this.policyProviders.put(providerFactory.getId(), providerFactory);
|
||||
}
|
||||
storeFactory = this.authorization.getStoreFactory();
|
||||
policyStore = storeFactory.getPolicyStore();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void evaluate(ResourcePermission permission, EvaluationContext executionContext, Decision decision) {
|
||||
ResourceServer resourceServer = permission.getResourceServer();
|
||||
PolicyEnforcementMode enforcementMode = resourceServer.getPolicyEnforcementMode();
|
||||
|
||||
if (PolicyEnforcementMode.DISABLED.equals(resourceServer.getPolicyEnforcementMode())) {
|
||||
if (PolicyEnforcementMode.DISABLED.equals(enforcementMode)) {
|
||||
createEvaluation(permission, executionContext, decision, null, null).grant();
|
||||
return;
|
||||
}
|
||||
|
||||
StoreFactory storeFactory = this.authorization.getStoreFactory();
|
||||
PolicyStore policyStore = storeFactory.getPolicyStore();
|
||||
AtomicInteger policiesCount = new AtomicInteger(0);
|
||||
Consumer<Policy> consumer = createDecisionConsumer(permission, executionContext, decision, policiesCount);
|
||||
AtomicBoolean verified = new AtomicBoolean(false);
|
||||
Consumer<Policy> consumer = createDecisionConsumer(permission, executionContext, decision, verified);
|
||||
Resource resource = permission.getResource();
|
||||
List<Scope> scopes = permission.getScopes();
|
||||
|
||||
if (resource != null) {
|
||||
List<? extends Policy> resourcePolicies = policyStore.findByResource(resource.getId());
|
||||
|
||||
if (!resourcePolicies.isEmpty()) {
|
||||
resourcePolicies.forEach(consumer);
|
||||
}
|
||||
evaluatePolicies(() -> policyStore.findByResource(resource.getId(), resourceServer.getId()), consumer);
|
||||
|
||||
if (resource.getType() != null) {
|
||||
policyStore.findByResourceType(resource.getType(), resourceServer.getId()).forEach(consumer);
|
||||
evaluatePolicies(() -> policyStore.findByResourceType(resource.getType(), resourceServer.getId()), consumer);
|
||||
}
|
||||
|
||||
if (permission.getScopes().isEmpty() && !resource.getScopes().isEmpty()) {
|
||||
policyStore.findByScopeIds(resource.getScopes().stream().map(Scope::getId).collect(Collectors.toList()), resourceServer.getId()).forEach(consumer);
|
||||
if (scopes.isEmpty() && !resource.getScopes().isEmpty()) {
|
||||
scopes.removeAll(resource.getScopes());
|
||||
evaluatePolicies(() -> policyStore.findByScopeIds(resource.getScopes().stream().map(Scope::getId).collect(Collectors.toList()), resourceServer.getId()), consumer);
|
||||
}
|
||||
}
|
||||
|
||||
if (!permission.getScopes().isEmpty()) {
|
||||
policyStore.findByScopeIds(permission.getScopes().stream().map(Scope::getId).collect(Collectors.toList()), resourceServer.getId()).forEach(consumer);
|
||||
if (!scopes.isEmpty()) {
|
||||
evaluatePolicies(() -> policyStore.findByScopeIds(scopes.stream().map(Scope::getId).collect(Collectors.toList()), resourceServer.getId()), consumer);
|
||||
}
|
||||
|
||||
if (PolicyEnforcementMode.PERMISSIVE.equals(resourceServer.getPolicyEnforcementMode()) && policiesCount.get() == 0) {
|
||||
if (PolicyEnforcementMode.PERMISSIVE.equals(enforcementMode) && verified.get()) {
|
||||
createEvaluation(permission, executionContext, decision, null, null).grant();
|
||||
}
|
||||
}
|
||||
|
||||
private Consumer<Policy> createDecisionConsumer(ResourcePermission permission, EvaluationContext executionContext, Decision decision, AtomicInteger policiesCount) {
|
||||
return (parentPolicy) -> {
|
||||
if (hasRequestedScopes(permission, parentPolicy)) {
|
||||
for (Policy associatedPolicy : parentPolicy.getAssociatedPolicies()) {
|
||||
PolicyProviderFactory providerFactory = policyProviders.get(associatedPolicy.getType());
|
||||
private void evaluatePolicies(Supplier<List<Policy>> supplier, Consumer<Policy> consumer) {
|
||||
List<Policy> policies = supplier.get();
|
||||
|
||||
if (providerFactory == null) {
|
||||
throw new RuntimeException("Could not find a policy provider for policy type [" + associatedPolicy.getType() + "].");
|
||||
if (!policies.isEmpty()) {
|
||||
policies.forEach(consumer);
|
||||
}
|
||||
}
|
||||
|
||||
PolicyProvider policyProvider = providerFactory.create(associatedPolicy, this.authorization);
|
||||
private Consumer<Policy> createDecisionConsumer(ResourcePermission permission, EvaluationContext executionContext, Decision decision, AtomicBoolean verified) {
|
||||
return (parentPolicy) -> {
|
||||
if (!hasRequestedScopes(permission, parentPolicy)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (Policy associatedPolicy : parentPolicy.getAssociatedPolicies()) {
|
||||
PolicyProvider policyProvider = authorization.getProvider(associatedPolicy.getType());
|
||||
|
||||
if (policyProvider == null) {
|
||||
throw new RuntimeException("Unknown parentPolicy provider for type [" + associatedPolicy.getType() + "].");
|
||||
|
@ -116,15 +115,14 @@ public class DefaultPolicyEvaluator implements PolicyEvaluator {
|
|||
|
||||
policyProvider.evaluate(evaluation);
|
||||
evaluation.denyIfNoEffect();
|
||||
}
|
||||
|
||||
policiesCount.incrementAndGet();
|
||||
}
|
||||
}
|
||||
verified.compareAndSet(false, true);
|
||||
};
|
||||
}
|
||||
|
||||
private DefaultEvaluation createEvaluation(ResourcePermission permission, EvaluationContext executionContext, Decision decision, Policy parentPolicy, Policy associatedPolicy) {
|
||||
return new DefaultEvaluation(permission, executionContext, parentPolicy, associatedPolicy, decision);
|
||||
return new DefaultEvaluation(permission, executionContext, parentPolicy, associatedPolicy, decision, authorization);
|
||||
}
|
||||
|
||||
private boolean hasRequestedScopes(final ResourcePermission permission, final Policy policy) {
|
||||
|
@ -161,7 +159,7 @@ public class DefaultPolicyEvaluator implements PolicyEvaluator {
|
|||
String type = resource.getType();
|
||||
|
||||
if (type != null) {
|
||||
List<Resource> resourcesByType = authorization.getStoreFactory().getResourceStore().findByType(type);
|
||||
List<Resource> resourcesByType = authorization.getStoreFactory().getResourceStore().findByType(type, resource.getResourceServer().getId());
|
||||
|
||||
for (Resource resourceType : resourcesByType) {
|
||||
if (resourceType.getOwner().equals(resource.getResourceServer().getClientId())) {
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
|
||||
package org.keycloak.authorization.policy.evaluation;
|
||||
|
||||
import org.keycloak.authorization.AuthorizationProvider;
|
||||
import org.keycloak.authorization.model.Policy;
|
||||
import org.keycloak.authorization.permission.ResourcePermission;
|
||||
|
||||
/**
|
||||
|
@ -42,6 +44,15 @@ public interface Evaluation {
|
|||
*/
|
||||
EvaluationContext getContext();
|
||||
|
||||
/**
|
||||
* Returns the {@link Policy}. being evaluated.
|
||||
*
|
||||
* @return the evaluation context
|
||||
*/
|
||||
Policy getPolicy();
|
||||
|
||||
AuthorizationProvider getAuthorizationProvider();
|
||||
|
||||
/**
|
||||
* Grants the requested permission to the caller.
|
||||
*/
|
||||
|
|
|
@ -32,7 +32,7 @@ public interface PolicyProviderFactory extends ProviderFactory<PolicyProvider> {
|
|||
|
||||
String getGroup();
|
||||
|
||||
PolicyProvider create(Policy policy, AuthorizationProvider authorization);
|
||||
PolicyProvider create(AuthorizationProvider authorization);
|
||||
|
||||
PolicyProviderAdminService getAdminResource(ResourceServer resourceServer);
|
||||
}
|
||||
|
|
|
@ -53,9 +53,10 @@ public interface PolicyStore {
|
|||
* Returns a {@link Policy} with the given <code>id</code>
|
||||
*
|
||||
* @param id the identifier of the policy
|
||||
* @param resourceServerId the resource server id
|
||||
* @return a policy with the given identifier.
|
||||
*/
|
||||
Policy findById(String id);
|
||||
Policy findById(String id, String resourceServerId);
|
||||
|
||||
/**
|
||||
* Returns a {@link Policy} with the given <code>name</code>
|
||||
|
@ -87,9 +88,10 @@ public interface PolicyStore {
|
|||
* Returns a list of {@link Policy} associated with a {@link org.keycloak.authorization.core.model.Resource} with the given <code>resourceId</code>.
|
||||
*
|
||||
* @param resourceId the identifier of a resource
|
||||
* @param resourceServerId the resource server id
|
||||
* @return a list of policies associated with the given resource
|
||||
*/
|
||||
List<Policy> findByResource(String resourceId);
|
||||
List<Policy> findByResource(String resourceId, String resourceServerId);
|
||||
|
||||
/**
|
||||
* Returns a list of {@link Policy} associated with a {@link org.keycloak.authorization.core.model.Resource} with the given <code>type</code>.
|
||||
|
@ -113,15 +115,26 @@ public interface PolicyStore {
|
|||
* Returns a list of {@link Policy} with the given <code>type</code>.
|
||||
*
|
||||
* @param type the type of the policy
|
||||
* @param resourceServerId the resource server id
|
||||
* @return a list of policies with the given type
|
||||
*/
|
||||
List<Policy> findByType(String type);
|
||||
List<Policy> findByType(String type, String resourceServerId);
|
||||
|
||||
/**
|
||||
* Returns a list of {@link Policy} that depends on another policy with the given <code>id</code>.
|
||||
*
|
||||
* @param id the id of the policy to query its dependents
|
||||
* @param resourceServerId the resource server id
|
||||
* @return a list of policies that depends on the a policy with the given identifier
|
||||
*/
|
||||
List<Policy> findDependentPolicies(String id);
|
||||
List<Policy> findDependentPolicies(String id, String resourceServerId);
|
||||
|
||||
/**
|
||||
* Notify this store about changes to data associated with policies. E.g.: resources and scopes..
|
||||
*
|
||||
* TODO: need a better strategy to handle cross-references between stores, specially in cases where the store is caching data. Use some event-based solution here.
|
||||
*
|
||||
* @param cached
|
||||
*/
|
||||
default void notifyChange(Object cached) {}
|
||||
}
|
||||
|
|
|
@ -53,7 +53,7 @@ public interface ResourceStore {
|
|||
* @param id the identifier of an existing resource instance
|
||||
* @return the resource instance with the given identifier or null if no instance was found
|
||||
*/
|
||||
Resource findById(String id);
|
||||
Resource findById(String id, String resourceServerId);
|
||||
|
||||
/**
|
||||
* Finds all {@link Resource} instances with the given {@code ownerId}.
|
||||
|
@ -61,7 +61,7 @@ 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);
|
||||
List<Resource> findByOwner(String ownerId, String resourceServerId);
|
||||
|
||||
/**
|
||||
* Finds all {@link Resource} instances associated with a given resource server.
|
||||
|
@ -86,7 +86,7 @@ public interface ResourceStore {
|
|||
* @param id one or more scope identifiers
|
||||
* @return a list of resources associated with the given scope(s)
|
||||
*/
|
||||
List<Resource> findByScope(String... id);
|
||||
List<Resource> findByScope(List<String> id, String resourceServerId);
|
||||
|
||||
/**
|
||||
* Find a {@link Resource} by its name.
|
||||
|
@ -103,5 +103,5 @@ public interface ResourceStore {
|
|||
* @param type the type of the resource
|
||||
* @return a list of resources with the given type
|
||||
*/
|
||||
List<Resource> findByType(String type);
|
||||
List<Resource> findByType(String type, String resourceServerId);
|
||||
}
|
||||
|
|
|
@ -53,17 +53,17 @@ public interface ScopeStore {
|
|||
* Returns a {@link Scope} with the given <code>id</code>
|
||||
*
|
||||
* @param id the identifier of the scope
|
||||
*
|
||||
* @param resourceServerId the resource server id
|
||||
* @return a scope with the given identifier.
|
||||
*/
|
||||
Scope findById(String id);
|
||||
Scope findById(String id, String resourceServerId);
|
||||
|
||||
/**
|
||||
* Returns a {@link Scope} with the given <code>name</code>
|
||||
*
|
||||
* @param name the name of the scope
|
||||
*
|
||||
* @param resourceServerId
|
||||
* @param resourceServerId the resource server id
|
||||
* @return a scope with the given name.
|
||||
*/
|
||||
Scope findByName(String name, String resourceServerId);
|
||||
|
|
|
@ -36,7 +36,7 @@ public class RealmSynchronizer implements Synchronizer<RealmRemovedEvent> {
|
|||
StoreFactory storeFactory = authorizationProvider.getStoreFactory();
|
||||
|
||||
event.getRealm().getClients().forEach(clientModel -> {
|
||||
ResourceServer resourceServer = storeFactory.getResourceServerStore().findByClient(clientModel.getClientId());
|
||||
ResourceServer resourceServer = storeFactory.getResourceServerStore().findByClient(clientModel.getId());
|
||||
|
||||
if (resourceServer != null) {
|
||||
String id = resourceServer.getId();
|
||||
|
|
|
@ -17,11 +17,16 @@
|
|||
|
||||
package org.keycloak.authorization.store.syncronization;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.keycloak.authorization.AuthorizationProvider;
|
||||
import org.keycloak.authorization.model.ResourceServer;
|
||||
import org.keycloak.authorization.store.PolicyStore;
|
||||
import org.keycloak.authorization.store.ResourceServerStore;
|
||||
import org.keycloak.authorization.store.ResourceStore;
|
||||
import org.keycloak.authorization.store.StoreFactory;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserModel.UserRemovedEvent;
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
|
@ -39,10 +44,16 @@ public class UserSynchronizer implements Synchronizer<UserRemovedEvent> {
|
|||
UserModel userModel = event.getUser();
|
||||
ResourceStore resourceStore = storeFactory.getResourceStore();
|
||||
PolicyStore policyStore = storeFactory.getPolicyStore();
|
||||
ResourceServerStore resourceServerStore = storeFactory.getResourceServerStore();
|
||||
RealmModel realm = event.getRealm();
|
||||
|
||||
resourceStore.findByOwner(userModel.getId()).forEach(resource -> {
|
||||
realm.getClients().forEach(clientModel -> {
|
||||
ResourceServer resourceServer = resourceServerStore.findByClient(clientModel.getId());
|
||||
|
||||
if (resourceServer != null) {
|
||||
resourceStore.findByOwner(userModel.getId(), resourceServer.getId()).forEach(resource -> {
|
||||
String resourceId = resource.getId();
|
||||
policyStore.findByResource(resourceId).forEach(policy -> {
|
||||
policyStore.findByResource(resourceId, resourceServer.getId()).forEach(policy -> {
|
||||
if (policy.getResources().size() == 1) {
|
||||
policyStore.delete(policy.getId());
|
||||
} else {
|
||||
|
@ -52,4 +63,6 @@ public class UserSynchronizer implements Synchronizer<UserRemovedEvent> {
|
|||
resourceStore.delete(resourceId);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -70,7 +70,7 @@ public class MigrateTo2_1_0 implements Migration {
|
|||
ResourceServer resourceServer = storeFactory.getResourceServerStore().findByClient(clientModel.getId());
|
||||
|
||||
if (resourceServer != null) {
|
||||
policyStore.findByType("role").forEach(policy -> {
|
||||
policyStore.findByType("role", resourceServer.getId()).forEach(policy -> {
|
||||
Map<String, String> config = policy.getConfig();
|
||||
String roles = config.get("roles");
|
||||
List roleConfig;
|
||||
|
|
|
@ -17,14 +17,23 @@
|
|||
|
||||
package org.keycloak.models.utils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.keycloak.authorization.AuthorizationProvider;
|
||||
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.PolicyStore;
|
||||
import org.keycloak.authorization.store.ResourceStore;
|
||||
import org.keycloak.authorization.store.StoreFactory;
|
||||
import org.keycloak.common.util.MultivaluedHashMap;
|
||||
import org.keycloak.common.util.Time;
|
||||
import org.keycloak.component.ComponentModel;
|
||||
|
@ -84,20 +93,6 @@ import org.keycloak.representations.idm.authorization.ResourceRepresentation;
|
|||
import org.keycloak.representations.idm.authorization.ResourceServerRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
|
||||
import org.keycloak.storage.StorageId;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
|
@ -772,34 +767,16 @@ public class ModelToRepresentation {
|
|||
}
|
||||
|
||||
public static ScopeRepresentation toRepresentation(Scope model, AuthorizationProvider authorizationProvider) {
|
||||
return toRepresentation(model, authorizationProvider, true);
|
||||
}
|
||||
|
||||
public static ScopeRepresentation toRepresentation(Scope model, AuthorizationProvider authorizationProvider, boolean deep) {
|
||||
ScopeRepresentation scope = new ScopeRepresentation();
|
||||
|
||||
scope.setId(model.getId());
|
||||
scope.setName(model.getName());
|
||||
scope.setIconUri(model.getIconUri());
|
||||
|
||||
StoreFactory storeFactory = authorizationProvider.getStoreFactory();
|
||||
|
||||
scope.setResources(new ArrayList<>());
|
||||
|
||||
storeFactory.getResourceStore().findByScope(model.getId()).forEach(resource -> scope.getResources().add(toRepresentation(resource, resource.getResourceServer(), authorizationProvider)));
|
||||
|
||||
PolicyStore policyStore = storeFactory.getPolicyStore();
|
||||
|
||||
scope.setPolicies(new ArrayList<>());
|
||||
|
||||
policyStore.findByScopeIds(Arrays.asList(model.getId()), model.getResourceServer().getId()).forEach(policyModel -> {
|
||||
PolicyRepresentation policy = new PolicyRepresentation();
|
||||
|
||||
policy.setId(policyModel.getId());
|
||||
policy.setName(policyModel.getName());
|
||||
policy.setType(policyModel.getType());
|
||||
|
||||
if (!scope.getPolicies().contains(policy)) {
|
||||
scope.getPolicies().add(policy);
|
||||
}
|
||||
});
|
||||
|
||||
return scope;
|
||||
}
|
||||
|
||||
|
@ -815,7 +792,7 @@ public class ModelToRepresentation {
|
|||
return server;
|
||||
}
|
||||
|
||||
public static PolicyRepresentation toRepresentation(Policy model, AuthorizationProvider authorization) {
|
||||
public static PolicyRepresentation toRepresentation(Policy model) {
|
||||
PolicyRepresentation representation = new PolicyRepresentation();
|
||||
|
||||
representation.setId(model.getId());
|
||||
|
@ -824,45 +801,16 @@ public class ModelToRepresentation {
|
|||
representation.setType(model.getType());
|
||||
representation.setDecisionStrategy(model.getDecisionStrategy());
|
||||
representation.setLogic(model.getLogic());
|
||||
representation.setConfig(new HashMap<>(model.getConfig()));
|
||||
|
||||
List<Policy> policies = authorization.getStoreFactory().getPolicyStore().findDependentPolicies(model.getId());
|
||||
|
||||
representation.setDependentPolicies(policies.stream().map(policy -> {
|
||||
PolicyRepresentation representation1 = new PolicyRepresentation();
|
||||
|
||||
representation1.setId(policy.getId());
|
||||
representation1.setName(policy.getName());
|
||||
|
||||
return representation1;
|
||||
}).collect(Collectors.toList()));
|
||||
|
||||
List<PolicyRepresentation> associatedPolicies = new ArrayList<>();
|
||||
|
||||
List<String> obj = model.getAssociatedPolicies().stream().map(policy -> {
|
||||
PolicyRepresentation representation1 = new PolicyRepresentation();
|
||||
|
||||
representation1.setId(policy.getId());
|
||||
representation1.setName(policy.getName());
|
||||
representation1.setType(policy.getType());
|
||||
|
||||
associatedPolicies.add(representation1);
|
||||
|
||||
return policy.getId();
|
||||
}).collect(Collectors.toList());
|
||||
|
||||
representation.setAssociatedPolicies(associatedPolicies);
|
||||
|
||||
try {
|
||||
representation.getConfig().put("applyPolicies", JsonSerialization.writeValueAsString(obj));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
representation.setConfig(model.getConfig());
|
||||
|
||||
return representation;
|
||||
}
|
||||
|
||||
public static ResourceRepresentation toRepresentation(Resource model, ResourceServer resourceServer, AuthorizationProvider authorization) {
|
||||
return toRepresentation(model, resourceServer, authorization, true);
|
||||
}
|
||||
|
||||
public static ResourceRepresentation toRepresentation(Resource model, ResourceServer resourceServer, AuthorizationProvider authorization, Boolean deep) {
|
||||
ResourceRepresentation resource = new ResourceRepresentation();
|
||||
|
||||
resource.setId(model.getId());
|
||||
|
@ -893,6 +841,7 @@ public class ModelToRepresentation {
|
|||
|
||||
resource.setOwner(owner);
|
||||
|
||||
if (deep) {
|
||||
resource.setScopes(model.getScopes().stream().map(model1 -> {
|
||||
ScopeRepresentation scope = new ScopeRepresentation();
|
||||
scope.setId(model1.getId());
|
||||
|
@ -908,7 +857,7 @@ public class ModelToRepresentation {
|
|||
|
||||
if (resource.getType() != null) {
|
||||
ResourceStore resourceStore = authorization.getStoreFactory().getResourceStore();
|
||||
for (Resource typed : resourceStore.findByType(resource.getType())) {
|
||||
for (Resource typed : resourceStore.findByType(resource.getType(), resourceServer.getId())) {
|
||||
if (typed.getOwner().equals(resourceServer.getClientId()) && !typed.getId().equals(resource.getId())) {
|
||||
resource.setTypedScopes(typed.getScopes().stream().map(model1 -> {
|
||||
ScopeRepresentation scope = new ScopeRepresentation();
|
||||
|
@ -923,26 +872,6 @@ public class ModelToRepresentation {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
resource.setPolicies(new ArrayList<>());
|
||||
|
||||
Set<Policy> policies = new HashSet<>();
|
||||
PolicyStore policyStore = authorization.getStoreFactory().getPolicyStore();
|
||||
|
||||
policies.addAll(policyStore.findByResource(resource.getId()));
|
||||
policies.addAll(policyStore.findByResourceType(resource.getType(), resourceServer.getId()));
|
||||
policies.addAll(policyStore.findByScopeIds(resource.getScopes().stream().map(scope -> scope.getId()).collect(Collectors.toList()), resourceServer.getId()));
|
||||
|
||||
for (Policy policyModel : policies) {
|
||||
PolicyRepresentation policy = new PolicyRepresentation();
|
||||
|
||||
policy.setId(policyModel.getId());
|
||||
policy.setName(policyModel.getName());
|
||||
policy.setType(policyModel.getType());
|
||||
|
||||
if (!resource.getPolicies().contains(policy)) {
|
||||
resource.getPolicies().add(policy);
|
||||
}
|
||||
}
|
||||
|
||||
return resource;
|
||||
|
|
|
@ -1898,7 +1898,7 @@ public class RepresentationToModel {
|
|||
|
||||
if (roles != null && !roles.isEmpty()) {
|
||||
try {
|
||||
List<Map> rolesMap = JsonSerialization.readValue(roles, List.class);
|
||||
List<Map> rolesMap = (List<Map>)JsonSerialization.readValue(roles, List.class);
|
||||
config.put("roles", JsonSerialization.writeValueAsString(rolesMap.stream().map(roleConfig -> {
|
||||
String roleName = roleConfig.get("id").toString();
|
||||
String clientId = null;
|
||||
|
@ -1950,7 +1950,7 @@ public class RepresentationToModel {
|
|||
|
||||
if (users != null && !users.isEmpty()) {
|
||||
try {
|
||||
List<String> usersMap = JsonSerialization.readValue(users, List.class);
|
||||
List<String> usersMap = (List<String>) JsonSerialization.readValue(users, List.class);
|
||||
config.put("users", JsonSerialization.writeValueAsString(usersMap.stream().map(userId -> {
|
||||
UserModel user = session.users().getUserByUsername(userId, realm);
|
||||
|
||||
|
@ -1974,12 +1974,12 @@ public class RepresentationToModel {
|
|||
if (scopes != null && !scopes.isEmpty()) {
|
||||
try {
|
||||
ScopeStore scopeStore = storeFactory.getScopeStore();
|
||||
List<String> scopesMap = JsonSerialization.readValue(scopes, List.class);
|
||||
List<String> scopesMap = (List<String>) JsonSerialization.readValue(scopes, List.class);
|
||||
config.put("scopes", JsonSerialization.writeValueAsString(scopesMap.stream().map(scopeName -> {
|
||||
Scope newScope = scopeStore.findByName(scopeName, resourceServer.getId());
|
||||
|
||||
if (newScope == null) {
|
||||
newScope = scopeStore.findById(scopeName);
|
||||
newScope = scopeStore.findById(scopeName, resourceServer.getId());
|
||||
}
|
||||
|
||||
if (newScope == null) {
|
||||
|
@ -1999,13 +1999,11 @@ public class RepresentationToModel {
|
|||
ResourceStore resourceStore = storeFactory.getResourceStore();
|
||||
try {
|
||||
List<String> resources = JsonSerialization.readValue(policyResources, List.class);
|
||||
config.put("resources", JsonSerialization.writeValueAsString(resources.stream().map(new Function<String, String>() {
|
||||
@Override
|
||||
public String apply(String resourceName) {
|
||||
config.put("resources", JsonSerialization.writeValueAsString(resources.stream().map(resourceName -> {
|
||||
Resource resource = resourceStore.findByName(resourceName, resourceServer.getId());
|
||||
|
||||
if (resource == null) {
|
||||
resource = resourceStore.findById(resourceName);
|
||||
resource = resourceStore.findById(resourceName, resourceServer.getId());
|
||||
}
|
||||
|
||||
if (resource == null) {
|
||||
|
@ -2013,7 +2011,6 @@ public class RepresentationToModel {
|
|||
}
|
||||
|
||||
return resource.getId();
|
||||
}
|
||||
}).collect(Collectors.toList())));
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Error while exporting policy [" + policyRepresentation.getName() + "].", e);
|
||||
|
@ -2025,12 +2022,12 @@ public class RepresentationToModel {
|
|||
if (applyPolicies != null && !applyPolicies.isEmpty()) {
|
||||
PolicyStore policyStore = storeFactory.getPolicyStore();
|
||||
try {
|
||||
List<String> policies = JsonSerialization.readValue(applyPolicies, List.class);
|
||||
List<String> policies = (List<String>) JsonSerialization.readValue(applyPolicies, List.class);
|
||||
config.put("applyPolicies", JsonSerialization.writeValueAsString(policies.stream().map(policyName -> {
|
||||
Policy policy = policyStore.findByName(policyName, resourceServer.getId());
|
||||
|
||||
if (policy == null) {
|
||||
policy = policyStore.findById(policyName);
|
||||
policy = policyStore.findById(policyName, resourceServer.getId());
|
||||
}
|
||||
|
||||
if (policy == null) {
|
||||
|
@ -2062,7 +2059,7 @@ public class RepresentationToModel {
|
|||
Policy existing;
|
||||
|
||||
if (policy.getId() != null) {
|
||||
existing = policyStore.findById(policy.getId());
|
||||
existing = policyStore.findById(policy.getId(), resourceServer.getId());
|
||||
} else {
|
||||
existing = policyStore.findByName(policy.getName(), resourceServer.getId());
|
||||
}
|
||||
|
@ -2119,7 +2116,14 @@ public class RepresentationToModel {
|
|||
}
|
||||
}
|
||||
if (!hasScope) {
|
||||
policy.addScope(storeFactory.getScopeStore().findById(scopeId));
|
||||
ResourceServer resourceServer = policy.getResourceServer();
|
||||
Scope scope = storeFactory.getScopeStore().findById(scopeId, resourceServer.getId());
|
||||
|
||||
if (scope == null) {
|
||||
storeFactory.getScopeStore().findByName(scopeId, resourceServer.getId());
|
||||
}
|
||||
|
||||
policy.addScope(scope);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2135,6 +2139,8 @@ public class RepresentationToModel {
|
|||
policy.removeScope(scopeModel);
|
||||
}
|
||||
}
|
||||
|
||||
policy.getConfig().remove("scopes");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2164,7 +2170,7 @@ public class RepresentationToModel {
|
|||
|
||||
|
||||
if (!hasPolicy) {
|
||||
Policy associatedPolicy = policyStore.findById(policyId);
|
||||
Policy associatedPolicy = policyStore.findById(policyId, resourceServer.getId());
|
||||
|
||||
if (associatedPolicy == null) {
|
||||
associatedPolicy = policyStore.findByName(policyId, resourceServer.getId());
|
||||
|
@ -2186,6 +2192,8 @@ public class RepresentationToModel {
|
|||
policy.removeAssociatedPolicy(policyModel);;
|
||||
}
|
||||
}
|
||||
|
||||
policy.getConfig().remove("applyPolicies");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2210,7 +2218,7 @@ public class RepresentationToModel {
|
|||
}
|
||||
}
|
||||
if (!hasResource && !"".equals(resourceId)) {
|
||||
policy.addResource(storeFactory.getResourceStore().findById(resourceId));
|
||||
policy.addResource(storeFactory.getResourceStore().findById(resourceId, policy.getResourceServer().getId()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2227,6 +2235,8 @@ public class RepresentationToModel {
|
|||
policy.removeResource(resourceModel);
|
||||
}
|
||||
}
|
||||
|
||||
policy.getConfig().remove("resources");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2235,7 +2245,7 @@ public class RepresentationToModel {
|
|||
Resource existing;
|
||||
|
||||
if (resource.getId() != null) {
|
||||
existing = resourceStore.findById(resource.getId());
|
||||
existing = resourceStore.findById(resource.getId(), resourceServer.getId());
|
||||
} else {
|
||||
existing = resourceStore.findByName(resource.getName(), resourceServer.getId());
|
||||
}
|
||||
|
@ -2286,7 +2296,7 @@ public class RepresentationToModel {
|
|||
Scope existing;
|
||||
|
||||
if (scope.getId() != null) {
|
||||
existing = scopeStore.findById(scope.getId());
|
||||
existing = scopeStore.findById(scope.getId(), resourceServer.getId());
|
||||
} else {
|
||||
existing = scopeStore.findByName(scope.getName(), resourceServer.getId());
|
||||
}
|
||||
|
|
|
@ -18,14 +18,20 @@
|
|||
|
||||
package org.keycloak.authorization;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.authorization.policy.provider.PolicyProvider;
|
||||
import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
|
||||
import org.keycloak.authorization.store.StoreFactory;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.cache.authorization.CachedStoreFactoryProvider;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
|
@ -33,6 +39,7 @@ import java.util.concurrent.Executor;
|
|||
public class DefaultAuthorizationProviderFactory implements AuthorizationProviderFactory {
|
||||
|
||||
private Executor scheduler;
|
||||
private Map<String, PolicyProviderFactory> policyProviderFactories;
|
||||
|
||||
@Override
|
||||
public AuthorizationProvider create(KeycloakSession session) {
|
||||
|
@ -54,6 +61,7 @@ public class DefaultAuthorizationProviderFactory implements AuthorizationProvide
|
|||
|
||||
@Override
|
||||
public void postInit(KeycloakSessionFactory factory) {
|
||||
policyProviderFactories = configurePolicyProviderFactories(factory);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -74,6 +82,21 @@ public class DefaultAuthorizationProviderFactory implements AuthorizationProvide
|
|||
storeFactory = session.getProvider(StoreFactory.class);
|
||||
}
|
||||
|
||||
return new AuthorizationProvider(session, realm, storeFactory);
|
||||
return new AuthorizationProvider(session, realm, storeFactory, policyProviderFactories);
|
||||
}
|
||||
|
||||
private Map<String, PolicyProviderFactory> configurePolicyProviderFactories(KeycloakSessionFactory keycloakSessionFactory) {
|
||||
List<ProviderFactory> providerFactories = keycloakSessionFactory.getProviderFactories(PolicyProvider.class);
|
||||
|
||||
if (providerFactories.isEmpty()) {
|
||||
throw new RuntimeException("Could not find any policy provider.");
|
||||
}
|
||||
|
||||
HashMap<String, PolicyProviderFactory> providers = new HashMap<>();
|
||||
|
||||
providerFactories.forEach(providerFactory -> providers.put(providerFactory.getId(), (PolicyProviderFactory) providerFactory));
|
||||
|
||||
return providers;
|
||||
}
|
||||
|
||||
}
|
|
@ -17,6 +17,27 @@
|
|||
*/
|
||||
package org.keycloak.authorization.admin;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.container.AsyncResponse;
|
||||
import javax.ws.rs.container.Suspended;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import org.jboss.resteasy.spi.HttpRequest;
|
||||
import org.keycloak.authorization.AuthorizationProvider;
|
||||
import org.keycloak.authorization.admin.representation.PolicyEvaluationRequest;
|
||||
|
@ -46,29 +67,10 @@ import org.keycloak.models.UserSessionModel;
|
|||
import org.keycloak.protocol.ProtocolMapper;
|
||||
import org.keycloak.protocol.oidc.mappers.OIDCAccessTokenMapper;
|
||||
import org.keycloak.representations.AccessToken;
|
||||
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
|
||||
import org.keycloak.services.Urls;
|
||||
import org.keycloak.services.resources.admin.RealmAuth;
|
||||
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.container.AsyncResponse;
|
||||
import javax.ws.rs.container.Suspended;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
|
@ -144,40 +146,35 @@ public class PolicyEvaluationService {
|
|||
private List<ResourcePermission> createPermissions(PolicyEvaluationRequest representation, EvaluationContext evaluationContext, AuthorizationProvider authorization) {
|
||||
List<PolicyEvaluationRequest.Resource> resources = representation.getResources();
|
||||
return resources.stream().flatMap((Function<PolicyEvaluationRequest.Resource, Stream<ResourcePermission>>) resource -> {
|
||||
Set<String> givenScopes = resource.getScopes();
|
||||
StoreFactory storeFactory = authorization.getStoreFactory();
|
||||
if (resource == null) {
|
||||
resource = new PolicyEvaluationRequest.Resource();
|
||||
}
|
||||
|
||||
Set<ScopeRepresentation> givenScopes = resource.getScopes();
|
||||
|
||||
if (givenScopes == null) {
|
||||
givenScopes = new HashSet();
|
||||
}
|
||||
|
||||
StoreFactory storeFactory = authorization.getStoreFactory();
|
||||
Set<String> scopeNames = givenScopes.stream().map(ScopeRepresentation::getName).collect(Collectors.toSet());
|
||||
|
||||
if (resource.getId() != null) {
|
||||
Resource resourceModel = storeFactory.getResourceStore().findById(resource.getId());
|
||||
return Permissions.createResourcePermissions(resourceModel, givenScopes, authorization).stream();
|
||||
Resource resourceModel = storeFactory.getResourceStore().findById(resource.getId(), resourceServer.getId());
|
||||
return Permissions.createResourcePermissions(resourceModel, scopeNames, authorization).stream();
|
||||
} else if (resource.getType() != null) {
|
||||
Set<String> finalGivenScopes = givenScopes;
|
||||
return storeFactory.getResourceStore().findByType(resource.getType()).stream().flatMap(resource1 -> Permissions.createResourcePermissions(resource1, finalGivenScopes, authorization).stream());
|
||||
return storeFactory.getResourceStore().findByType(resource.getType(), resourceServer.getId()).stream().flatMap(resource1 -> Permissions.createResourcePermissions(resource1, scopeNames, authorization).stream());
|
||||
} else {
|
||||
ScopeStore scopeStore = storeFactory.getScopeStore();
|
||||
List<Scope> scopes = givenScopes.stream().map(scopeName -> scopeStore.findByName(scopeName, this.resourceServer.getId())).collect(Collectors.toList());
|
||||
List<ResourcePermission> collect = scopes.stream().map(scope -> new ResourcePermission(null, asList(scope), resourceServer)).collect(Collectors.toList());
|
||||
List<Scope> scopes = scopeNames.stream().map(scopeName -> scopeStore.findByName(scopeName, this.resourceServer.getId())).collect(Collectors.toList());
|
||||
List<ResourcePermission> collect = new ArrayList<ResourcePermission>();
|
||||
|
||||
if (scopes.isEmpty()) {
|
||||
scopes = scopeStore.findByResourceServer(resourceServer.getId());
|
||||
if (!scopes.isEmpty()) {
|
||||
collect.addAll(scopes.stream().map(scope -> new ResourcePermission(null, asList(scope), resourceServer)).collect(Collectors.toList()));
|
||||
} else {
|
||||
collect.addAll(Permissions.all(resourceServer, evaluationContext.getIdentity(), authorization));
|
||||
}
|
||||
|
||||
for (Scope scope : scopes) {
|
||||
collect.addAll(storeFactory.getResourceStore().findByScope(scope.getId()).stream().map(resource12 -> new ResourcePermission(resource12, asList(scope), resourceServer)).collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
collect.addAll(storeFactory.getResourceStore().findByResourceServer(resourceServer.getId()).stream().map(new Function<Resource, ResourcePermission>() {
|
||||
@Override
|
||||
public ResourcePermission apply(Resource resource) {
|
||||
return new ResourcePermission(resource, resource.getScopes(), resourceServer);
|
||||
}
|
||||
}).collect(Collectors.toList()));
|
||||
|
||||
return collect.stream();
|
||||
}
|
||||
}).collect(Collectors.toList());
|
||||
|
|
|
@ -17,21 +17,15 @@
|
|||
*/
|
||||
package org.keycloak.authorization.admin;
|
||||
|
||||
import org.jboss.resteasy.annotations.cache.NoCache;
|
||||
import org.jboss.resteasy.spi.ResteasyProviderFactory;
|
||||
import org.keycloak.authorization.AuthorizationProvider;
|
||||
import org.keycloak.authorization.model.Policy;
|
||||
import org.keycloak.authorization.model.ResourceServer;
|
||||
import org.keycloak.authorization.policy.provider.PolicyProviderAdminService;
|
||||
import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
|
||||
import org.keycloak.authorization.store.PolicyStore;
|
||||
import org.keycloak.authorization.store.StoreFactory;
|
||||
import org.keycloak.models.Constants;
|
||||
import org.keycloak.models.utils.ModelToRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.PolicyProviderRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
|
||||
import org.keycloak.services.resources.admin.RealmAuth;
|
||||
import static org.keycloak.models.utils.ModelToRepresentation.toRepresentation;
|
||||
import static org.keycloak.models.utils.RepresentationToModel.toModel;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.DELETE;
|
||||
|
@ -44,15 +38,24 @@ import javax.ws.rs.Produces;
|
|||
import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.Response.Status;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.keycloak.models.utils.ModelToRepresentation.toRepresentation;
|
||||
import static org.keycloak.models.utils.RepresentationToModel.toModel;
|
||||
import org.jboss.resteasy.annotations.cache.NoCache;
|
||||
import org.jboss.resteasy.spi.ResteasyProviderFactory;
|
||||
import org.keycloak.authorization.AuthorizationProvider;
|
||||
import org.keycloak.authorization.model.Policy;
|
||||
import org.keycloak.authorization.model.ResourceServer;
|
||||
import org.keycloak.authorization.policy.provider.PolicyProviderAdminService;
|
||||
import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
|
||||
import org.keycloak.authorization.store.PolicyStore;
|
||||
import org.keycloak.authorization.store.ResourceStore;
|
||||
import org.keycloak.authorization.store.StoreFactory;
|
||||
import org.keycloak.models.Constants;
|
||||
import org.keycloak.models.utils.ModelToRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.PolicyProviderRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
|
||||
import org.keycloak.services.resources.admin.RealmAuth;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
|
@ -100,7 +103,7 @@ public class PolicyService {
|
|||
this.auth.requireManage();
|
||||
representation.setId(id);
|
||||
StoreFactory storeFactory = authorization.getStoreFactory();
|
||||
Policy policy = storeFactory.getPolicyStore().findById(representation.getId());
|
||||
Policy policy = storeFactory.getPolicyStore().findById(representation.getId(), resourceServer.getId());
|
||||
|
||||
if (policy == null) {
|
||||
return Response.status(Status.NOT_FOUND).build();
|
||||
|
@ -127,7 +130,7 @@ public class PolicyService {
|
|||
this.auth.requireManage();
|
||||
StoreFactory storeFactory = authorization.getStoreFactory();
|
||||
PolicyStore policyStore = storeFactory.getPolicyStore();
|
||||
Policy policy = policyStore.findById(id);
|
||||
Policy policy = policyStore.findById(id, resourceServer.getId());
|
||||
|
||||
if (policy == null) {
|
||||
return Response.status(Status.NOT_FOUND).build();
|
||||
|
@ -143,7 +146,7 @@ public class PolicyService {
|
|||
}
|
||||
}
|
||||
|
||||
policyStore.findDependentPolicies(id).forEach(dependentPolicy -> {
|
||||
policyStore.findDependentPolicies(id, resourceServer.getId()).forEach(dependentPolicy -> {
|
||||
if (dependentPolicy.getAssociatedPolicies().size() == 1) {
|
||||
policyStore.delete(dependentPolicy.getId());
|
||||
} else {
|
||||
|
@ -163,13 +166,109 @@ public class PolicyService {
|
|||
public Response findById(@PathParam("id") String id) {
|
||||
this.auth.requireView();
|
||||
StoreFactory storeFactory = authorization.getStoreFactory();
|
||||
Policy model = storeFactory.getPolicyStore().findById(id);
|
||||
Policy model = storeFactory.getPolicyStore().findById(id, resourceServer.getId());
|
||||
|
||||
if (model == null) {
|
||||
return Response.status(Status.NOT_FOUND).build();
|
||||
}
|
||||
|
||||
return Response.ok(toRepresentation(model, authorization)).build();
|
||||
return Response.ok(toRepresentation(model)).build();
|
||||
}
|
||||
|
||||
@Path("{id}/dependentPolicies")
|
||||
@GET
|
||||
@Produces("application/json")
|
||||
@NoCache
|
||||
public Response getDependentPolicies(@PathParam("id") String id) {
|
||||
this.auth.requireView();
|
||||
StoreFactory storeFactory = authorization.getStoreFactory();
|
||||
Policy model = storeFactory.getPolicyStore().findById(id, resourceServer.getId());
|
||||
|
||||
if (model == null) {
|
||||
return Response.status(Status.NOT_FOUND).build();
|
||||
}
|
||||
|
||||
List<Policy> policies = authorization.getStoreFactory().getPolicyStore().findDependentPolicies(model.getId(), resourceServer.getId());
|
||||
|
||||
return Response.ok(policies.stream().map(policy -> {
|
||||
PolicyRepresentation representation1 = new PolicyRepresentation();
|
||||
|
||||
representation1.setId(policy.getId());
|
||||
representation1.setName(policy.getName());
|
||||
representation1.setType(policy.getType());
|
||||
|
||||
return representation1;
|
||||
}).collect(Collectors.toList())).build();
|
||||
}
|
||||
|
||||
@Path("{id}/scopes")
|
||||
@GET
|
||||
@Produces("application/json")
|
||||
@NoCache
|
||||
public Response getScopes(@PathParam("id") String id) {
|
||||
this.auth.requireView();
|
||||
StoreFactory storeFactory = authorization.getStoreFactory();
|
||||
Policy model = storeFactory.getPolicyStore().findById(id, resourceServer.getId());
|
||||
|
||||
if (model == null) {
|
||||
return Response.status(Status.NOT_FOUND).build();
|
||||
}
|
||||
|
||||
return Response.ok(model.getScopes().stream().map(scope -> {
|
||||
ScopeRepresentation representation = new ScopeRepresentation();
|
||||
|
||||
representation.setId(scope.getId());
|
||||
representation.setName(scope.getName());
|
||||
|
||||
return representation;
|
||||
}).collect(Collectors.toList())).build();
|
||||
}
|
||||
|
||||
@Path("{id}/resources")
|
||||
@GET
|
||||
@Produces("application/json")
|
||||
@NoCache
|
||||
public Response getResources(@PathParam("id") String id) {
|
||||
this.auth.requireView();
|
||||
StoreFactory storeFactory = authorization.getStoreFactory();
|
||||
Policy model = storeFactory.getPolicyStore().findById(id, resourceServer.getId());
|
||||
|
||||
if (model == null) {
|
||||
return Response.status(Status.NOT_FOUND).build();
|
||||
}
|
||||
|
||||
return Response.ok(model.getResources().stream().map(resource -> {
|
||||
ResourceRepresentation representation = new ResourceRepresentation();
|
||||
|
||||
representation.setId(resource.getId());
|
||||
representation.setName(resource.getName());
|
||||
|
||||
return representation;
|
||||
}).collect(Collectors.toList())).build();
|
||||
}
|
||||
|
||||
@Path("{id}/associatedPolicies")
|
||||
@GET
|
||||
@Produces("application/json")
|
||||
@NoCache
|
||||
public Response getAssociatedPolicies(@PathParam("id") String id) {
|
||||
this.auth.requireView();
|
||||
StoreFactory storeFactory = authorization.getStoreFactory();
|
||||
Policy model = storeFactory.getPolicyStore().findById(id, resourceServer.getId());
|
||||
|
||||
if (model == null) {
|
||||
return Response.status(Status.NOT_FOUND).build();
|
||||
}
|
||||
|
||||
return Response.ok(model.getAssociatedPolicies().stream().map(policy -> {
|
||||
PolicyRepresentation representation1 = new PolicyRepresentation();
|
||||
|
||||
representation1.setId(policy.getId());
|
||||
representation1.setName(policy.getName());
|
||||
representation1.setType(policy.getType());
|
||||
|
||||
return representation1;
|
||||
}).collect(Collectors.toList())).build();
|
||||
}
|
||||
|
||||
@Path("/search")
|
||||
|
@ -190,13 +289,14 @@ public class PolicyService {
|
|||
return Response.status(Status.OK).build();
|
||||
}
|
||||
|
||||
return Response.ok(toRepresentation(model, authorization)).build();
|
||||
return Response.ok(toRepresentation(model)).build();
|
||||
}
|
||||
|
||||
@GET
|
||||
@Produces("application/json")
|
||||
@NoCache
|
||||
public Response findAll(@QueryParam("name") String name,
|
||||
public Response findAll(@QueryParam("policyId") String id,
|
||||
@QueryParam("name") String name,
|
||||
@QueryParam("type") String type,
|
||||
@QueryParam("resource") String resource,
|
||||
@QueryParam("permission") Boolean permission,
|
||||
|
@ -206,6 +306,10 @@ public class PolicyService {
|
|||
|
||||
Map<String, String[]> search = new HashMap<>();
|
||||
|
||||
if (id != null && !"".equals(id.trim())) {
|
||||
search.put("id", new String[] {id});
|
||||
}
|
||||
|
||||
if (name != null && !"".equals(name.trim())) {
|
||||
search.put("name", new String[] {name});
|
||||
}
|
||||
|
@ -216,16 +320,17 @@ public class PolicyService {
|
|||
|
||||
StoreFactory storeFactory = authorization.getStoreFactory();
|
||||
|
||||
PolicyStore policyStore = storeFactory.getPolicyStore();
|
||||
if (resource != null && !"".equals(resource.trim())) {
|
||||
List<Policy> policies = new ArrayList<>();
|
||||
HashMap<String, String[]> resourceSearch = new HashMap<>();
|
||||
|
||||
resourceSearch.put("name", new String[] {resource});
|
||||
|
||||
storeFactory.getResourceStore().findByResourceServer(resourceSearch, resourceServer.getId(), -1, -1).forEach(resource1 -> {
|
||||
ResourceRepresentation resourceRepresentation = ModelToRepresentation.toRepresentation(resource1, resourceServer, authorization);
|
||||
resourceRepresentation.getPolicies().forEach(policyRepresentation -> {
|
||||
Policy associated = storeFactory.getPolicyStore().findById(policyRepresentation.getId());
|
||||
ResourceStore resourceStore = storeFactory.getResourceStore();
|
||||
resourceStore.findByResourceServer(resourceSearch, resourceServer.getId(), -1, -1).forEach(resource1 -> {
|
||||
policyStore.findByResource(resource1.getId(), resourceServer.getId()).forEach(policyRepresentation -> {
|
||||
Policy associated = policyStore.findById(policyRepresentation.getId(), resourceServer.getId());
|
||||
policies.add(associated);
|
||||
findAssociatedPolicies(associated, policies);
|
||||
});
|
||||
|
@ -243,8 +348,8 @@ public class PolicyService {
|
|||
}
|
||||
|
||||
return Response.ok(
|
||||
storeFactory.getPolicyStore().findByResourceServer(search, resourceServer.getId(), firstResult != null ? firstResult : -1, maxResult != null ? maxResult : Constants.DEFAULT_MAX_RESULTS).stream()
|
||||
.map(policy -> toRepresentation(policy, authorization))
|
||||
policyStore.findByResourceServer(search, resourceServer.getId(), firstResult != null ? firstResult : -1, maxResult != null ? maxResult : Constants.DEFAULT_MAX_RESULTS).stream()
|
||||
.map(policy -> toRepresentation(policy))
|
||||
.collect(Collectors.toList()))
|
||||
.build();
|
||||
}
|
||||
|
|
|
@ -32,8 +32,10 @@ import org.keycloak.models.KeycloakSession;
|
|||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserProvider;
|
||||
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ResourceOwnerRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
|
||||
import org.keycloak.services.ErrorResponse;
|
||||
import org.keycloak.services.resources.admin.RealmAuth;
|
||||
|
||||
|
@ -48,10 +50,15 @@ import javax.ws.rs.Produces;
|
|||
import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.Response.Status;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.keycloak.models.utils.ModelToRepresentation.toRepresentation;
|
||||
|
@ -127,7 +134,7 @@ public class ResourceSetService {
|
|||
resource.setId(id);
|
||||
StoreFactory storeFactory = this.authorization.getStoreFactory();
|
||||
ResourceStore resourceStore = storeFactory.getResourceStore();
|
||||
Resource model = resourceStore.findById(resource.getId());
|
||||
Resource model = resourceStore.findById(resource.getId(), resourceServer.getId());
|
||||
|
||||
if (model == null) {
|
||||
return Response.status(Status.NOT_FOUND).build();
|
||||
|
@ -143,14 +150,14 @@ public class ResourceSetService {
|
|||
public Response delete(@PathParam("id") String id) {
|
||||
requireManage();
|
||||
StoreFactory storeFactory = authorization.getStoreFactory();
|
||||
Resource resource = storeFactory.getResourceStore().findById(id);
|
||||
Resource resource = storeFactory.getResourceStore().findById(id, resourceServer.getId());
|
||||
|
||||
if (resource == null) {
|
||||
return Response.status(Status.NOT_FOUND).build();
|
||||
}
|
||||
|
||||
PolicyStore policyStore = storeFactory.getPolicyStore();
|
||||
List<Policy> policies = policyStore.findByResource(id);
|
||||
List<Policy> policies = policyStore.findByResource(id, resourceServer.getId());
|
||||
|
||||
for (Policy policyModel : policies) {
|
||||
if (policyModel.getResources().size() == 1) {
|
||||
|
@ -172,13 +179,93 @@ public class ResourceSetService {
|
|||
public Response findById(@PathParam("id") String id) {
|
||||
requireView();
|
||||
StoreFactory storeFactory = authorization.getStoreFactory();
|
||||
Resource model = storeFactory.getResourceStore().findById(id);
|
||||
Resource model = storeFactory.getResourceStore().findById(id, resourceServer.getId());
|
||||
|
||||
if (model == null) {
|
||||
return Response.status(Status.NOT_FOUND).build();
|
||||
}
|
||||
|
||||
return Response.ok(toRepresentation(model, this.resourceServer, authorization)).build();
|
||||
return Response.ok(toRepresentation(model, this.resourceServer, authorization, true)).build();
|
||||
}
|
||||
|
||||
@Path("{id}/scopes")
|
||||
@GET
|
||||
@NoCache
|
||||
@Produces("application/json")
|
||||
public Response getScopes(@PathParam("id") String id) {
|
||||
requireView();
|
||||
StoreFactory storeFactory = authorization.getStoreFactory();
|
||||
Resource model = storeFactory.getResourceStore().findById(id, resourceServer.getId());
|
||||
|
||||
if (model == null) {
|
||||
return Response.status(Status.NOT_FOUND).build();
|
||||
}
|
||||
|
||||
List<ScopeRepresentation> scopes = model.getScopes().stream().map(scope -> {
|
||||
ScopeRepresentation representation = new ScopeRepresentation();
|
||||
|
||||
representation.setId(scope.getId());
|
||||
representation.setName(scope.getName());
|
||||
|
||||
return representation;
|
||||
}).collect(Collectors.toList());
|
||||
|
||||
if (model.getType() != null) {
|
||||
ResourceStore resourceStore = authorization.getStoreFactory().getResourceStore();
|
||||
for (Resource typed : resourceStore.findByType(model.getType(), resourceServer.getId())) {
|
||||
if (typed.getOwner().equals(resourceServer.getClientId()) && !typed.getId().equals(model.getId())) {
|
||||
scopes.addAll(typed.getScopes().stream().map(model1 -> {
|
||||
ScopeRepresentation scope = new ScopeRepresentation();
|
||||
scope.setId(model1.getId());
|
||||
scope.setName(model1.getName());
|
||||
String iconUri = model1.getIconUri();
|
||||
if (iconUri != null) {
|
||||
scope.setIconUri(iconUri);
|
||||
}
|
||||
return scope;
|
||||
}).filter(scopeRepresentation -> !scopes.contains(scopeRepresentation)).collect(Collectors.toList()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Response.ok(scopes).build();
|
||||
}
|
||||
|
||||
@Path("{id}/permissions")
|
||||
@GET
|
||||
@NoCache
|
||||
@Produces("application/json")
|
||||
public Response getPermissions(@PathParam("id") String id) {
|
||||
requireView();
|
||||
StoreFactory storeFactory = authorization.getStoreFactory();
|
||||
Resource model = storeFactory.getResourceStore().findById(id, resourceServer.getId());
|
||||
|
||||
if (model == null) {
|
||||
return Response.status(Status.NOT_FOUND).build();
|
||||
}
|
||||
|
||||
PolicyStore policyStore = authorization.getStoreFactory().getPolicyStore();
|
||||
Set<Policy> policies = new HashSet<>();
|
||||
|
||||
policies.addAll(policyStore.findByResource(model.getId(), resourceServer.getId()));
|
||||
policies.addAll(policyStore.findByResourceType(model.getType(), resourceServer.getId()));
|
||||
policies.addAll(policyStore.findByScopeIds(model.getScopes().stream().map(scope -> scope.getId()).collect(Collectors.toList()), resourceServer.getId()));
|
||||
|
||||
List<PolicyRepresentation> representation = new ArrayList<>();
|
||||
|
||||
for (Policy policyModel : policies) {
|
||||
PolicyRepresentation policy = new PolicyRepresentation();
|
||||
|
||||
policy.setId(policyModel.getId());
|
||||
policy.setName(policyModel.getName());
|
||||
policy.setType(policyModel.getType());
|
||||
|
||||
if (!representation.contains(policy)) {
|
||||
representation.add(policy);
|
||||
}
|
||||
}
|
||||
|
||||
return Response.ok(representation).build();
|
||||
}
|
||||
|
||||
@Path("/search")
|
||||
|
@ -205,18 +292,29 @@ public class ResourceSetService {
|
|||
@GET
|
||||
@NoCache
|
||||
@Produces("application/json")
|
||||
public Response find(@QueryParam("name") String name,
|
||||
public Response find(@QueryParam("_id") String id,
|
||||
@QueryParam("name") String name,
|
||||
@QueryParam("uri") String uri,
|
||||
@QueryParam("owner") String owner,
|
||||
@QueryParam("type") String type,
|
||||
@QueryParam("scope") String scope,
|
||||
@QueryParam("deep") Boolean deep,
|
||||
@QueryParam("first") Integer firstResult,
|
||||
@QueryParam("max") Integer maxResult) {
|
||||
requireView();
|
||||
|
||||
StoreFactory storeFactory = authorization.getStoreFactory();
|
||||
|
||||
if (deep == null) {
|
||||
deep = true;
|
||||
}
|
||||
|
||||
Map<String, String[]> search = new HashMap<>();
|
||||
|
||||
if (id != null && !"".equals(id.trim())) {
|
||||
search.put("id", new String[] {id});
|
||||
}
|
||||
|
||||
if (name != null && !"".equals(name.trim())) {
|
||||
search.put("name", new String[] {name});
|
||||
}
|
||||
|
@ -260,9 +358,10 @@ public class ResourceSetService {
|
|||
search.put("scope", scopes.stream().map(Scope::getId).toArray(String[]::new));
|
||||
}
|
||||
|
||||
Boolean finalDeep = deep;
|
||||
return Response.ok(
|
||||
storeFactory.getResourceStore().findByResourceServer(search, this.resourceServer.getId(), firstResult != null ? firstResult : -1, maxResult != null ? maxResult : Constants.DEFAULT_MAX_RESULTS).stream()
|
||||
.map(resource -> toRepresentation(resource, this.resourceServer, authorization))
|
||||
.map(resource -> toRepresentation(resource, resourceServer, authorization, finalDeep))
|
||||
.collect(Collectors.toList()))
|
||||
.build();
|
||||
}
|
||||
|
|
|
@ -26,6 +26,8 @@ import org.keycloak.authorization.model.Scope;
|
|||
import org.keycloak.authorization.store.PolicyStore;
|
||||
import org.keycloak.authorization.store.StoreFactory;
|
||||
import org.keycloak.models.Constants;
|
||||
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
|
||||
import org.keycloak.services.ErrorResponse;
|
||||
import org.keycloak.services.resources.admin.RealmAuth;
|
||||
|
@ -41,6 +43,7 @@ import javax.ws.rs.Produces;
|
|||
import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.Response.Status;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
@ -85,7 +88,7 @@ public class ScopeService {
|
|||
this.auth.requireManage();
|
||||
scope.setId(id);
|
||||
StoreFactory storeFactory = authorization.getStoreFactory();
|
||||
Scope model = storeFactory.getScopeStore().findById(scope.getId());
|
||||
Scope model = storeFactory.getScopeStore().findById(scope.getId(), resourceServer.getId());
|
||||
|
||||
if (model == null) {
|
||||
return Response.status(Status.NOT_FOUND).build();
|
||||
|
@ -101,13 +104,13 @@ public class ScopeService {
|
|||
public Response delete(@PathParam("id") String id) {
|
||||
this.auth.requireManage();
|
||||
StoreFactory storeFactory = authorization.getStoreFactory();
|
||||
List<Resource> resources = storeFactory.getResourceStore().findByScope(id);
|
||||
List<Resource> resources = storeFactory.getResourceStore().findByScope(Arrays.asList(id), resourceServer.getId());
|
||||
|
||||
if (!resources.isEmpty()) {
|
||||
return ErrorResponse.exists("Scopes can not be removed while associated with resources.");
|
||||
}
|
||||
|
||||
Scope scope = storeFactory.getScopeStore().findById(id);
|
||||
Scope scope = storeFactory.getScopeStore().findById(id, resourceServer.getId());
|
||||
|
||||
if (scope == null) {
|
||||
return Response.status(Status.NOT_FOUND).build();
|
||||
|
@ -134,7 +137,7 @@ public class ScopeService {
|
|||
@Produces("application/json")
|
||||
public Response findById(@PathParam("id") String id) {
|
||||
this.auth.requireView();
|
||||
Scope model = this.authorization.getStoreFactory().getScopeStore().findById(id);
|
||||
Scope model = this.authorization.getStoreFactory().getScopeStore().findById(id, resourceServer.getId());
|
||||
|
||||
if (model == null) {
|
||||
return Response.status(Status.NOT_FOUND).build();
|
||||
|
@ -143,6 +146,53 @@ public class ScopeService {
|
|||
return Response.ok(toRepresentation(model, this.authorization)).build();
|
||||
}
|
||||
|
||||
@Path("{id}/resources")
|
||||
@GET
|
||||
@Produces("application/json")
|
||||
public Response getResources(@PathParam("id") String id) {
|
||||
this.auth.requireView();
|
||||
StoreFactory storeFactory = this.authorization.getStoreFactory();
|
||||
Scope model = storeFactory.getScopeStore().findById(id, resourceServer.getId());
|
||||
|
||||
if (model == null) {
|
||||
return Response.status(Status.NOT_FOUND).build();
|
||||
}
|
||||
|
||||
return Response.ok(storeFactory.getResourceStore().findByScope(Arrays.asList(model.getId()), resourceServer.getId()).stream().map(resource -> {
|
||||
ResourceRepresentation representation = new ResourceRepresentation();
|
||||
|
||||
representation.setId(resource.getId());
|
||||
representation.setName(resource.getName());
|
||||
|
||||
return representation;
|
||||
}).collect(Collectors.toList())).build();
|
||||
}
|
||||
|
||||
@Path("{id}/permissions")
|
||||
@GET
|
||||
@Produces("application/json")
|
||||
public Response getPermissions(@PathParam("id") String id) {
|
||||
this.auth.requireView();
|
||||
StoreFactory storeFactory = this.authorization.getStoreFactory();
|
||||
Scope model = storeFactory.getScopeStore().findById(id, resourceServer.getId());
|
||||
|
||||
if (model == null) {
|
||||
return Response.status(Status.NOT_FOUND).build();
|
||||
}
|
||||
|
||||
PolicyStore policyStore = storeFactory.getPolicyStore();
|
||||
|
||||
return Response.ok(policyStore.findByScopeIds(Arrays.asList(model.getId()), resourceServer.getId()).stream().map(policy -> {
|
||||
PolicyRepresentation representation = new PolicyRepresentation();
|
||||
|
||||
representation.setId(policy.getId());
|
||||
representation.setName(policy.getName());
|
||||
representation.setType(policy.getType());
|
||||
|
||||
return representation;
|
||||
}).collect(Collectors.toList())).build();
|
||||
}
|
||||
|
||||
@Path("/search")
|
||||
@GET
|
||||
@Produces("application/json")
|
||||
|
@ -166,20 +216,31 @@ public class ScopeService {
|
|||
|
||||
@GET
|
||||
@Produces("application/json")
|
||||
public Response findAll(@QueryParam("name") String name,
|
||||
public Response findAll(@QueryParam("scopeId") String id,
|
||||
@QueryParam("name") String name,
|
||||
@QueryParam("deep") Boolean deep,
|
||||
@QueryParam("first") Integer firstResult,
|
||||
@QueryParam("max") Integer maxResult) {
|
||||
this.auth.requireView();
|
||||
|
||||
if (deep == null) {
|
||||
deep = true;
|
||||
}
|
||||
|
||||
Map<String, String[]> search = new HashMap<>();
|
||||
|
||||
if (id != null && !"".equals(id.trim())) {
|
||||
search.put("id", new String[] {id});
|
||||
}
|
||||
|
||||
if (name != null && !"".equals(name.trim())) {
|
||||
search.put("name", new String[] {name});
|
||||
}
|
||||
|
||||
Boolean finalDeep = deep;
|
||||
return Response.ok(
|
||||
this.authorization.getStoreFactory().getScopeStore().findByResourceServer(search, this.resourceServer.getId(), firstResult != null ? firstResult : -1, maxResult != null ? maxResult : Constants.DEFAULT_MAX_RESULTS).stream()
|
||||
.map(scope -> toRepresentation(scope, this.authorization))
|
||||
.map(scope -> toRepresentation(scope, this.authorization, finalDeep))
|
||||
.collect(Collectors.toList()))
|
||||
.build();
|
||||
}
|
||||
|
|
|
@ -22,6 +22,8 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
|
@ -82,42 +84,7 @@ public class PolicyEvaluationRequest {
|
|||
this.entitlements = entitlements;
|
||||
}
|
||||
|
||||
public static class Resource {
|
||||
private String id;
|
||||
private String name;
|
||||
private String type;
|
||||
private Set<String> scopes;
|
||||
public static class Resource extends ResourceRepresentation {
|
||||
|
||||
public String getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(final String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public Set<String> getScopes() {
|
||||
return scopes;
|
||||
}
|
||||
|
||||
public void setScopes(final Set<String> scopes) {
|
||||
this.scopes = scopes;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,9 +35,11 @@ import org.keycloak.representations.idm.authorization.ScopeRepresentation;
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
@ -62,7 +64,7 @@ public class PolicyEvaluationResponse {
|
|||
AccessToken accessToken = identity.getAccessToken();
|
||||
AccessToken.Authorization authorizationData = new AccessToken.Authorization();
|
||||
|
||||
authorizationData.setPermissions(Permissions.allPermits(results, authorization));
|
||||
authorizationData.setPermissions(Permissions.permits(results, authorization, resourceServer.getId()));
|
||||
accessToken.setAuthorization(authorizationData);
|
||||
|
||||
response.rpt = accessToken;
|
||||
|
@ -80,7 +82,12 @@ public class PolicyEvaluationResponse {
|
|||
resultsRep.add(rep);
|
||||
|
||||
if (result.getPermission().getResource() != null) {
|
||||
rep.setResource(ModelToRepresentation.toRepresentation(result.getPermission().getResource(), resourceServer, authorization));
|
||||
ResourceRepresentation resource = new ResourceRepresentation();
|
||||
|
||||
resource.setId(result.getPermission().getResource().getId());
|
||||
resource.setName(result.getPermission().getResource().getName());
|
||||
|
||||
rep.setResource(resource);
|
||||
} else {
|
||||
ResourceRepresentation resource = new ResourceRepresentation();
|
||||
|
||||
|
@ -89,7 +96,14 @@ public class PolicyEvaluationResponse {
|
|||
rep.setResource(resource);
|
||||
}
|
||||
|
||||
rep.setScopes(result.getPermission().getScopes().stream().map(scope -> ModelToRepresentation.toRepresentation(scope, authorization)).collect(Collectors.toList()));
|
||||
rep.setScopes(result.getPermission().getScopes().stream().map(scope -> {
|
||||
ScopeRepresentation representation = new ScopeRepresentation();
|
||||
|
||||
representation.setId(scope.getId());
|
||||
representation.setName(scope.getName());
|
||||
|
||||
return representation;
|
||||
}).collect(Collectors.toList()));
|
||||
|
||||
List<PolicyResultRepresentation> policies = new ArrayList<>();
|
||||
|
||||
|
@ -100,7 +114,7 @@ public class PolicyEvaluationResponse {
|
|||
rep.setPolicies(policies);
|
||||
}
|
||||
|
||||
resultsRep.sort((o1, o2) -> o1.getResource().getName().compareTo(o2.getResource().getName()));
|
||||
resultsRep.sort(Comparator.comparing(o -> o.getResource().getName()));
|
||||
|
||||
Map<String, EvaluationResultRepresentation> groupedResults = new HashMap<>();
|
||||
|
||||
|
@ -127,17 +141,29 @@ public class PolicyEvaluationResponse {
|
|||
List<ScopeRepresentation> currentScopes = evaluationResultRepresentation.getScopes();
|
||||
|
||||
if (currentScopes != null) {
|
||||
List<ScopeRepresentation> allowedScopes = result.getAllowedScopes();
|
||||
for (ScopeRepresentation scope : currentScopes) {
|
||||
if (!scopes.contains(scope)) {
|
||||
scopes.add(scope);
|
||||
}
|
||||
if (evaluationResultRepresentation.getStatus().equals(Effect.PERMIT)) {
|
||||
List<ScopeRepresentation> allowedScopes = result.getAllowedScopes();
|
||||
if (!allowedScopes.contains(scope)) {
|
||||
allowedScopes.add(scope);
|
||||
}
|
||||
} else {
|
||||
evaluationResultRepresentation.getPolicies().forEach(new Consumer<PolicyResultRepresentation>() {
|
||||
@Override
|
||||
public void accept(PolicyResultRepresentation policyResultRepresentation) {
|
||||
if (policyResultRepresentation.getStatus().equals(Effect.PERMIT)) {
|
||||
if (!allowedScopes.contains(scope)) {
|
||||
allowedScopes.add(scope);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
result.setAllowedScopes(allowedScopes);
|
||||
}
|
||||
|
||||
if (resource.getId() != null) {
|
||||
|
@ -160,18 +186,14 @@ public class PolicyEvaluationResponse {
|
|||
}
|
||||
|
||||
if (policy.getStatus().equals(Effect.DENY)) {
|
||||
Policy policyModel = authorization.getStoreFactory().getPolicyStore().findById(policy.getPolicy().getId());
|
||||
Policy policyModel = authorization.getStoreFactory().getPolicyStore().findById(policy.getPolicy().getId(), resourceServer.getId());
|
||||
for (ScopeRepresentation scope : policyModel.getScopes().stream().map(scopeModel -> ModelToRepresentation.toRepresentation(scopeModel, authorization)).collect(Collectors.toList())) {
|
||||
if (!policy.getScopes().contains(scope)) {
|
||||
if (!policy.getScopes().contains(scope) && policyModel.getScopes().stream().filter(policyScope -> policyScope.getId().equals(scope.getId())).findFirst().isPresent()) {
|
||||
result.getAllowedScopes().remove(scope);
|
||||
policy.getScopes().add(scope);
|
||||
}
|
||||
}
|
||||
for (ScopeRepresentation scope : currentScopes) {
|
||||
if (!policy.getScopes().contains(scope)) {
|
||||
policy.getScopes().add(scope);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {}
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -183,7 +205,14 @@ public class PolicyEvaluationResponse {
|
|||
private static PolicyResultRepresentation toRepresentation(PolicyResult policy, AuthorizationProvider authorization) {
|
||||
PolicyResultRepresentation policyResultRep = new PolicyResultRepresentation();
|
||||
|
||||
policyResultRep.setPolicy(ModelToRepresentation.toRepresentation(policy.getPolicy(), authorization));
|
||||
PolicyRepresentation representation = new PolicyRepresentation();
|
||||
|
||||
representation.setId(policy.getPolicy().getId());
|
||||
representation.setName(policy.getPolicy().getName());
|
||||
representation.setType(policy.getPolicy().getType());
|
||||
representation.setDecisionStrategy(policy.getPolicy().getDecisionStrategy());
|
||||
|
||||
policyResultRep.setPolicy(representation);
|
||||
policyResultRep.setStatus(policy.getStatus());
|
||||
policyResultRep.setAssociatedPolicies(policy.getAssociatedPolicies().stream().map(result -> toRepresentation(result, authorization)).collect(Collectors.toList()));
|
||||
|
||||
|
|
|
@ -110,7 +110,7 @@ public class AuthorizationTokenService {
|
|||
authorization.evaluators().from(createPermissions(ticket, authorizationRequest, authorization), evaluationContext).evaluate(new DecisionResultCollector() {
|
||||
@Override
|
||||
public void onComplete(List<Result> results) {
|
||||
List<Permission> entitlements = Permissions.allPermits(results, authorization);
|
||||
List<Permission> entitlements = Permissions.permits(results, authorization, ticket.getResourceServerId());
|
||||
|
||||
if (entitlements.isEmpty()) {
|
||||
HashMap<Object, Object> error = new HashMap<>();
|
||||
|
@ -144,7 +144,7 @@ public class AuthorizationTokenService {
|
|||
Resource resource;
|
||||
|
||||
if (requestedResource.getId() != null) {
|
||||
resource = storeFactory.getResourceStore().findById(requestedResource.getId());
|
||||
resource = storeFactory.getResourceStore().findById(requestedResource.getId(), ticket.getResourceServerId());
|
||||
} else {
|
||||
resource = storeFactory.getResourceStore().findByName(requestedResource.getName(), ticket.getResourceServerId());
|
||||
}
|
||||
|
@ -171,7 +171,7 @@ public class AuthorizationTokenService {
|
|||
}
|
||||
|
||||
return scope.getId();
|
||||
}).filter(s -> s != null).collect(Collectors.toList()).toArray(new String[requestedScopes.size()])));
|
||||
}).filter(s -> s != null).collect(Collectors.toList()), ticket.getResourceServerId()));
|
||||
|
||||
for (Resource resource1 : resources) {
|
||||
permissionsToEvaluate.put(resource1.getId(), collect);
|
||||
|
@ -204,7 +204,7 @@ public class AuthorizationTokenService {
|
|||
|
||||
if (permissions != null) {
|
||||
permissions.forEach(permission -> {
|
||||
Resource resourcePermission = storeFactory.getResourceStore().findById(permission.getResourceSetId());
|
||||
Resource resourcePermission = storeFactory.getResourceStore().findById(permission.getResourceSetId(), ticket.getResourceServerId());
|
||||
|
||||
if (resourcePermission != null) {
|
||||
Set<String> scopes = permissionsToEvaluate.get(resourcePermission.getId());
|
||||
|
@ -240,7 +240,7 @@ public class AuthorizationTokenService {
|
|||
}).collect(Collectors.toList());
|
||||
return Arrays.asList(new ResourcePermission(null, scopes, resourceServer)).stream();
|
||||
} else {
|
||||
Resource entryResource = storeFactory.getResourceStore().findById(key);
|
||||
Resource entryResource = storeFactory.getResourceStore().findById(key, resourceServer.getId());
|
||||
return Permissions.createResourcePermissions(entryResource, entry.getValue(), authorization).stream();
|
||||
}
|
||||
}).collect(Collectors.toList());
|
||||
|
|
|
@ -46,6 +46,7 @@ import org.keycloak.protocol.oidc.TokenManager;
|
|||
import org.keycloak.representations.AccessToken;
|
||||
import org.keycloak.representations.idm.authorization.Permission;
|
||||
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
|
||||
import org.keycloak.services.ErrorResponse;
|
||||
import org.keycloak.services.ErrorResponseException;
|
||||
import org.keycloak.services.resources.Cors;
|
||||
|
||||
|
@ -125,7 +126,7 @@ public class EntitlementService {
|
|||
|
||||
@Override
|
||||
protected void onComplete(List<Result> results) {
|
||||
List<Permission> entitlements = Permissions.allPermits(results, authorization);
|
||||
List<Permission> entitlements = Permissions.allPermits(results, authorization, resourceServer);
|
||||
|
||||
if (entitlements.isEmpty()) {
|
||||
HashMap<Object, Object> error = new HashMap<>();
|
||||
|
@ -169,8 +170,8 @@ public class EntitlementService {
|
|||
StoreFactory storeFactory = authorization.getStoreFactory();
|
||||
ResourceServer resourceServer = storeFactory.getResourceServerStore().findByClient(client.getId());
|
||||
|
||||
try {
|
||||
authorization.evaluators().from(createPermissions(entitlementRequest, resourceServer, authorization), new KeycloakEvaluationContext(this.authorization.getKeycloakSession())).evaluate(new DecisionResultCollector() {
|
||||
|
||||
@Override
|
||||
public void onError(Throwable cause) {
|
||||
asyncResponse.resume(cause);
|
||||
|
@ -178,7 +179,7 @@ public class EntitlementService {
|
|||
|
||||
@Override
|
||||
protected void onComplete(List<Result> results) {
|
||||
List<Permission> entitlements = Permissions.allPermits(results, authorization);
|
||||
List<Permission> entitlements = Permissions.allPermits(results, authorization, resourceServer);
|
||||
|
||||
if (entitlements.isEmpty()) {
|
||||
HashMap<Object, Object> error = new HashMap<>();
|
||||
|
@ -194,6 +195,15 @@ public class EntitlementService {
|
|||
}
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
String message = e.getMessage();
|
||||
|
||||
if (message == null) {
|
||||
message = "Could not process authorization request";
|
||||
}
|
||||
|
||||
asyncResponse.resume(ErrorResponse.error(message, Status.BAD_REQUEST));
|
||||
}
|
||||
}
|
||||
|
||||
private String createRequestingPartyToken(List<Permission> permissions) {
|
||||
|
@ -215,7 +225,7 @@ public class EntitlementService {
|
|||
Resource resource;
|
||||
|
||||
if (requestedResource.getResourceSetId() != null) {
|
||||
resource = storeFactory.getResourceStore().findById(requestedResource.getResourceSetId());
|
||||
resource = storeFactory.getResourceStore().findById(requestedResource.getResourceSetId(), resourceServer.getId());
|
||||
} else {
|
||||
resource = storeFactory.getResourceStore().findByName(requestedResource.getResourceSetName(), resourceServer.getId());
|
||||
}
|
||||
|
@ -242,7 +252,7 @@ public class EntitlementService {
|
|||
}
|
||||
|
||||
return scope.getId();
|
||||
}).filter(s -> s != null).collect(Collectors.toList()).toArray(new String[requestedScopes.size()])));
|
||||
}).filter(s -> s != null).collect(Collectors.toList()), resourceServer.getId()));
|
||||
|
||||
for (Resource resource1 : resources) {
|
||||
permissionsToEvaluate.put(resource1.getId(), collect);
|
||||
|
@ -276,7 +286,7 @@ public class EntitlementService {
|
|||
|
||||
if (permissions != null) {
|
||||
permissions.forEach(permission -> {
|
||||
Resource resourcePermission = storeFactory.getResourceStore().findById(permission.getResourceSetId());
|
||||
Resource resourcePermission = storeFactory.getResourceStore().findById(permission.getResourceSetId(), resourceServer.getId());
|
||||
|
||||
if (resourcePermission != null) {
|
||||
Set<String> scopes = permissionsToEvaluate.get(resourcePermission.getId());
|
||||
|
@ -310,7 +320,7 @@ public class EntitlementService {
|
|||
}).collect(Collectors.toList());
|
||||
return Arrays.asList(new ResourcePermission(null, scopes, resourceServer)).stream();
|
||||
} else {
|
||||
Resource entryResource = storeFactory.getResourceStore().findById(key);
|
||||
Resource entryResource = storeFactory.getResourceStore().findById(key, resourceServer.getId());
|
||||
return Permissions.createResourcePermissions(entryResource, entry.getValue(), authorization).stream();
|
||||
}
|
||||
}).collect(Collectors.toList());
|
||||
|
|
|
@ -77,7 +77,7 @@ public class AbstractPermissionService {
|
|||
|
||||
if (!resourceNotProvider) {
|
||||
if (resourceSetId != null) {
|
||||
resource = storeFactory.getResourceStore().findById(resourceSetId);
|
||||
resource = storeFactory.getResourceStore().findById(resourceSetId, resourceServer.getId());
|
||||
} else {
|
||||
resource = storeFactory.getResourceStore().findByName(resourceSetName, this.resourceServer.getId());
|
||||
}
|
||||
|
@ -113,7 +113,7 @@ public class AbstractPermissionService {
|
|||
}
|
||||
}
|
||||
|
||||
for (Resource baseResource : authorization.getStoreFactory().getResourceStore().findByType(resource.getType())) {
|
||||
for (Resource baseResource : authorization.getStoreFactory().getResourceStore().findByType(resource.getType(), resourceServer.getId())) {
|
||||
if (baseResource.getOwner().equals(resource.getResourceServer().getClientId())) {
|
||||
for (Scope baseScope : baseResource.getScopes()) {
|
||||
if (baseScope.getName().equals(scopeName)) {
|
||||
|
|
|
@ -109,7 +109,7 @@ public class ResourceService {
|
|||
}
|
||||
|
||||
private Set<String> findAll() {
|
||||
Response response = this.resourceManager.find(null, null, null, null, null, -1, -1);
|
||||
Response response = this.resourceManager.find(null, null, null, null, null, null, true, -1, -1);
|
||||
List<ResourceRepresentation> resources = (List<ResourceRepresentation>) response.getEntity();
|
||||
return resources.stream().map(ResourceRepresentation::getId).collect(Collectors.toSet());
|
||||
}
|
||||
|
@ -150,7 +150,7 @@ public class ResourceService {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
resources = storeFactory.getResourceStore().findByOwner(identity.getId()).stream()
|
||||
resources = storeFactory.getResourceStore().findByOwner(identity.getId(), resourceServer.getId()).stream()
|
||||
.map(resource -> ModelToRepresentation.toRepresentation(resource, this.resourceServer, authorization))
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
|
|
@ -18,9 +18,18 @@
|
|||
|
||||
package org.keycloak.authorization.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.keycloak.authorization.AuthorizationProvider;
|
||||
import org.keycloak.authorization.Decision.Effect;
|
||||
import org.keycloak.authorization.identity.Identity;
|
||||
import org.keycloak.authorization.model.Policy;
|
||||
import org.keycloak.authorization.model.Resource;
|
||||
import org.keycloak.authorization.model.ResourceServer;
|
||||
import org.keycloak.authorization.model.Scope;
|
||||
|
@ -31,16 +40,6 @@ import org.keycloak.authorization.store.ScopeStore;
|
|||
import org.keycloak.authorization.store.StoreFactory;
|
||||
import org.keycloak.representations.idm.authorization.Permission;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
|
@ -62,8 +61,8 @@ public final class Permissions {
|
|||
StoreFactory storeFactory = authorization.getStoreFactory();
|
||||
ResourceStore resourceStore = storeFactory.getResourceStore();
|
||||
|
||||
resourceStore.findByOwner(resourceServer.getClientId()).stream().forEach(resource -> permissions.addAll(createResourcePermissions(resource, resource.getScopes().stream().map(Scope::getName).collect(Collectors.toSet()), authorization)));
|
||||
resourceStore.findByOwner(identity.getId()).stream().forEach(resource -> permissions.addAll(createResourcePermissions(resource, resource.getScopes().stream().map(Scope::getName).collect(Collectors.toSet()), authorization)));
|
||||
resourceStore.findByOwner(resourceServer.getClientId(), resourceServer.getId()).stream().forEach(resource -> permissions.addAll(createResourcePermissionsWithScopes(resource, resource.getScopes(), authorization)));
|
||||
resourceStore.findByOwner(identity.getId(), resourceServer.getId()).stream().forEach(resource -> permissions.addAll(createResourcePermissionsWithScopes(resource, resource.getScopes(), authorization)));
|
||||
|
||||
return permissions;
|
||||
}
|
||||
|
@ -81,7 +80,7 @@ public final class Permissions {
|
|||
if (type != null && !resource.getOwner().equals(resourceServer.getClientId())) {
|
||||
StoreFactory storeFactory = authorization.getStoreFactory();
|
||||
ResourceStore resourceStore = storeFactory.getResourceStore();
|
||||
resourceStore.findByType(type).forEach(resource1 -> {
|
||||
resourceStore.findByType(type, resourceServer.getId()).forEach(resource1 -> {
|
||||
if (resource1.getOwner().equals(resourceServer.getClientId())) {
|
||||
for (Scope typeScope : resource1.getScopes()) {
|
||||
if (!scopes.contains(typeScope)) {
|
||||
|
@ -95,34 +94,100 @@ public final class Permissions {
|
|||
ScopeStore scopeStore = authorization.getStoreFactory().getScopeStore();
|
||||
scopes = requestedScopes.stream().map(scopeName -> {
|
||||
Scope byName = scopeStore.findByName(scopeName, resource.getResourceServer().getId());
|
||||
|
||||
if (byName == null) {
|
||||
throw new RuntimeException("Invalid scope [" + scopeName + "].");
|
||||
}
|
||||
|
||||
return byName;
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
if (scopes.isEmpty()) {
|
||||
permissions.add(new ResourcePermission(resource, Collections.emptyList(), resource.getResourceServer()));
|
||||
} else {
|
||||
for (Scope scope : scopes) {
|
||||
permissions.add(new ResourcePermission(resource, Arrays.asList(scope), resource.getResourceServer()));
|
||||
}
|
||||
}
|
||||
permissions.add(new ResourcePermission(resource, scopes, resource.getResourceServer()));
|
||||
|
||||
return permissions;
|
||||
}
|
||||
|
||||
public static List<Permission> allPermits(List<Result> evaluation, AuthorizationProvider authorizationProvider) {
|
||||
public static List<ResourcePermission> createResourcePermissionsWithScopes(Resource resource, List<Scope> scopes, AuthorizationProvider authorization) {
|
||||
List<ResourcePermission> permissions = new ArrayList<>();
|
||||
String type = resource.getType();
|
||||
ResourceServer resourceServer = resource.getResourceServer();
|
||||
|
||||
// check if there is a typed resource whose scopes are inherited by the resource being requested. In this case, we assume that parent resource
|
||||
// is owned by the resource server itself
|
||||
if (type != null && !resource.getOwner().equals(resourceServer.getClientId())) {
|
||||
StoreFactory storeFactory = authorization.getStoreFactory();
|
||||
ResourceStore resourceStore = storeFactory.getResourceStore();
|
||||
resourceStore.findByType(type, resourceServer.getId()).forEach(resource1 -> {
|
||||
if (resource1.getOwner().equals(resourceServer.getClientId())) {
|
||||
for (Scope typeScope : resource1.getScopes()) {
|
||||
if (!scopes.contains(typeScope)) {
|
||||
scopes.add(typeScope);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
permissions.add(new ResourcePermission(resource, scopes, resource.getResourceServer()));
|
||||
|
||||
return permissions;
|
||||
}
|
||||
|
||||
public static List<Permission> allPermits(List<Result> evaluation, AuthorizationProvider authorizationProvider, ResourceServer resourceServer) {
|
||||
Map<String, Permission> permissions = new HashMap<>();
|
||||
|
||||
for (Result evaluationResult : evaluation) {
|
||||
ResourcePermission permission = evaluationResult.getPermission();
|
||||
|
||||
// if overall decision was a DENY, check for scope-based policies for a PERMIT
|
||||
if (evaluationResult.getEffect().equals(Effect.DENY)) {
|
||||
for (Result.PolicyResult result : evaluationResult.getResults()) {
|
||||
Policy policy = result.getPolicy();
|
||||
|
||||
if ("scope".equals(policy.getType())) {
|
||||
Set<Resource> resources = policy.getResources();
|
||||
|
||||
if (Effect.PERMIT.equals(result.getStatus())) {
|
||||
List<Scope> scopes = policy.getScopes().stream().collect(Collectors.toList());
|
||||
|
||||
if (!resources.isEmpty()) {
|
||||
resources.forEach(resource -> grantPermission(authorizationProvider, permissions, new ResourcePermission(resource, scopes, policy.getResourceServer()), resourceServer.getId()));
|
||||
} else {
|
||||
grantPermission(authorizationProvider, permissions, new ResourcePermission(permission.getResource(), scopes, policy.getResourceServer()), resourceServer.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
grantPermission(authorizationProvider, permissions, permission, resourceServer.getId());
|
||||
}
|
||||
|
||||
return permissions.values().stream().collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static List<Permission> permits(List<Result> evaluation, AuthorizationProvider authorizationProvider, String resourceServer) {
|
||||
Map<String, Permission> permissions = new HashMap<>();
|
||||
|
||||
for (Result evaluationResult : evaluation) {
|
||||
ResourcePermission permission = evaluationResult.getPermission();
|
||||
Set<String> scopes = permission.getScopes().stream().map(Scope::getName).collect(Collectors.toSet());
|
||||
|
||||
if (evaluationResult.getEffect().equals(Effect.DENY)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
grantPermission(authorizationProvider, permissions, permission, resourceServer);
|
||||
}
|
||||
|
||||
return permissions.values().stream().collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private static void grantPermission(AuthorizationProvider authorizationProvider, Map<String, Permission> permissions, ResourcePermission permission, String resourceServer) {
|
||||
List<Resource> resources = new ArrayList<>();
|
||||
Resource resource = permission.getResource();
|
||||
Set<String> scopes = permission.getScopes().stream().map(Scope::getName).collect(Collectors.toSet());
|
||||
|
||||
if (resource != null) {
|
||||
resources.add(resource);
|
||||
|
@ -131,7 +196,7 @@ public final class Permissions {
|
|||
|
||||
if (!permissionScopes.isEmpty()) {
|
||||
ResourceStore resourceStore = authorizationProvider.getStoreFactory().getResourceStore();
|
||||
resources.addAll(resourceStore.findByScope(permissionScopes.stream().map(Scope::getId).collect(Collectors.toList()).toArray(new String[permissionScopes.size()])));
|
||||
resources.addAll(resourceStore.findByScope(permissionScopes.stream().map(Scope::getId).collect(Collectors.toList()), resourceServer));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -166,7 +231,4 @@ public final class Permissions {
|
|||
permissions.put(scopePermission.toString(), scopePermission);
|
||||
}
|
||||
}
|
||||
|
||||
return permissions.values().stream().collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,18 +17,27 @@
|
|||
|
||||
package org.keycloak.exportimport.util;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonEncoding;
|
||||
import com.fasterxml.jackson.core.JsonFactory;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||
import static org.keycloak.models.utils.ModelToRepresentation.toRepresentation;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.keycloak.authorization.AuthorizationProvider;
|
||||
import org.keycloak.authorization.AuthorizationProviderFactory;
|
||||
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.PolicyStore;
|
||||
import org.keycloak.authorization.store.ResourceStore;
|
||||
import org.keycloak.authorization.store.ScopeStore;
|
||||
import org.keycloak.authorization.store.StoreFactory;
|
||||
import org.keycloak.common.Version;
|
||||
import org.keycloak.common.util.Base64;
|
||||
|
@ -63,20 +72,11 @@ import org.keycloak.representations.idm.authorization.ResourceRepresentation;
|
|||
import org.keycloak.representations.idm.authorization.ResourceServerRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.keycloak.models.utils.ModelToRepresentation.toRepresentation;
|
||||
import com.fasterxml.jackson.core.JsonEncoding;
|
||||
import com.fasterxml.jackson.core.JsonFactory;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
|
@ -295,7 +295,6 @@ public class ExportUtils {
|
|||
rep.getOwner().setId(null);
|
||||
}
|
||||
rep.setId(null);
|
||||
rep.setPolicies(null);
|
||||
rep.getScopes().forEach(scopeRepresentation -> {
|
||||
scopeRepresentation.setId(null);
|
||||
scopeRepresentation.setIconUri(null);
|
||||
|
@ -338,10 +337,9 @@ public class ExportUtils {
|
|||
RealmModel realm = authorizationProvider.getRealm();
|
||||
StoreFactory storeFactory = authorizationProvider.getStoreFactory();
|
||||
try {
|
||||
PolicyRepresentation rep = toRepresentation(policy, authorizationProvider);
|
||||
PolicyRepresentation rep = toRepresentation(policy);
|
||||
|
||||
rep.setId(null);
|
||||
rep.setDependentPolicies(null);
|
||||
|
||||
Map<String, String> config = rep.getConfig();
|
||||
|
||||
|
@ -363,20 +361,18 @@ public class ExportUtils {
|
|||
config.put("users", JsonSerialization.writeValueAsString(userIds.stream().map(userId -> userManager.getUserById(userId, realm).getUsername()).collect(Collectors.toList())));
|
||||
}
|
||||
|
||||
String scopes = config.get("scopes");
|
||||
Set<Scope> scopes = policy.getScopes();
|
||||
|
||||
if (scopes != null && !scopes.isEmpty()) {
|
||||
ScopeStore scopeStore = storeFactory.getScopeStore();
|
||||
List<String> scopeIds = JsonSerialization.readValue(scopes, List.class);
|
||||
config.put("scopes", JsonSerialization.writeValueAsString(scopeIds.stream().map(scopeId -> scopeStore.findById(scopeId).getName()).collect(Collectors.toList())));
|
||||
if (!scopes.isEmpty()) {
|
||||
List<String> scopeNames = scopes.stream().map(Scope::getName).collect(Collectors.toList());
|
||||
config.put("scopes", JsonSerialization.writeValueAsString(scopeNames));
|
||||
}
|
||||
|
||||
String policyResources = config.get("resources");
|
||||
Set<Resource> policyResources = policy.getResources();
|
||||
|
||||
if (policyResources != null && !policyResources.isEmpty()) {
|
||||
ResourceStore resourceStore = storeFactory.getResourceStore();
|
||||
List<String> resourceIds = JsonSerialization.readValue(policyResources, List.class);
|
||||
config.put("resources", JsonSerialization.writeValueAsString(resourceIds.stream().map(resourceId -> resourceStore.findById(resourceId).getName()).collect(Collectors.toList())));
|
||||
if (!policyResources.isEmpty()) {
|
||||
List<String> resourceNames = scopes.stream().map(Scope::getName).collect(Collectors.toList());
|
||||
config.put("resources", JsonSerialization.writeValueAsString(resourceNames));
|
||||
}
|
||||
|
||||
Set<Policy> associatedPolicies = policy.getAssociatedPolicies();
|
||||
|
@ -385,8 +381,6 @@ public class ExportUtils {
|
|||
config.put("applyPolicies", JsonSerialization.writeValueAsString(associatedPolicies.stream().map(associated -> associated.getName()).collect(Collectors.toList())));
|
||||
}
|
||||
|
||||
rep.setAssociatedPolicies(null);
|
||||
|
||||
return rep;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Error while exporting policy [" + policy.getName() + "].", e);
|
||||
|
|
|
@ -18,7 +18,6 @@ package org.keycloak.testsuite.authorization;
|
|||
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.authorization.AuthorizationProvider;
|
||||
import org.keycloak.authorization.model.Policy;
|
||||
import org.keycloak.authorization.model.ResourceServer;
|
||||
import org.keycloak.authorization.policy.evaluation.Evaluation;
|
||||
import org.keycloak.authorization.policy.provider.PolicyProvider;
|
||||
|
@ -43,8 +42,8 @@ public class TestPolicyProviderFactory implements PolicyProviderFactory {
|
|||
}
|
||||
|
||||
@Override
|
||||
public PolicyProvider create(Policy policy, AuthorizationProvider authorization) {
|
||||
return new TestPolicyProvider(policy, authorization);
|
||||
public PolicyProvider create(AuthorizationProvider authorization) {
|
||||
return new TestPolicyProvider(authorization);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -79,11 +78,9 @@ public class TestPolicyProviderFactory implements PolicyProviderFactory {
|
|||
|
||||
private class TestPolicyProvider implements PolicyProvider {
|
||||
|
||||
private final Policy policy;
|
||||
private final AuthorizationProvider authorization;
|
||||
|
||||
public TestPolicyProvider(Policy policy, AuthorizationProvider authorization) {
|
||||
this.policy = policy;
|
||||
public TestPolicyProvider(AuthorizationProvider authorization) {
|
||||
this.authorization = authorization;
|
||||
}
|
||||
|
||||
|
|
|
@ -38,6 +38,7 @@ import java.util.Arrays;
|
|||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
@ -71,7 +72,7 @@ public class GenericPolicyManagementTest extends AbstractAuthorizationTest {
|
|||
PolicyRepresentation newPolicy = createTestingPolicy().toRepresentation();
|
||||
|
||||
assertEquals("Test Generic Policy", newPolicy.getName());
|
||||
assertEquals("test", newPolicy.getType());
|
||||
assertEquals("scope", newPolicy.getType());
|
||||
assertEquals(Logic.POSITIVE, newPolicy.getLogic());
|
||||
assertEquals(DecisionStrategy.UNANIMOUS, newPolicy.getDecisionStrategy());
|
||||
assertEquals("configuration for A", newPolicy.getConfig().get("configA"));
|
||||
|
@ -98,28 +99,28 @@ public class GenericPolicyManagementTest extends AbstractAuthorizationTest {
|
|||
@Test
|
||||
public void testUpdate() {
|
||||
PolicyResource policyResource = createTestingPolicy();
|
||||
PolicyRepresentation resource = policyResource.toRepresentation();
|
||||
PolicyRepresentation policy = policyResource.toRepresentation();
|
||||
|
||||
resource.setName("changed");
|
||||
resource.setLogic(Logic.NEGATIVE);
|
||||
resource.setDecisionStrategy(DecisionStrategy.AFFIRMATIVE);
|
||||
resource.getConfig().put("configA", "changed configuration for A");
|
||||
resource.getConfig().remove("configB");
|
||||
resource.getConfig().put("configC", "changed configuration for C");
|
||||
policy.setName("changed");
|
||||
policy.setLogic(Logic.NEGATIVE);
|
||||
policy.setDecisionStrategy(DecisionStrategy.AFFIRMATIVE);
|
||||
policy.getConfig().put("configA", "changed configuration for A");
|
||||
policy.getConfig().remove("configB");
|
||||
policy.getConfig().put("configC", "changed configuration for C");
|
||||
|
||||
policyResource.update(resource);
|
||||
policyResource.update(policy);
|
||||
|
||||
resource = policyResource.toRepresentation();
|
||||
policy = policyResource.toRepresentation();
|
||||
|
||||
assertEquals("changed", resource.getName());
|
||||
assertEquals(Logic.NEGATIVE, resource.getLogic());
|
||||
assertEquals("changed", policy.getName());
|
||||
assertEquals(Logic.NEGATIVE, policy.getLogic());
|
||||
|
||||
assertEquals(DecisionStrategy.AFFIRMATIVE, resource.getDecisionStrategy());
|
||||
assertEquals("changed configuration for A", resource.getConfig().get("configA"));
|
||||
assertNull(resource.getConfig().get("configB"));
|
||||
assertEquals("changed configuration for C", resource.getConfig().get("configC"));
|
||||
assertEquals(DecisionStrategy.AFFIRMATIVE, policy.getDecisionStrategy());
|
||||
assertEquals("changed configuration for A", policy.getConfig().get("configA"));
|
||||
assertNull(policy.getConfig().get("configB"));
|
||||
assertEquals("changed configuration for C", policy.getConfig().get("configC"));
|
||||
|
||||
Map<String, String> config = resource.getConfig();
|
||||
Map<String, String> config = policy.getConfig();
|
||||
|
||||
config.put("applyPolicies", buildConfigOption(findPolicyByName("Test Associated C").getId()));
|
||||
|
||||
|
@ -127,22 +128,25 @@ public class GenericPolicyManagementTest extends AbstractAuthorizationTest {
|
|||
|
||||
config.put("scopes", buildConfigOption(findScopeByName("Test Scope A").getId()));
|
||||
|
||||
policyResource.update(resource);
|
||||
policyResource.update(policy);
|
||||
|
||||
resource = policyResource.toRepresentation();
|
||||
config = resource.getConfig();
|
||||
policy = policyResource.toRepresentation();
|
||||
config = policy.getConfig();
|
||||
|
||||
assertAssociatedPolicy("Test Associated C", resource);
|
||||
assertFalse(config.get("applyPolicies").contains(findPolicyByName("Test Associated A").getId()));
|
||||
assertFalse(config.get("applyPolicies").contains(findPolicyByName("Test Associated B").getId()));
|
||||
assertAssociatedPolicy("Test Associated C", policy);
|
||||
List<PolicyRepresentation> associatedPolicies = getClientResource().authorization().policies().policy(policy.getId()).associatedPolicies();
|
||||
assertFalse(associatedPolicies.stream().filter(associated -> associated.getId().equals(findPolicyByName("Test Associated A").getId())).findFirst().isPresent());
|
||||
assertFalse(associatedPolicies.stream().filter(associated -> associated.getId().equals(findPolicyByName("Test Associated B").getId())).findFirst().isPresent());
|
||||
|
||||
assertAssociatedResource("Test Resource B", resource);
|
||||
assertFalse(config.get("resources").contains(findResourceByName("Test Resource A").getId()));
|
||||
assertFalse(config.get("resources").contains(findResourceByName("Test Resource C").getId()));
|
||||
assertAssociatedResource("Test Resource B", policy);
|
||||
List<ResourceRepresentation> resources = policyResource.resources();
|
||||
assertFalse(resources.contains(findResourceByName("Test Resource A")));
|
||||
assertFalse(resources.contains(findResourceByName("Test Resource C")));
|
||||
|
||||
assertAssociatedScope("Test Scope A", resource);
|
||||
assertFalse(config.get("scopes").contains(findScopeByName("Test Scope B").getId()));
|
||||
assertFalse(config.get("scopes").contains(findScopeByName("Test Scope C").getId()));
|
||||
assertAssociatedScope("Test Scope A", policy);
|
||||
List<ScopeRepresentation> scopes = getClientResource().authorization().policies().policy(policy.getId()).scopes();
|
||||
assertFalse(scopes.contains(findScopeByName("Test Scope B").getId()));
|
||||
assertFalse(scopes.contains(findScopeByName("Test Scope C").getId()));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -186,7 +190,7 @@ public class GenericPolicyManagementTest extends AbstractAuthorizationTest {
|
|||
PolicyRepresentation newPolicy = new PolicyRepresentation();
|
||||
|
||||
newPolicy.setName(name);
|
||||
newPolicy.setType("test");
|
||||
newPolicy.setType("scope");
|
||||
newPolicy.setConfig(config);
|
||||
|
||||
PoliciesResource policies = getClientResource().authorization().policies();
|
||||
|
@ -264,27 +268,38 @@ public class GenericPolicyManagementTest extends AbstractAuthorizationTest {
|
|||
|
||||
private void assertAssociatedPolicy(String associatedPolicyName, PolicyRepresentation dependentPolicy) {
|
||||
PolicyRepresentation associatedPolicy = findPolicyByName(associatedPolicyName);
|
||||
PoliciesResource policies = getClientResource().authorization().policies();
|
||||
associatedPolicy = policies.policy(associatedPolicy.getId()).toRepresentation();
|
||||
assertNotNull(associatedPolicy);
|
||||
assertTrue(dependentPolicy.getConfig().get("applyPolicies").contains(associatedPolicy.getId()));
|
||||
assertEquals(1, associatedPolicy.getDependentPolicies().size());
|
||||
assertEquals(dependentPolicy.getId(), associatedPolicy.getDependentPolicies().get(0).getId());
|
||||
PolicyRepresentation finalAssociatedPolicy = associatedPolicy;
|
||||
PolicyResource policyResource = policies.policy(dependentPolicy.getId());
|
||||
List<PolicyRepresentation> associatedPolicies = policyResource.associatedPolicies();
|
||||
assertTrue(associatedPolicies.stream().filter(associated -> associated.getId().equals(finalAssociatedPolicy.getId())).findFirst().isPresent());
|
||||
List<PolicyRepresentation> dependentPolicies = policies.policy(associatedPolicy.getId()).dependentPolicies();
|
||||
assertEquals(1, dependentPolicies.size());
|
||||
assertEquals(dependentPolicy.getId(), dependentPolicies.get(0).getId());
|
||||
}
|
||||
|
||||
private void assertAssociatedResource(String resourceName, PolicyRepresentation policy) {
|
||||
ResourceRepresentation resource = findResourceByName(resourceName);
|
||||
assertNotNull(resource);
|
||||
assertTrue(policy.getConfig().get("resources").contains(resource.getId()));
|
||||
assertEquals(1, resource.getPolicies().size());
|
||||
assertTrue(resource.getPolicies().stream().map(PolicyRepresentation::getId).collect(Collectors.toList())
|
||||
List<ResourceRepresentation> resources = getClientResource().authorization().policies().policy(policy.getId()).resources();
|
||||
assertTrue(resources.contains(resource));
|
||||
List<PolicyRepresentation> policies = getClientResource().authorization().resources().resource(resource.getId()).permissions();
|
||||
assertEquals(1, policies.size());
|
||||
assertTrue(policies.stream().map(PolicyRepresentation::getId).collect(Collectors.toList())
|
||||
.contains(policy.getId()));
|
||||
}
|
||||
|
||||
private void assertAssociatedScope(String scopeName, PolicyRepresentation policy) {
|
||||
ScopeRepresentation scope = findScopeByName(scopeName);
|
||||
scope = getClientResource().authorization().scopes().scope(scope.getId()).toRepresentation();
|
||||
assertNotNull(scope);
|
||||
assertTrue(policy.getConfig().get("scopes").contains(scope.getId()));
|
||||
assertEquals(1, scope.getPolicies().size());
|
||||
assertTrue(scope.getPolicies().stream().map(PolicyRepresentation::getId).collect(Collectors.toList())
|
||||
List<ScopeRepresentation> scopes = getClientResource().authorization().policies().policy(policy.getId()).scopes();
|
||||
assertTrue(scopes.stream().map((Function<ScopeRepresentation, String>) rep -> rep.getId()).collect(Collectors.toList()).contains(scope.getId()));
|
||||
List<PolicyRepresentation> permissions = getClientResource().authorization().scopes().scope(scope.getId()).permissions();
|
||||
assertEquals(1, permissions.size());
|
||||
assertTrue(permissions.stream().map(PolicyRepresentation::getId).collect(Collectors.toList())
|
||||
.contains(policy.getId()));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -101,7 +101,7 @@ public abstract class AbstractPhotozAdminTest extends AbstractAuthorizationTest
|
|||
|
||||
// during tests we create resource instances, but we need to reload them to get their collections updated
|
||||
List<ResourcePermission> updatedPermissions = permissions.stream().map(permission -> {
|
||||
Resource resource = storeFactory.getResourceStore().findById(permission.getResource().getId());
|
||||
Resource resource = storeFactory.getResourceStore().findById(permission.getResource().getId(), resourceServer.getId());
|
||||
return new ResourcePermission(resource, permission.getScopes(), permission.getResourceServer());
|
||||
}).collect(Collectors.toList());
|
||||
|
||||
|
|
|
@ -53,7 +53,7 @@ public class ResourceManagementTest extends AbstractPhotozAdminTest {
|
|||
ResourceRepresentation resource = response.readEntity(ResourceRepresentation.class);
|
||||
|
||||
onAuthorizationSession(authorizationProvider -> {
|
||||
Resource resourceModel = authorizationProvider.getStoreFactory().getResourceStore().findById(resource.getId());
|
||||
Resource resourceModel = authorizationProvider.getStoreFactory().getResourceStore().findById(resource.getId(), resourceServer.getId());
|
||||
|
||||
assertNotNull(resourceModel);
|
||||
assertEquals(resource.getId(), resourceModel.getId());
|
||||
|
@ -89,7 +89,7 @@ public class ResourceManagementTest extends AbstractPhotozAdminTest {
|
|||
ResourceRepresentation resource = response.readEntity(ResourceRepresentation.class);
|
||||
|
||||
onAuthorizationSession(authorizationProvider -> {
|
||||
Resource resourceModel = authorizationProvider.getStoreFactory().getResourceStore().findById(resource.getId());
|
||||
Resource resourceModel = authorizationProvider.getStoreFactory().getResourceStore().findById(resource.getId(), resourceServer.getId());
|
||||
|
||||
assertNotNull(resourceModel);
|
||||
assertEquals(resource.getId(), resourceModel.getId());
|
||||
|
@ -147,7 +147,7 @@ public class ResourceManagementTest extends AbstractPhotozAdminTest {
|
|||
assertEquals(Status.NO_CONTENT.getStatusCode(), response.getStatus());
|
||||
|
||||
onAuthorizationSession(authorizationProvider -> {
|
||||
Resource resourceModel = authorizationProvider.getStoreFactory().getResourceStore().findById(resource.getId());
|
||||
Resource resourceModel = authorizationProvider.getStoreFactory().getResourceStore().findById(resource.getId(), resourceServer.getId());
|
||||
|
||||
assertNull(resourceModel);
|
||||
});
|
||||
|
|
|
@ -46,8 +46,12 @@ import java.util.HashSet;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
|
@ -77,7 +81,7 @@ public class ResourcePermissionManagementTest extends AbstractPhotozAdminTest {
|
|||
PolicyRepresentation permission = response.readEntity(PolicyRepresentation.class);
|
||||
|
||||
onAuthorizationSession(authorizationProvider -> {
|
||||
Policy policyModel = authorizationProvider.getStoreFactory().getPolicyStore().findById(permission.getId());
|
||||
Policy policyModel = authorizationProvider.getStoreFactory().getPolicyStore().findById(permission.getId(), resourceServer.getId());
|
||||
|
||||
assertNotNull(policyModel);
|
||||
assertEquals(permission.getId(), policyModel.getId());
|
||||
|
@ -357,7 +361,7 @@ public class ResourcePermissionManagementTest extends AbstractPhotozAdminTest {
|
|||
PolicyRepresentation permission = response.readEntity(PolicyRepresentation.class);
|
||||
|
||||
onAuthorizationSession(authorizationProvider -> {
|
||||
Policy policyModel = authorizationProvider.getStoreFactory().getPolicyStore().findById(permission.getId());
|
||||
Policy policyModel = authorizationProvider.getStoreFactory().getPolicyStore().findById(permission.getId(), resourceServer.getId());
|
||||
|
||||
assertNotNull(policyModel);
|
||||
assertEquals(permission.getId(), policyModel.getId());
|
||||
|
@ -430,7 +434,8 @@ public class ResourcePermissionManagementTest extends AbstractPhotozAdminTest {
|
|||
|
||||
config.put("defaultResourceType", albumResource.getType());
|
||||
|
||||
String applyPolicies = JsonSerialization.writeValueAsString(new String[]{this.anyUserPolicy.getId(), this.administrationPolicy.getId()});
|
||||
String[] associatedPolicies = {this.anyUserPolicy.getId(), this.administrationPolicy.getId()};
|
||||
String applyPolicies = JsonSerialization.writeValueAsString(associatedPolicies);
|
||||
|
||||
config.put("applyPolicies", applyPolicies);
|
||||
|
||||
|
@ -443,14 +448,15 @@ public class ResourcePermissionManagementTest extends AbstractPhotozAdminTest {
|
|||
PolicyRepresentation permission = response.readEntity(PolicyRepresentation.class);
|
||||
|
||||
onAuthorizationSession(authorizationProvider -> {
|
||||
Policy policyModel = authorizationProvider.getStoreFactory().getPolicyStore().findById(permission.getId());
|
||||
Policy policyModel = authorizationProvider.getStoreFactory().getPolicyStore().findById(permission.getId(), resourceServer.getId());
|
||||
|
||||
assertNotNull(policyModel);
|
||||
assertEquals(permission.getId(), policyModel.getId());
|
||||
assertEquals(permission.getName(), policyModel.getName());
|
||||
assertEquals(permission.getType(), policyModel.getType());
|
||||
assertTrue(permission.getConfig().containsValue(albumResource.getType()));
|
||||
assertTrue(permission.getConfig().containsValue(applyPolicies));
|
||||
assertTrue(policyModel.getAssociatedPolicies().stream().map(Policy::getId).collect(Collectors.toList()).containsAll(Arrays.asList(associatedPolicies)));
|
||||
|
||||
assertEquals(resourceServer.getId(), policyModel.getResourceServer().getId());
|
||||
});
|
||||
|
||||
|
|
|
@ -57,7 +57,7 @@ public class ScopeManagementTest extends AbstractPhotozAdminTest {
|
|||
ScopeRepresentation scope = response.readEntity(ScopeRepresentation.class);
|
||||
|
||||
onAuthorizationSession(authorizationProvider -> {
|
||||
Scope scopeModel = authorizationProvider.getStoreFactory().getScopeStore().findById(scope.getId());
|
||||
Scope scopeModel = authorizationProvider.getStoreFactory().getScopeStore().findById(scope.getId(), resourceServer.getId());
|
||||
|
||||
assertNotNull(scopeModel);
|
||||
assertEquals(scope.getId(), scopeModel.getId());
|
||||
|
@ -86,7 +86,7 @@ public class ScopeManagementTest extends AbstractPhotozAdminTest {
|
|||
ScopeRepresentation scope = response.readEntity(ScopeRepresentation.class);
|
||||
|
||||
onAuthorizationSession(authorizationProvider -> {
|
||||
Scope scopeModel = authorizationProvider.getStoreFactory().getScopeStore().findById(scope.getId());
|
||||
Scope scopeModel = authorizationProvider.getStoreFactory().getScopeStore().findById(scope.getId(), resourceServer.getId());
|
||||
|
||||
assertNotNull(scopeModel);
|
||||
assertEquals(scope.getId(), scopeModel.getId());
|
||||
|
@ -138,7 +138,7 @@ public class ScopeManagementTest extends AbstractPhotozAdminTest {
|
|||
assertEquals(Status.NO_CONTENT.getStatusCode(), response.getStatus());
|
||||
|
||||
onAuthorizationSession(authorizationProvider -> {
|
||||
Scope scopeModel = authorizationProvider.getStoreFactory().getScopeStore().findById(scope.getId());
|
||||
Scope scopeModel = authorizationProvider.getStoreFactory().getScopeStore().findById(scope.getId(), resourceServer.getId());
|
||||
|
||||
assertNull(scopeModel);
|
||||
});
|
||||
|
|
|
@ -64,7 +64,7 @@
|
|||
|
||||
"connectionsJpa": {
|
||||
"default": {
|
||||
"url": "${keycloak.connectionsJpa.url:jdbc:h2:mem:test}",
|
||||
"url": "${keycloak.connectionsJpa.url:jdbc:h2:mem:test;DB_CLOSE_DELAY=-1}",
|
||||
"driver": "${keycloak.connectionsJpa.driver:org.h2.Driver}",
|
||||
"driverDialect": "${keycloak.connectionsJpa.driverDialect:}",
|
||||
"user": "${keycloak.connectionsJpa.user:sa}",
|
||||
|
|
|
@ -1036,6 +1036,10 @@ authz-result=Result
|
|||
authz-authorization-services-enabled=Authorization Enabled
|
||||
authz-authorization-services-enabled.tooltip=Enable/Disable fine-grained authorization support for a client
|
||||
authz-required=Required
|
||||
authz-show-details=Show Details
|
||||
authz-hide-details=Hide Details
|
||||
authz-associated-permissions=Associated Permissions
|
||||
authz-no-permission-associated=No permissions associated
|
||||
|
||||
# Authz Settings
|
||||
authz-import-config.tooltip=Import a JSON file containing authorization settings for this resource server.
|
||||
|
@ -1056,6 +1060,7 @@ authz-export-settings.tooltip=Export and download all authorization settings for
|
|||
authz-no-resources-available=No resources available.
|
||||
authz-no-scopes-assigned=No scopes assigned.
|
||||
authz-no-type-defined=No type defined.
|
||||
authz-no-uri-defined=No URI defined.
|
||||
authz-no-permission-assigned=No permission assigned.
|
||||
authz-no-policy-assigned=No policy assigned.
|
||||
authz-create-permission=Create permission
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -16,7 +16,9 @@ module.factory('ResourceServerResource', function($resource) {
|
|||
rsrid : '@rsrid'
|
||||
}, {
|
||||
'update' : {method : 'PUT'},
|
||||
'search' : {url: authUrl + '/admin/realms/:realm/clients/:client/authz/resource-server/resource/search', method : 'GET'}
|
||||
'search' : {url: authUrl + '/admin/realms/:realm/clients/:client/authz/resource-server/resource/search', method : 'GET'},
|
||||
'scopes' : {url: authUrl + '/admin/realms/:realm/clients/:client/authz/resource-server/resource/:rsrid/scopes', method : 'GET', isArray: true},
|
||||
'permissions' : {url: authUrl + '/admin/realms/:realm/clients/:client/authz/resource-server/resource/:rsrid/permissions', method : 'GET', isArray: true}
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -27,7 +29,9 @@ module.factory('ResourceServerScope', function($resource) {
|
|||
id : '@id'
|
||||
}, {
|
||||
'update' : {method : 'PUT'},
|
||||
'search' : {url: authUrl + '/admin/realms/:realm/clients/:client/authz/resource-server/scope/search', method : 'GET'}
|
||||
'search' : {url: authUrl + '/admin/realms/:realm/clients/:client/authz/resource-server/scope/search', method : 'GET'},
|
||||
'resources' : {url: authUrl + '/admin/realms/:realm/clients/:client/authz/resource-server/scope/:id/resources', method : 'GET', isArray: true},
|
||||
'permissions' : {url: authUrl + '/admin/realms/:realm/clients/:client/authz/resource-server/scope/:id/permissions', method : 'GET', isArray: true},
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -38,7 +42,11 @@ module.factory('ResourceServerPolicy', function($resource) {
|
|||
id : '@id'
|
||||
}, {
|
||||
'update' : {method : 'PUT'},
|
||||
'search' : {url: authUrl + '/admin/realms/:realm/clients/:client/authz/resource-server/policy/search', method : 'GET'}
|
||||
'search' : {url: authUrl + '/admin/realms/:realm/clients/:client/authz/resource-server/policy/search', method : 'GET'},
|
||||
'associatedPolicies' : {url: authUrl + '/admin/realms/:realm/clients/:client/authz/resource-server/policy/:id/associatedPolicies', method : 'GET', isArray: true},
|
||||
'dependentPolicies' : {url: authUrl + '/admin/realms/:realm/clients/:client/authz/resource-server/policy/:id/dependentPolicies', method : 'GET', isArray: true},
|
||||
'scopes' : {url: authUrl + '/admin/realms/:realm/clients/:client/authz/resource-server/policy/:id/scopes', method : 'GET', isArray: true},
|
||||
'resources' : {url: authUrl + '/admin/realms/:realm/clients/:client/authz/resource-server/policy/:id/resources', method : 'GET', isArray: true}
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -39,9 +39,7 @@
|
|||
<label class="col-md-2 control-label" for="reqActions">{{:: 'authz-resources' | translate}} <span class="required">*</span></label>
|
||||
|
||||
<div class="col-md-6">
|
||||
<select ui-select2="{ minimumInputLength: 1}" id="reqActions" data-ng-model="policy.config.resources" data-placeholder="{{:: 'authz-select-resource' | translate}}..." multiple data-ng-required="!policy.config.default">
|
||||
<option ng-repeat="resource in resources" value="{{resource._id}}" ng-selected="true">{{resource.name}}</option>
|
||||
</select>
|
||||
<input type="hidden" ui-select2="resourcesUiSelect" id="reqActions" data-ng-model="policy.config.resources" data-placeholder="{{:: 'authz-select-resource' | translate}}..." data-ng-required="!policy.config.default"/>
|
||||
</div>
|
||||
<kc-tooltip>{{:: 'authz-permission-resource-resource.tooltip' | translate}}</kc-tooltip>
|
||||
</div>
|
||||
|
@ -58,9 +56,7 @@
|
|||
<label class="col-md-2 control-label" for="reqActions">{{:: 'authz-policy-apply-policy' | translate}} <span class="required">*</span></label>
|
||||
|
||||
<div class="col-md-6">
|
||||
<select ui-select2 id="reqActions" data-ng-model="policy.config.applyPolicies" data-placeholder="{{:: 'authz-select-a-policy' | translate}}..." multiple required>
|
||||
<option ng-repeat="policy in policies" value="{{policy.id}}" ng-selected="true">{{policy.name}}</option>
|
||||
</select>
|
||||
<input type="hidden" ui-select2="policiesUiSelect" id="reqActions" data-ng-model="policy.config.applyPolicies" data-placeholder="{{:: 'authz-select-a-policy' | translate}}..." multiple required />
|
||||
</div>
|
||||
|
||||
<kc-tooltip>{{:: 'authz-policy-apply-policy.tooltip' | translate}}</kc-tooltip>
|
||||
|
|
|
@ -32,12 +32,7 @@
|
|||
<label class="col-md-2 control-label" for="reqActions">{{:: 'authz-resource' | translate}}</label>
|
||||
|
||||
<div class="col-md-6">
|
||||
<select class="form-control" id="reqActions"
|
||||
ng-model="policy.config.resources"
|
||||
ng-change="resolveScopes(policy)"
|
||||
data-ng-options="resource._id as resource.name for resource in resources">
|
||||
<option value="">{{:: 'authz-any-resource' | translate}}...</option>
|
||||
</select>
|
||||
<input type="hidden" ui-select2="resourcesUiSelect" data-ng-change="selectResource()" id="reqActions" data-ng-model="policy.config.resources" data-placeholder="{{:: 'authz-any-resource' | translate}}..." />
|
||||
</div>
|
||||
<kc-tooltip>{{:: 'authz-permission-scope-resource.tooltip' | translate}}</kc-tooltip>
|
||||
</div>
|
||||
|
@ -45,25 +40,19 @@
|
|||
<label class="col-md-2 control-label" for="reqActions">{{:: 'authz-scopes' | translate}} <span class="required">*</span></label>
|
||||
|
||||
<div class="col-md-6">
|
||||
<select ui-select2 id="reqActions"
|
||||
<select ui-select2 id="reqActions2"
|
||||
data-ng-model="policy.config.scopes"
|
||||
data-placeholder="{{:: 'authz-any-scope' | translate}}..." multiple
|
||||
data-ng-required="policy.config.resources != ''"
|
||||
data-ng-options="scope.id as scope.name for scope in scopes track by scope.id"/>
|
||||
data-ng-required="policy.config.resources != null"
|
||||
data-ng-options="scope.id as scope.name for scope in policy.config.resources.scopes track by scope.id"/>
|
||||
</div>
|
||||
|
||||
<kc-tooltip>{{:: 'authz-permission-scope-scope.tooltip' | translate}}</kc-tooltip>
|
||||
</div>
|
||||
<div class="form-group clearfix" data-ng-show="!policy.config.resources">
|
||||
<label class="col-md-2 control-label" for="reqActions">{{:: 'authz-scopes' | translate}} <span class="required">*</span></label>
|
||||
|
||||
<div class="col-md-6">
|
||||
<select ui-select2="{ minimumInputLength: 1}" id="reqActions"
|
||||
data-ng-model="policy.config.scopes"
|
||||
data-placeholder="{{:: 'authz-any-scope' | translate}}..." multiple
|
||||
data-ng-required="policy.config.resources == ''"
|
||||
data-ng-options="scope.id as scope.name for scope in scopes track by scope.id"/>
|
||||
</select>
|
||||
<input type="hidden" ui-select2="scopesUiSelect" id="reqActions" data-ng-model="policy.config.scopes" data-placeholder="{{:: 'authz-any-scope' | translate}}..." multiple data-ng-required="policy.config.resources == null" />
|
||||
</div>
|
||||
<kc-tooltip>{{:: 'authz-permission-scope-scope.tooltip' | translate}}</kc-tooltip>
|
||||
</div>
|
||||
|
@ -71,9 +60,7 @@
|
|||
<label class="col-md-2 control-label" for="reqActions">{{:: 'authz-policy-apply-policy' | translate}} <span class="required">*</span></label>
|
||||
|
||||
<div class="col-md-6">
|
||||
<select ui-select2 id="reqActions" data-ng-model="policy.config.applyPolicies" data-placeholder="{{:: 'authz-select-a-policy' | translate}}..." multiple required>
|
||||
<option ng-repeat="policy in policies" value="{{policy.id}}" ng-selected="true">{{policy.name}}</option>
|
||||
</select>
|
||||
<input type="hidden" ui-select2="policiesUiSelect" id="reqActions" data-ng-model="policy.config.applyPolicies" data-placeholder="{{:: 'authz-select-a-policy' | translate}}..." multiple required />
|
||||
</div>
|
||||
|
||||
<kc-tooltip>{{:: 'authz-policy-apply-policy.tooltip' | translate}}</kc-tooltip>
|
||||
|
@ -95,7 +82,6 @@
|
|||
</div>
|
||||
<input type="hidden" data-ng-model="policy.type"/>
|
||||
</fieldset>
|
||||
|
||||
<div class="form-group" data-ng-show="access.manageAuthorization">
|
||||
<div class="col-md-10 col-md-offset-2">
|
||||
<button kc-save data-ng-disabled="!changed">{{:: 'save' | translate}}</button>
|
||||
|
|
|
@ -28,6 +28,12 @@
|
|||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<select class="form-control search" data-ng-model="detailsFilter" data-ng-change="searchQuery();">
|
||||
<option value="" selected>Hide Details</option>
|
||||
<option value="true">Show Details</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="pull-right">
|
||||
<select class="form-control" ng-model="policyType"
|
||||
ng-options="p.name for p in policyProviders track by p.type"
|
||||
|
@ -42,7 +48,7 @@
|
|||
<th>{{:: 'name' | translate}}</th>
|
||||
<th>{{:: 'description' | translate}}</th>
|
||||
<th>{{:: 'type' | translate}}</th>
|
||||
<th>{{:: 'authz-associated-policies' | translate}}</th>
|
||||
<th>{{:: 'actions' | translate}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tfoot data-ng-show="policies && (policies.length >= query.max || query.first > 0)">
|
||||
|
@ -52,22 +58,48 @@
|
|||
<button data-ng-click="firstPage()" class="first" ng-disabled="query.first == 0">{{:: 'first-page' | translate}}</button>
|
||||
<button data-ng-click="previousPage()" class="prev" ng-disabled="query.first == 0">{{:: 'previous-page' | translate}}</button>
|
||||
<button data-ng-click="nextPage()" class="next" ng-disabled="policies.length < query.max">{{:: 'next-page' | translate}}</button>
|
||||
<select class="first" data-ng-model="query.max"
|
||||
ng-options="size for size in listSizes" data-ng-change="firstPage()">
|
||||
</select>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
<tbody>
|
||||
<tr ng-repeat="policy in policies | filter: {name: search.name, type: search.type} | orderBy:'name'">
|
||||
<tr ng-repeat-start="policy in policies | filter: {name: search.name, type: search.type} | orderBy:'name'">
|
||||
<td><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/permission/{{policy.type}}/{{policy.id}}">{{policy.name}}</a></td>
|
||||
<td>{{policy.description}}</td>
|
||||
<td>{{policy.type}}</td>
|
||||
<td ng-if="!policy.details.loaded" class="kc-action-cell" data-ng-click="showDetails(policy);">
|
||||
{{:: 'authz-show-details' | translate}}
|
||||
</td>
|
||||
<td ng-if="policy.details.loaded" class="kc-action-cell" data-ng-click="showDetails(policy);">
|
||||
{{:: 'authz-hide-details' | translate}}
|
||||
</td>
|
||||
</tr>
|
||||
<tr ng-if="policy.details && policy.details.loaded" ng-repeat-end="">
|
||||
<td colspan="4">
|
||||
<div id="details">
|
||||
<table class="table kc-authz-table-expanded table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Associated Permissions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<span data-ng-show="!policy.associatedPolicies.length">{{:: 'authz-no-policy-assigned' | translate}}</span>
|
||||
<span data-ng-show="policy.associatedPolicies.length > 0">
|
||||
<span ng-repeat="policy in policy.associatedPolicies">
|
||||
<a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/policy/{{policy.type}}/{{policy.id}}">{{policy.name}}</a>{{$last ? '' : ', '}}
|
||||
</span>
|
||||
</span>
|
||||
<span data-ng-show="policy.associatedPolicies && !policy.associatedPolicies.length">{{:: 'authz-no-permission-assigned' | translate}}</span>
|
||||
<ul ng-repeat="dep in policy.associatedPolicies" data-ng-show="policy.associatedPolicies.length > 0">
|
||||
<li>
|
||||
<a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/policy/{{dep.type}}/{{dep.id}}">{{dep.name}}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr data-ng-show="(policies | filter:search).length == 0">
|
||||
|
|
|
@ -34,11 +34,8 @@
|
|||
<label class="col-md-2 control-label" for="reqActions">{{:: 'authz-policy-apply-policy' | translate}} <span class="required">*</span></label>
|
||||
|
||||
<div class="col-md-6">
|
||||
<select ui-select2 id="reqActions" data-ng-model="policy.config.applyPolicies" data-placeholder="{{:: 'authz-select-a-policy' | translate}}..." multiple required>
|
||||
<option ng-repeat="policy in policies" value="{{policy.id}}" ng-selected="true">{{policy.name}}</option>
|
||||
</select>
|
||||
<input type="hidden" ui-select2="policiesUiSelect" id="reqActions" data-ng-model="policy.config.applyPolicies" data-placeholder="{{:: 'authz-select-a-policy' | translate}}..." multiple required />
|
||||
</div>
|
||||
|
||||
<kc-tooltip>{{:: 'authz-policy-apply-policy.tooltip' | translate}}</kc-tooltip>
|
||||
</div>
|
||||
<div class="form-group clearfix">
|
||||
|
|
|
@ -172,14 +172,7 @@
|
|||
<label class="col-md-2 control-label" for="reqActions">{{:: 'authz-resources' | translate}} <span class="required">*</span></label>
|
||||
|
||||
<div class="col-md-6">
|
||||
<select ui-select2="{ minimumInputLength: 1, allowClear:true }"
|
||||
ng-model="newResource._id"
|
||||
data-placeholder="Select a resource..."
|
||||
data-ng-required="!applyResourceType && authzRequest.resources.length == 0 && !authzRequest.entitlements"
|
||||
data-ng-click="resolveScopes()"
|
||||
ng-options="resource._id as resource.name for resource in resources track by resource._id">
|
||||
<option value=""></option>
|
||||
</select>
|
||||
<input type="hidden" ui-select2="resourcesUiSelect" id="reqActions3" data-ng-change="resolveScopes()" data-ng-model="newResource" data-placeholder="{{:: 'authz-select-resource' | translate}}..." data-ng-required="!applyResourceType && authzRequest.resources.length == 0 && !authzRequest.entitlements" />
|
||||
</div>
|
||||
<kc-tooltip>{{:: 'authz-permission-resource-resource.tooltip' | translate}}</kc-tooltip>
|
||||
</div>
|
||||
|
@ -199,12 +192,7 @@
|
|||
<label class="col-md-2 control-label" for="newResource.scopes">{{:: 'authz-scopes' | translate}}</label>
|
||||
|
||||
<div class="col-md-6">
|
||||
<select ui-select2="{ minimumInputLength: 1}"
|
||||
id="newResource.scopes"
|
||||
multiple
|
||||
data-ng-model="newResource.scopes"
|
||||
data-placeholder="{{:: 'authz-select-scope' | translate}}..."
|
||||
data-ng-options="scope.name as scope.name for scope in scopes track by scope.name"/>
|
||||
<input type="hidden" ui-select2="scopesUiSelect" id="reqActions" data-ng-model="newScopes" data-placeholder="{{:: 'authz-any-scope' | translate}}..." multiple />
|
||||
</div>
|
||||
|
||||
<kc-tooltip>{{:: 'authz-permission-scope-scope.tooltip' | translate}}</kc-tooltip>
|
||||
|
@ -215,7 +203,7 @@
|
|||
<div class="col-md-6">
|
||||
<select ui-select2
|
||||
id="newResource.scopes"
|
||||
data-ng-model="newResource.scopes"
|
||||
data-ng-model="newScopes"
|
||||
data-placeholder="{{:: 'authz-any-scope' | translate}}..." multiple>
|
||||
<option ng-repeat="scope in scopes" value="{{scope.name}}">{{scope.name}}</option>
|
||||
</select>
|
||||
|
@ -248,7 +236,7 @@
|
|||
<span data-ng-show="!resource.scopes.length">{{:: 'authz-any-scope' | translate}}.</span>
|
||||
<span data-ng-show="resource.scopes.length > 0">
|
||||
<span ng-repeat="scope in resource.scopes">
|
||||
{{scope}} {{$last ? '' : ', '}}
|
||||
{{scope.name ? scope.name : scope}} {{$last ? '' : ', '}}
|
||||
</span>
|
||||
</span>
|
||||
</td>
|
||||
|
|
|
@ -27,8 +27,16 @@
|
|||
<option value="" selected ng-click="query.type = ''">{{:: 'authz-all-types' | translate}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<select class="form-control search" data-ng-model="detailsFilter" data-ng-change="searchQuery();">
|
||||
<option value="" selected>Hide Details</option>
|
||||
<option value="true">Show Details</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pull-right">
|
||||
<a id="hideDetails" data-ng-show="showDetailsFlag" class="btn btn-default" data-ng-click="showDetailsFlag = !showDetailsFlag;showDetails();" href="">{{:: 'authz-hide-details' | translate}}</a>
|
||||
<a id="showDetails" data-ng-hide="showDetailsFlag" class="btn btn-default" data-ng-click="showDetailsFlag = !showDetailsFlag;showDetails();" href="">{{:: 'authz-show-details' | translate}}</a>
|
||||
<select class="form-control" ng-model="policyType"
|
||||
ng-options="p.name for p in policyProviders track by p.type"
|
||||
data-ng-change="addPolicy(policyType);">
|
||||
|
@ -42,6 +50,7 @@
|
|||
<th>{{:: 'name' | translate}}</th>
|
||||
<th>{{:: 'description' | translate}}</th>
|
||||
<th>{{:: 'type' | translate}}</th>
|
||||
<th>{{:: 'actions' | translate}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tfoot data-ng-show="policies && (policies.length >= query.max || query.first > 0)">
|
||||
|
@ -51,15 +60,49 @@
|
|||
<button data-ng-click="firstPage()" class="first" ng-disabled="query.first == 0">{{:: 'first-page' | translate}}</button>
|
||||
<button data-ng-click="previousPage()" class="prev" ng-disabled="query.first == 0">{{:: 'previous-page' | translate}}</button>
|
||||
<button data-ng-click="nextPage()" class="next" ng-disabled="policies.length < query.max">{{:: 'next-page' | translate}}</button>
|
||||
<select class="first" data-ng-model="query.max"
|
||||
ng-options="size for size in listSizes" data-ng-change="firstPage()">
|
||||
</select>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
<tbody>
|
||||
<tr ng-repeat="policy in policies | filter: {name: search.name, type: search.type} | orderBy:'name'">
|
||||
<tr ng-repeat-start="policy in policies | filter: {name: search.name, type: search.type} | orderBy:'name'">
|
||||
<td><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/policy/{{policy.type}}/{{policy.id}}">{{policy.name}}</a></td>
|
||||
<td>{{policy.description}}</td>
|
||||
<td>{{policy.type}}</td>
|
||||
<td ng-if="!policy.details.loaded" class="kc-action-cell" data-ng-click="showDetails(policy);">
|
||||
{{:: 'authz-show-details' | translate}}
|
||||
</td>
|
||||
<td ng-if="policy.details.loaded" class="kc-action-cell" data-ng-click="showDetails(policy);">
|
||||
{{:: 'authz-hide-details' | translate}}
|
||||
</td>
|
||||
</tr>
|
||||
<tr ng-if="policy.details && policy.details.loaded" ng-repeat-end="">
|
||||
<td colspan="4">
|
||||
<div id="details">
|
||||
<table class="table kc-authz-table-expanded table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Dependent Permissions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<span data-ng-show="policy.dependentPolicies && !policy.dependentPolicies.length">{{:: 'authz-no-permission-assigned' | translate}}</span>
|
||||
<ul ng-repeat="dep in policy.dependentPolicies" data-ng-show="policy.dependentPolicies.length > 0">
|
||||
<li>
|
||||
<a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/permission/{{dep.type}}/{{dep.id}}">{{dep.name}}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr data-ng-show="(policies | filter:search).length == 0">
|
||||
<td class="text-muted" colspan="3" data-ng-show="search.name">{{:: 'no-results' | translate}}</td>
|
||||
|
|
|
@ -47,9 +47,7 @@
|
|||
<label class="col-md-2 control-label" for="reqActions">{{:: 'authz-scopes' | translate}}</label>
|
||||
|
||||
<div class="col-md-6">
|
||||
<select ui-select2 id="reqActions" ng-model="resource.scopes" data-placeholder="{{:: 'authz-select-scope' | translate}}..." multiple>
|
||||
<option ng-repeat="scope in scopes" value="{{scope.name}}" ng-selected="true">{{scope.name}}</option>
|
||||
</select>
|
||||
<input type="hidden" ui-select2="scopesUiSelect" id="reqActions" data-ng-model="resource.scopes" data-placeholder="{{:: 'authz-select-scope' | translate}}..." multiple/>
|
||||
</div>
|
||||
|
||||
<kc-tooltip>{{:: 'authz-resource-scopes.tooltip' | translate}}</kc-tooltip>
|
||||
|
|
|
@ -39,8 +39,13 @@
|
|||
<i class="fa fa-search" type="submit" data-ng-click="firstPage()"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<select class="form-control search" data-ng-model="detailsFilter" data-ng-change="searchQuery();">
|
||||
<option value="" selected>Hide Details</option>
|
||||
<option value="true">Show Details</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pull-right">
|
||||
<a id="createResource" class="btn btn-default" href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/resource/create">{{:: 'create' | translate}}</a>
|
||||
</div>
|
||||
|
@ -52,9 +57,7 @@
|
|||
<th>{{:: 'type' | translate}}</th>
|
||||
<th>{{:: 'authz-uri' | translate}}</th>
|
||||
<th>{{:: 'authz-owner' | translate}}</th>
|
||||
<th>{{:: 'authz-scopes' | translate}}</th>
|
||||
<th>{{:: 'authz-permissions' | translate}}</th>
|
||||
<th>{{:: 'actions' | translate}}</th>
|
||||
<th colspan="2">{{:: 'actions' | translate}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tfoot data-ng-show="resources && (resources.length >= query.max || query.first > 0)">
|
||||
|
@ -64,37 +67,67 @@
|
|||
<button data-ng-click="firstPage()" class="first" ng-disabled="query.first == 0">{{:: 'first-page' | translate}}</button>
|
||||
<button data-ng-click="previousPage()" class="prev" ng-disabled="query.first == 0">{{:: 'previous-page' | translate}}</button>
|
||||
<button data-ng-click="nextPage()" class="next" ng-disabled="resources.length < query.max">{{:: 'next-page' | translate}}</button>
|
||||
<select class="first" data-ng-model="query.max"
|
||||
ng-options="size for size in listSizes" data-ng-change="firstPage()">
|
||||
</select>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
<tbody>
|
||||
<tr ng-repeat="resource in resources | filter:search | orderBy:'name'">
|
||||
<tr ng-repeat-start="resource in resources | filter:search | orderBy:'name'">
|
||||
<td><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/resource/{{resource._id}}">{{resource.name}}</a></td>
|
||||
<td>
|
||||
<span data-ng-show="resource.type">{{resource.type}}</span>
|
||||
<span data-ng-show="!resource.type">{{:: 'authz-no-type-defined' | translate}}</span>
|
||||
</td>
|
||||
<td>{{resource.uri}}</td>
|
||||
<td>
|
||||
<span data-ng-show="resource.uri">{{resource.uri}}</span>
|
||||
<span data-ng-show="!resource.uri">{{:: 'authz-no-uri-defined' | translate}}</span>
|
||||
</td>
|
||||
<td>{{resource.owner.name}}</td>
|
||||
<td ng-if="!resource.details.loaded" class="kc-action-cell" data-ng-click="showDetails(resource);">
|
||||
{{:: 'authz-show-details' | translate}}
|
||||
</td>
|
||||
<td ng-if="resource.details.loaded" class="kc-action-cell" data-ng-click="showDetails(resource);">
|
||||
{{:: 'authz-hide-details' | translate}}
|
||||
</td>
|
||||
<td class="kc-action-cell" ng-click="createPolicy(resource);">
|
||||
{{:: 'authz-create-permission' | translate}}
|
||||
</td>
|
||||
</tr>
|
||||
<tr ng-if="resource.details && resource.details.loaded" ng-repeat-end="">
|
||||
<td colspan="6">
|
||||
<div id="details">
|
||||
<table class="table kc-authz-table-expanded table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Scopes</th>
|
||||
<th>Associated Permissions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<span data-ng-show="!resource.scopes.length">{{:: 'authz-no-scopes-assigned' | translate}}</span>
|
||||
<span data-ng-show="resource.scopes.length > 0">
|
||||
<span ng-repeat="scope in resource.scopes">
|
||||
<a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/scope/{{scope.id}}">{{scope.name}}</a>{{$last ? '' : ', '}}
|
||||
</span>
|
||||
</span>
|
||||
<span data-ng-show="resource.scopes && !resource.scopes.length">{{:: 'authz-no-scopes-assigned' | translate}}</span>
|
||||
<ul ng-repeat="scope in resource.scopes" data-ng-show="resource.scopes.length > 0">
|
||||
<li>
|
||||
<a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/scope/{{scope.id}}">{{scope.name}}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<span data-ng-show="!resource.policies.length">{{:: 'authz-no-permission-assigned' | translate}}</span>
|
||||
<span data-ng-show="resource.policies.length > 0">
|
||||
<span ng-repeat="policy in resource.policies">
|
||||
<a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/permission/{{policy.type}}/{{policy.id}}">{{policy.name}}</a>{{$last ? '' : ', '}}
|
||||
</span>
|
||||
</span>
|
||||
<span data-ng-show="resource.policies && !resource.policies.length">{{:: 'authz-no-permission-assigned' | translate}}</span>
|
||||
<ul ng-repeat="policy in resource.policies" data-ng-show="resource.policies.length > 0">
|
||||
<li>
|
||||
<a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/permission/{{policy.type}}/{{policy.id}}">{{policy.name}}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</td>
|
||||
<td class="kc-action-cell" style="vertical-align: middle">
|
||||
<button class="btn btn-default btn-block btn-sm" ng-click="createPolicy(resource);">{{:: 'authz-create-permission' | translate}}</button>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr data-ng-show="(resources | filter:search).length == 0">
|
||||
|
|
|
@ -14,6 +14,12 @@
|
|||
<i class="fa fa-search" id="scopeSearch" type="submit" data-ng-click="firstPage()"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<select class="form-control search" data-ng-model="detailsFilter" data-ng-change="showDetails();">
|
||||
<option value="" selected>Hide Details</option>
|
||||
<option value="true">Show Details</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pull-right">
|
||||
<a id="createScope" class="btn btn-default" href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/scope/create">{{:: 'create' | translate}}</a>
|
||||
|
@ -23,43 +29,68 @@
|
|||
</tr>
|
||||
<tr data-ng-hide="scopes.length == 0">
|
||||
<th>{{:: 'name' | translate}}</th>
|
||||
<th>{{:: 'authz-resources' | translate}}</th>
|
||||
<th>{{:: 'authz-permissions' | translate}}</th>
|
||||
<th>{{:: 'actions' | translate}}</th>
|
||||
<th colspan="2">{{:: 'actions' | translate}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tfoot data-ng-show="scopes && (scopes.length >= query.max || query.first > 0)">
|
||||
<tr>
|
||||
<td colspan="7">
|
||||
<td colspan="8">
|
||||
<div class="table-nav">
|
||||
<button data-ng-click="firstPage()" class="first" ng-disabled="query.first == 0">{{:: 'first-page' | translate}}</button>
|
||||
<button data-ng-click="previousPage()" class="prev" ng-disabled="query.first == 0">{{:: 'previous-page' | translate}}</button>
|
||||
<button data-ng-click="nextPage()" class="next" ng-disabled="scopes.length < query.max">{{:: 'next-page' | translate}}</button>
|
||||
<select class="first" data-ng-model="query.max"
|
||||
ng-options="size for size in listSizes" data-ng-change="firstPage()">
|
||||
</select>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
<tbody>
|
||||
<tr ng-repeat="scope in scopes | filter:search | orderBy:'name'">
|
||||
<tr ng-repeat-start="scope in scopes | filter:search | orderBy:'name'">
|
||||
<td><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/scope/{{scope.id}}">{{scope.name}}</a></td>
|
||||
<td ng-if="!scope.details.loaded" class="kc-action-cell" data-ng-click="showDetails(scope);">
|
||||
{{:: 'authz-show-details' | translate}}
|
||||
</td>
|
||||
<td ng-if="scope.details.loaded" class="kc-action-cell" data-ng-click="showDetails(scope);">
|
||||
{{:: 'authz-hide-details' | translate}}
|
||||
</td>
|
||||
<td class="kc-action-cell" ng-click="createPolicy(scope);">
|
||||
{{:: 'authz-create-permission' | translate}}
|
||||
</td>
|
||||
</tr>
|
||||
<tr ng-if="scope.details && scope.details.loaded" ng-repeat-end="">
|
||||
<td colspan="3">
|
||||
<div id="details">
|
||||
<table class="table kc-authz-table-expanded table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Resources</th>
|
||||
<th>Associated Permissions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<span data-ng-show="!scope.resources.length">{{:: 'authz-no-resources-assigned' | translate}}</span>
|
||||
<span data-ng-show="scope.resources.length > 0">
|
||||
<span ng-repeat="resource in scope.resources">
|
||||
<a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/resource/{{resource._id}}">{{resource.name}}</a>{{$last ? '' : ', '}}
|
||||
</span>
|
||||
</span>
|
||||
<span data-ng-show="scope.resources && !scope.resources.length">{{:: 'authz-no-resources-assigned' | translate}}</span>
|
||||
<ul ng-repeat="resource in scope.resources" data-ng-show="scope.resources.length > 0">
|
||||
<li>
|
||||
<a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/resource/{{resource._id}}">{{resource.name}}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<span data-ng-show="!scope.policies.length">{{:: 'authz-no-permission-assigned' | translate}}</span>
|
||||
<span data-ng-show="scope.policies.length > 0">
|
||||
<span ng-repeat="policy in scope.policies">
|
||||
<a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/permission/{{policy.type}}/{{policy.id}}">{{policy.name}}</a>{{$last ? '' : ', '}}
|
||||
</span>
|
||||
</span>
|
||||
<span data-ng-show="scope.policies && !scope.policies.length">{{:: 'authz-no-permission-assigned' | translate}}</span>
|
||||
<ul ng-repeat="policy in scope.policies" data-ng-show="scope.policies.length > 0">
|
||||
<li>
|
||||
<a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/permission/{{policy.type}}/{{policy.id}}">{{policy.name}}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</td>
|
||||
<td class="kc-action-cell" style="vertical-align: middle">
|
||||
<button class="btn btn-default btn-block btn-sm" ng-click="createPolicy(scope);">{{:: 'authz-create-permission' | translate}}</button>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr data-ng-show="(scopes | filter:search).length == 0">
|
||||
|
|
|
@ -391,3 +391,12 @@ h1 i {
|
|||
margin-left: 2%;
|
||||
width: 25%;
|
||||
}
|
||||
|
||||
table.kc-authz-table-expanded {
|
||||
margin-top: 0px !important;
|
||||
}
|
||||
|
||||
.no-gutter > [class*='col-'] {
|
||||
padding-right:0!important;
|
||||
padding-left:0!important;
|
||||
}
|
Loading…
Reference in a new issue