Merge pull request #3662 from pedroigor/KEYCLOAK-4034

[KEYCLOAK-4034] - Improvements to UI, performance and some code cleanup
This commit is contained in:
Pedro Igor 2016-12-19 16:49:10 -02:00 committed by GitHub
commit 40591cff25
88 changed files with 2667 additions and 1553 deletions

View file

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

View file

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

View file

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

View file

@ -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,31 +60,36 @@ 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 -> {
List<String> clients = new ArrayList<>();
if (resourceServer != null) {
policyStore.findByType(getId(), resourceServer.getId()).forEach(policy -> {
List<String> clients = new ArrayList<>();
for (String clientId : getClients(policy)) {
if (!clientId.equals(removedClient.getId())) {
clients.add(clientId);
for (String clientId : getClients(policy)) {
if (!clientId.equals(removedClient.getId())) {
clients.add(clientId);
}
}
}
try {
if (clients.isEmpty()) {
policyStore.findDependentPolicies(policy.getId()).forEach(dependentPolicy -> {
dependentPolicy.removeAssociatedPolicy(policy);
});
policyStore.delete(policy.getId());
} else {
policy.getConfig().put("clients", JsonSerialization.writeValueAsString(clients));
try {
if (clients.isEmpty()) {
policyStore.findDependentPolicies(policy.getId(), resourceServer.getId()).forEach(dependentPolicy -> {
dependentPolicy.removeAssociatedPolicy(policy);
});
policyStore.delete(policy.getId());
} else {
policy.getConfig().put("clients", JsonSerialization.writeValueAsString(clients));
}
} catch (IOException e) {
throw new RuntimeException("Error while synchronizing clients with policy [" + policy.getName() + "].", e);
}
} catch (IOException e) {
throw new RuntimeException("Error while synchronizing clients with policy [" + policy.getName() + "].", e);
}
});
});
}
}
});
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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,43 +87,63 @@ 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 -> {
List<Map> roles = new ArrayList<>();
for (Map<String,Object> role : getRoles(policy)) {
if (!role.get("id").equals(removedRole.getId())) {
Map updated = new HashMap();
updated.put("id", role.get("id"));
Object required = role.get("required");
if (required != null) {
updated.put("required", required);
}
roles.add(updated);
}
}
try {
if (roles.isEmpty()) {
policyStore.findDependentPolicies(policy.getId()).forEach(dependentPolicy -> {
dependentPolicy.removeAssociatedPolicy(policy);
});
policyStore.delete(policy.getId());
} else {
Map<String, String> config = policy.getConfig();
config.put("roles", JsonSerialization.writeValueAsString(roles));
policy.setConfig(config);
}
} catch (IOException e) {
throw new RuntimeException("Error while synchronizing roles with policy [" + policy.getName() + "].", e);
}
});
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)) {
if (!role.get("id").equals(removedRole.getId())) {
Map updated = new HashMap();
updated.put("id", role.get("id"));
Object required = role.get("required");
if (required != null) {
updated.put("required", required);
}
roles.add(updated);
}
}
try {
if (roles.isEmpty()) {
policyStore.findDependentPolicies(policy.getId(), resourceServer.getId()).forEach(dependentPolicy -> {
dependentPolicy.removeAssociatedPolicy(policy);
if (dependentPolicy.getAssociatedPolicies().isEmpty()) {
policyStore.delete(dependentPolicy.getId());
}
});
policyStore.delete(policy.getId());
} else {
Map<String, String> config = policy.getConfig();
config.put("roles", JsonSerialization.writeValueAsString(roles));
policy.setConfig(config);
}
} catch (IOException e) {
throw new RuntimeException("Error while synchronizing roles with policy [" + policy.getName() + "].", e);
}
});
}
}
@Override
public void close() {

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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,29 +82,40 @@ 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 -> {
List<String> users = new ArrayList<>();
if (resourceServer != null) {
policyStore.findByType(getId(), resourceServer.getId()).forEach(policy -> {
List<String> users = new ArrayList<>();
for (String userId : getUsers(policy)) {
if (!userId.equals(removedUser.getId())) {
users.add(userId);
}
}
for (String userId : getUsers(policy)) {
if (!userId.equals(removedUser.getId())) {
users.add(userId);
}
}
try {
if (users.isEmpty()) {
policyStore.findDependentPolicies(policy.getId()).forEach(dependentPolicy -> {
dependentPolicy.removeAssociatedPolicy(policy);
});
policyStore.delete(policy.getId());
} else {
policy.getConfig().put("users", JsonSerialization.writeValueAsString(users));
}
} catch (IOException e) {
throw new RuntimeException("Error while synchronizing users with policy [" + policy.getName() + "].", e);
try {
if (users.isEmpty()) {
policyStore.findDependentPolicies(policy.getId(), resourceServer.getId()).forEach(dependentPolicy -> {
dependentPolicy.removeAssociatedPolicy(policy);
if (dependentPolicy.getAssociatedPolicies().isEmpty()) {
policyStore.delete(dependentPolicy.getId());
}
});
policyStore.delete(policy.getId());
} else {
policy.getConfig().put("users", JsonSerialization.writeValueAsString(users));
}
} catch (IOException e) {
throw new RuntimeException("Error while synchronizing users with policy [" + policy.getName() + "].", e);
}
});
}
});
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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);
for (String id : cached.getAssociatedPoliciesIds()) {
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);
for (String id : cached.getResourcesIds()) {
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);
for (String id : cached.getScopesIds()) {
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());
}
}

View file

@ -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();
String cacheKeyForResourceServer = getCacheKeyForResourceServerClientId(id);
List<String> cached = this.cache.get(cacheKeyForResourceServer);
if (cacheKey.startsWith(RS_ID_CACHE_PREFIX)) {
List<ResourceServer> cache = (List<ResourceServer>) entry.getValue();
ResourceServer resourceServer = cache.get(0);
if (cached == null) {
ResourceServer resourceServer = getDelegate().findByClient(id);
if (resourceServer.getClientId().equals(id)) {
return findById(resourceServer.getId());
}
if (resourceServer != null) {
cache.put(cacheKeyForResourceServer, Arrays.asList(resourceServer.getId()));
return findById(resourceServer.getId());
}
return null;
}
ResourceServer resourceServer = getDelegate().findByClient(id);
if (resourceServer != null) {
return findById(updateResourceServerCache(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) {

View file

@ -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();
String cacheKeyForResource = getCacheKeyForResourceName(name, resourceServerId);
List<CachedResource> cached = this.cache.get(cacheKeyForResource);
if (cacheKey.startsWith(RESOURCE_ID_CACHE_PREFIX)) {
List<CachedResource> value = (List<CachedResource>) entry.getValue();
CachedResource resource = value.get(0);
if (cached == null) {
Resource resource = getDelegate().findByName(name, resourceServerId);
if (resource.getResourceServerId().equals(resourceServerId) && resource.getName().equals(name)) {
return findById(resource.getId());
}
if (resource != null) {
cache.put(cacheKeyForResource, Arrays.asList(new CachedResource(resource)));
return findById(resource.getId(), resourceServerId);
}
return null;
}
Resource resource = getDelegate().findByName(name, resourceServerId);
if (resource != null) {
return findById(updateResourceCache(resource).getId());
}
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);
for (String id : cached.getScopesIds()) {
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));
}
}

View file

@ -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,26 +98,21 @@ public class CachedScopeStore implements ScopeStore {
@Override
public Scope findByName(String name, String resourceServerId) {
for (Entry entry : this.cache.entrySet()) {
String cacheKey = (String) entry.getKey();
String cacheKeyForScope = getCacheKeyForScopeName(name, resourceServerId);
List<String> cached = this.cache.get(cacheKeyForScope);
if (cacheKey.startsWith(SCOPE_ID_CACHE_PREFIX)) {
List<CachedScope> cache = (List<CachedScope>) entry.getValue();
CachedScope scope = cache.get(0);
if (cached == null) {
Scope scope = getDelegate().findByName(name, resourceServerId);
if (scope.getResourceServerId().equals(resourceServerId) && scope.getName().equals(name)) {
return findById(scope.getId());
}
if (scope != null) {
cache.put(cacheKeyForScope, Arrays.asList(scope.getId()));
return findById(scope.getId(), resourceServerId);
}
return null;
}
Scope scope = getDelegate().findByName(name, resourceServerId);
if (scope != null) {
return findById(updateScopeCache(scope).getId());
}
return null;
return findById(cached.get(0), resourceServerId);
}
@Override
@ -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);

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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,12 +62,30 @@ 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
@ -74,8 +93,8 @@ public class JPAScopeStore implements ScopeStore {
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) -> {
predicates.add(builder.like(builder.lower(root.get(name)), "%" + value[0].toLowerCase() + "%"));
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")));

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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,105 +34,95 @@ 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) {
private void evaluatePolicies(Supplier<List<Policy>> supplier, Consumer<Policy> consumer) {
List<Policy> policies = supplier.get();
if (!policies.isEmpty()) {
policies.forEach(consumer);
}
}
private Consumer<Policy> createDecisionConsumer(ResourcePermission permission, EvaluationContext executionContext, Decision decision, AtomicBoolean verified) {
return (parentPolicy) -> {
if (hasRequestedScopes(permission, parentPolicy)) {
for (Policy associatedPolicy : parentPolicy.getAssociatedPolicies()) {
PolicyProviderFactory providerFactory = policyProviders.get(associatedPolicy.getType());
if (providerFactory == null) {
throw new RuntimeException("Could not find a policy provider for policy type [" + associatedPolicy.getType() + "].");
}
PolicyProvider policyProvider = providerFactory.create(associatedPolicy, this.authorization);
if (policyProvider == null) {
throw new RuntimeException("Unknown parentPolicy provider for type [" + associatedPolicy.getType() + "].");
}
DefaultEvaluation evaluation = createEvaluation(permission, executionContext, decision, parentPolicy, associatedPolicy);
policyProvider.evaluate(evaluation);
evaluation.denyIfNoEffect();
policiesCount.incrementAndGet();
}
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() + "].");
}
DefaultEvaluation evaluation = createEvaluation(permission, executionContext, decision, parentPolicy, associatedPolicy);
policyProvider.evaluate(evaluation);
evaluation.denyIfNoEffect();
}
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) {
@ -136,7 +134,7 @@ public class DefaultPolicyEvaluator implements PolicyEvaluator {
Set<Resource> policyResources = policy.getResources();
if (resourcePermission != null && !policyResources.isEmpty()) {
if (!policyResources.stream().filter(resource -> resource.getId().equals(resourcePermission.getId())).findFirst().isPresent()) {
if (!policyResources.stream().filter(resource -> resource.getId().equals(resourcePermission.getId())).findFirst().isPresent()) {
return false;
}
}
@ -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())) {

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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,17 +44,25 @@ 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 -> {
String resourceId = resource.getId();
policyStore.findByResource(resourceId).forEach(policy -> {
if (policy.getResources().size() == 1) {
policyStore.delete(policy.getId());
} else {
policy.removeResource(resource);
}
});
resourceStore.delete(resourceId);
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, resourceServer.getId()).forEach(policy -> {
if (policy.getResources().size() == 1) {
policyStore.delete(policy.getId());
} else {
policy.removeResource(resource);
}
});
resourceStore.delete(resourceId);
});
}
});
}
}

View file

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

View file

@ -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,55 +841,36 @@ public class ModelToRepresentation {
resource.setOwner(owner);
resource.setScopes(model.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;
}).collect(Collectors.toSet()));
resource.setTypedScopes(new ArrayList<>());
if (resource.getType() != null) {
ResourceStore resourceStore = authorization.getStoreFactory().getResourceStore();
for (Resource typed : resourceStore.findByType(resource.getType())) {
if (typed.getOwner().equals(resourceServer.getClientId()) && !typed.getId().equals(resource.getId())) {
resource.setTypedScopes(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 -> !resource.getScopes().contains(scopeRepresentation)).collect(Collectors.toList()));
if (deep) {
resource.setScopes(model.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;
}).collect(Collectors.toSet()));
resource.setPolicies(new ArrayList<>());
resource.setTypedScopes(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);
if (resource.getType() != null) {
ResourceStore resourceStore = authorization.getStoreFactory().getResourceStore();
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();
scope.setId(model1.getId());
scope.setName(model1.getName());
String iconUri = model1.getIconUri();
if (iconUri != null) {
scope.setIconUri(iconUri);
}
return scope;
}).filter(scopeRepresentation -> !resource.getScopes().contains(scopeRepresentation)).collect(Collectors.toList()));
}
}
}
}

View file

@ -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,21 +1999,18 @@ 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) {
Resource resource = resourceStore.findByName(resourceName, resourceServer.getId());
config.put("resources", JsonSerialization.writeValueAsString(resources.stream().map(resourceName -> {
Resource resource = resourceStore.findByName(resourceName, resourceServer.getId());
if (resource == null) {
resource = resourceStore.findById(resourceName);
}
if (resource == null) {
throw new RuntimeException("Resource with name [" + resourceName + "] not defined.");
}
return resource.getId();
if (resource == null) {
resource = resourceStore.findById(resourceName, resourceServer.getId());
}
if (resource == null) {
throw new RuntimeException("Resource with name [" + resourceName + "] not defined.");
}
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());
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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,31 +170,40 @@ public class EntitlementService {
StoreFactory storeFactory = authorization.getStoreFactory();
ResourceServer resourceServer = storeFactory.getResourceServerStore().findByClient(client.getId());
authorization.evaluators().from(createPermissions(entitlementRequest, resourceServer, authorization), new KeycloakEvaluationContext(this.authorization.getKeycloakSession())).evaluate(new DecisionResultCollector() {
@Override
public void onError(Throwable cause) {
asyncResponse.resume(cause);
}
@Override
protected void onComplete(List<Result> results) {
List<Permission> entitlements = Permissions.allPermits(results, authorization);
if (entitlements.isEmpty()) {
HashMap<Object, Object> error = new HashMap<>();
error.put(OAuth2Constants.ERROR, "not_authorized");
asyncResponse.resume(Cors.add(request, Response.status(Status.FORBIDDEN)
.entity(error))
.allowedOrigins(identity.getAccessToken())
.exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS).build());
} else {
asyncResponse.resume(Cors.add(request, Response.ok().entity(new EntitlementResponse(createRequestingPartyToken(entitlements)))).allowedOrigins(identity.getAccessToken()).allowedMethods("GET").exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS).build());
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);
}
@Override
protected void onComplete(List<Result> results) {
List<Permission> entitlements = Permissions.allPermits(results, authorization, resourceServer);
if (entitlements.isEmpty()) {
HashMap<Object, Object> error = new HashMap<>();
error.put(OAuth2Constants.ERROR, "not_authorized");
asyncResponse.resume(Cors.add(request, Response.status(Status.FORBIDDEN)
.entity(error))
.allowedOrigins(identity.getAccessToken())
.exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS).build());
} else {
asyncResponse.resume(Cors.add(request, Response.ok().entity(new EntitlementResponse(createRequestingPartyToken(entitlements)))).allowedOrigins(identity.getAccessToken()).allowedMethods("GET").exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS).build());
}
}
});
} 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());

View file

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

View file

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

View file

@ -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,78 +94,141 @@ 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;
}
List<Resource> resources = new ArrayList<>();
Resource resource = permission.getResource();
if (resource != null) {
resources.add(resource);
} else {
List<Scope> permissionScopes = permission.getScopes();
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()])));
}
}
if (!resources.isEmpty()) {
for (Resource allowedResource : resources) {
String resourceId = allowedResource.getId();
String resourceName = allowedResource.getName();
Permission evalPermission = permissions.get(allowedResource.getId());
if (evalPermission == null) {
evalPermission = new Permission(resourceId, resourceName, scopes);
permissions.put(resourceId, evalPermission);
}
if (scopes != null && !scopes.isEmpty()) {
Set<String> finalScopes = evalPermission.getScopes();
if (finalScopes == null) {
finalScopes = new HashSet();
evalPermission.setScopes(finalScopes);
}
for (String scopeName : scopes) {
if (!finalScopes.contains(scopeName)) {
finalScopes.add(scopeName);
}
}
}
}
} else {
Permission scopePermission = new Permission(null, null, scopes);
permissions.put(scopePermission.toString(), scopePermission);
}
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);
} else {
List<Scope> permissionScopes = permission.getScopes();
if (!permissionScopes.isEmpty()) {
ResourceStore resourceStore = authorizationProvider.getStoreFactory().getResourceStore();
resources.addAll(resourceStore.findByScope(permissionScopes.stream().map(Scope::getId).collect(Collectors.toList()), resourceServer));
}
}
if (!resources.isEmpty()) {
for (Resource allowedResource : resources) {
String resourceId = allowedResource.getId();
String resourceName = allowedResource.getName();
Permission evalPermission = permissions.get(allowedResource.getId());
if (evalPermission == null) {
evalPermission = new Permission(resourceId, resourceName, scopes);
permissions.put(resourceId, evalPermission);
}
if (scopes != null && !scopes.isEmpty()) {
Set<String> finalScopes = evalPermission.getScopes();
if (finalScopes == null) {
finalScopes = new HashSet();
evalPermission.setScopes(finalScopes);
}
for (String scopeName : scopes) {
if (!finalScopes.contains(scopeName)) {
finalScopes.add(scopeName);
}
}
}
}
} else {
Permission scopePermission = new Permission(null, null, scopes);
permissions.put(scopePermission.toString(), scopePermission);
}
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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>
@ -246,11 +234,11 @@
<td>{{resource.name ? resource.name : 'authz-evaluation-any-resource-with-scopes' | translate}}</td>
<td>
<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 ? '' : ', '}}
<span data-ng-show="resource.scopes.length > 0">
<span ng-repeat="scope in resource.scopes">
{{scope.name ? scope.name : scope}} {{$last ? '' : ', '}}
</span>
</span>
</span>
</td>
<td class="kc-action-cell">
<button class="btn btn-default btn-block btn-sm"

View file

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

View file

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

View file

@ -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>
<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>
<td ng-if="!resource.details.loaded" class="kc-action-cell" data-ng-click="showDetails(resource);">
{{:: 'authz-show-details' | translate}}
</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>
<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" style="vertical-align: middle">
<button class="btn btn-default btn-block btn-sm" ng-click="createPolicy(resource);">{{:: 'authz-create-permission' | translate}}</button>
<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 && !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 && !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>
</tr>
</tbody>
</table>
</div>
</td>
</tr>
<tr data-ng-show="(resources | filter:search).length == 0">

View file

@ -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>
<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>
<td ng-if="!scope.details.loaded" class="kc-action-cell" data-ng-click="showDetails(scope);">
{{:: 'authz-show-details' | translate}}
</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>
<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" style="vertical-align: middle">
<button class="btn btn-default btn-block btn-sm" ng-click="createPolicy(scope);">{{:: 'authz-create-permission' | translate}}</button>
<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 && !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 && !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>
</tr>
</tbody>
</table>
</div>
</td>
</tr>
<tr data-ng-show="(scopes | filter:search).length == 0">

View file

@ -390,4 +390,13 @@ h1 i {
float: left;
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;
}