[KEYCLOAK-3372] - Code cleanup
This commit is contained in:
parent
e6ce2e138d
commit
3c8ed8e3d8
18 changed files with 179 additions and 129 deletions
|
@ -126,11 +126,13 @@ public abstract class AbstractPolicyEnforcer {
|
||||||
List<Permission> permissions = authorization.getPermissions();
|
List<Permission> permissions = authorization.getPermissions();
|
||||||
|
|
||||||
for (Permission permission : permissions) {
|
for (Permission permission : permissions) {
|
||||||
Set<String> allowedScopes = permission.getScopes();
|
|
||||||
|
|
||||||
if (permission.getResourceSetId() != null) {
|
if (permission.getResourceSetId() != null) {
|
||||||
if (isResourcePermission(actualPathConfig, permission)) {
|
if (isResourcePermission(actualPathConfig, permission)) {
|
||||||
if (((allowedScopes == null || allowedScopes.isEmpty()) && requiredScopes.isEmpty()) || allowedScopes.containsAll(requiredScopes)) {
|
if (actualPathConfig.isInstance() && !matchResourcePermission(actualPathConfig, permission)) {
|
||||||
|
continue;
|
||||||
|
|
||||||
|
}
|
||||||
|
if (hasResourceScopePermission(requiredScopes, permission, actualPathConfig)) {
|
||||||
LOGGER.debugf("Authorization GRANTED for path [%s]. Permissions [%s].", actualPathConfig, permissions);
|
LOGGER.debugf("Authorization GRANTED for path [%s]. Permissions [%s].", actualPathConfig, permissions);
|
||||||
if (request.getMethod().equalsIgnoreCase("DELETE") && actualPathConfig.isInstance()) {
|
if (request.getMethod().equalsIgnoreCase("DELETE") && actualPathConfig.isInstance()) {
|
||||||
this.paths.remove(actualPathConfig);
|
this.paths.remove(actualPathConfig);
|
||||||
|
@ -138,11 +140,6 @@ public abstract class AbstractPolicyEnforcer {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
if ((allowedScopes.isEmpty() && requiredScopes.isEmpty()) || allowedScopes.containsAll(requiredScopes)) {
|
|
||||||
LOGGER.debugf("Authorization GRANTED for path [%s]. Permissions [%s].", actualPathConfig, permissions);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,6 +148,11 @@ public abstract class AbstractPolicyEnforcer {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean hasResourceScopePermission(Set<String> requiredScopes, Permission permission, PathConfig actualPathConfig) {
|
||||||
|
Set<String> allowedScopes = permission.getScopes();
|
||||||
|
return (allowedScopes.containsAll(requiredScopes) || allowedScopes.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
protected AuthzClient getAuthzClient() {
|
protected AuthzClient getAuthzClient() {
|
||||||
return this.authzClient;
|
return this.authzClient;
|
||||||
}
|
}
|
||||||
|
@ -210,7 +212,6 @@ public abstract class AbstractPolicyEnforcer {
|
||||||
config.setPath(targetResource.getUri());
|
config.setPath(targetResource.getUri());
|
||||||
config.setScopes(originalConfig.getScopes());
|
config.setScopes(originalConfig.getScopes());
|
||||||
config.setMethods(originalConfig.getMethods());
|
config.setMethods(originalConfig.getMethods());
|
||||||
config.setInstance(true);
|
|
||||||
config.setParentConfig(originalConfig);
|
config.setParentConfig(originalConfig);
|
||||||
|
|
||||||
this.paths.add(config);
|
this.paths.add(config);
|
||||||
|
@ -244,13 +245,17 @@ public abstract class AbstractPolicyEnforcer {
|
||||||
|
|
||||||
private boolean isResourcePermission(PathConfig actualPathConfig, Permission permission) {
|
private boolean isResourcePermission(PathConfig actualPathConfig, Permission permission) {
|
||||||
// first we try a match using resource id
|
// first we try a match using resource id
|
||||||
boolean resourceMatch = permission.getResourceSetId().equals(actualPathConfig.getId());
|
boolean resourceMatch = matchResourcePermission(actualPathConfig, permission);
|
||||||
|
|
||||||
// as a fallback, check if the current path is an instance and if so, check if parent's id matches the permission
|
// as a fallback, check if the current path is an instance and if so, check if parent's id matches the permission
|
||||||
if (!resourceMatch && actualPathConfig.isInstance()) {
|
if (!resourceMatch && actualPathConfig.isInstance()) {
|
||||||
resourceMatch = permission.getResourceSetId().equals(actualPathConfig.getParentConfig().getId());
|
resourceMatch = matchResourcePermission(actualPathConfig.getParentConfig(), permission);
|
||||||
}
|
}
|
||||||
|
|
||||||
return resourceMatch;
|
return resourceMatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean matchResourcePermission(PathConfig actualPathConfig, Permission permission) {
|
||||||
|
return permission.getResourceSetId().equals(actualPathConfig.getId());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,7 +50,7 @@ public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer {
|
||||||
int retry = 2;
|
int retry = 2;
|
||||||
AccessToken original = accessToken;
|
AccessToken original = accessToken;
|
||||||
|
|
||||||
while (retry >= 0) {
|
while (retry > 0) {
|
||||||
if (super.isAuthorized(pathConfig, requiredScopes, accessToken, httpFacade)) {
|
if (super.isAuthorized(pathConfig, requiredScopes, accessToken, httpFacade)) {
|
||||||
original.setAuthorization(accessToken.getAuthorization());
|
original.setAuthorization(accessToken.getAuthorization());
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -45,7 +45,6 @@ public class PolicyEnforcer {
|
||||||
private static Logger LOGGER = Logger.getLogger(PolicyEnforcer.class);
|
private static Logger LOGGER = Logger.getLogger(PolicyEnforcer.class);
|
||||||
|
|
||||||
private final KeycloakDeployment deployment;
|
private final KeycloakDeployment deployment;
|
||||||
private final PathMatcher pathMatcher;
|
|
||||||
private final AuthzClient authzClient;
|
private final AuthzClient authzClient;
|
||||||
private final PolicyEnforcerConfig enforcerConfig;
|
private final PolicyEnforcerConfig enforcerConfig;
|
||||||
private final List<PathConfig> paths;
|
private final List<PathConfig> paths;
|
||||||
|
@ -54,7 +53,6 @@ public class PolicyEnforcer {
|
||||||
this.deployment = deployment;
|
this.deployment = deployment;
|
||||||
this.enforcerConfig = adapterConfig.getPolicyEnforcerConfig();
|
this.enforcerConfig = adapterConfig.getPolicyEnforcerConfig();
|
||||||
this.authzClient = AuthzClient.create(new Configuration(adapterConfig.getAuthServerUrl(), adapterConfig.getRealm(), adapterConfig.getResource(), adapterConfig.getCredentials(), deployment.getClient()));
|
this.authzClient = AuthzClient.create(new Configuration(adapterConfig.getAuthServerUrl(), adapterConfig.getRealm(), adapterConfig.getResource(), adapterConfig.getCredentials(), deployment.getClient()));
|
||||||
this.pathMatcher = new PathMatcher();
|
|
||||||
this.paths = configurePaths(this.authzClient.protection().resource(), this.enforcerConfig);
|
this.paths = configurePaths(this.authzClient.protection().resource(), this.enforcerConfig);
|
||||||
|
|
||||||
if (LOGGER.isDebugEnabled()) {
|
if (LOGGER.isDebugEnabled()) {
|
||||||
|
|
|
@ -109,7 +109,6 @@ public class PolicyEnforcerConfig {
|
||||||
private List<MethodConfig> methods = new ArrayList<>();
|
private List<MethodConfig> methods = new ArrayList<>();
|
||||||
private List<String> scopes = Collections.emptyList();
|
private List<String> scopes = Collections.emptyList();
|
||||||
private String id;
|
private String id;
|
||||||
private boolean instance;
|
|
||||||
|
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
private PathConfig parentConfig;
|
private PathConfig parentConfig;
|
||||||
|
@ -178,11 +177,7 @@ public class PolicyEnforcerConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isInstance() {
|
public boolean isInstance() {
|
||||||
return instance;
|
return this.parentConfig != null;
|
||||||
}
|
|
||||||
|
|
||||||
public void setInstance(boolean instance) {
|
|
||||||
this.instance = instance;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setParentConfig(PathConfig parentConfig) {
|
public void setParentConfig(PathConfig parentConfig) {
|
||||||
|
|
|
@ -16,6 +16,8 @@
|
||||||
*/
|
*/
|
||||||
package org.keycloak.representations.idm.authorization;
|
package org.keycloak.representations.idm.authorization;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -35,6 +37,7 @@ public class PolicyRepresentation {
|
||||||
private DecisionStrategy decisionStrategy = DecisionStrategy.UNANIMOUS;
|
private DecisionStrategy decisionStrategy = DecisionStrategy.UNANIMOUS;
|
||||||
private Map<String, String> config = new HashMap();
|
private Map<String, String> config = new HashMap();
|
||||||
private List<PolicyRepresentation> dependentPolicies;
|
private List<PolicyRepresentation> dependentPolicies;
|
||||||
|
@JsonInclude(JsonInclude.Include.NON_EMPTY)
|
||||||
private List<PolicyRepresentation> associatedPolicies = new ArrayList<>();
|
private List<PolicyRepresentation> associatedPolicies = new ArrayList<>();
|
||||||
|
|
||||||
public String getId() {
|
public String getId() {
|
||||||
|
|
|
@ -52,7 +52,6 @@
|
||||||
"sessionName": "MainOwnerSession",
|
"sessionName": "MainOwnerSession",
|
||||||
"mavenArtifactGroupId": "org.keycloak",
|
"mavenArtifactGroupId": "org.keycloak",
|
||||||
"moduleName": "PhotozAuthzOwnerPolicy",
|
"moduleName": "PhotozAuthzOwnerPolicy",
|
||||||
"applyPolicies": "[]",
|
|
||||||
"scannerPeriod": "1",
|
"scannerPeriod": "1",
|
||||||
"scannerPeriodUnit": "Hours"
|
"scannerPeriodUnit": "Hours"
|
||||||
}
|
}
|
||||||
|
@ -64,7 +63,6 @@
|
||||||
"logic": "POSITIVE",
|
"logic": "POSITIVE",
|
||||||
"decisionStrategy": "UNANIMOUS",
|
"decisionStrategy": "UNANIMOUS",
|
||||||
"config": {
|
"config": {
|
||||||
"applyPolicies": "[]",
|
|
||||||
"roles": "[{\"id\":\"admin\",\"required\":true}]"
|
"roles": "[{\"id\":\"admin\",\"required\":true}]"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -75,7 +73,6 @@
|
||||||
"logic": "POSITIVE",
|
"logic": "POSITIVE",
|
||||||
"decisionStrategy": "UNANIMOUS",
|
"decisionStrategy": "UNANIMOUS",
|
||||||
"config": {
|
"config": {
|
||||||
"applyPolicies": "[]",
|
|
||||||
"roles": "[{\"id\":\"user\"},{\"id\":\"manage-albums\",\"required\":true}]"
|
"roles": "[{\"id\":\"user\"},{\"id\":\"manage-albums\",\"required\":true}]"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -86,7 +83,6 @@
|
||||||
"logic": "POSITIVE",
|
"logic": "POSITIVE",
|
||||||
"decisionStrategy": "UNANIMOUS",
|
"decisionStrategy": "UNANIMOUS",
|
||||||
"config": {
|
"config": {
|
||||||
"applyPolicies": "[]",
|
|
||||||
"code": "var contextAttributes = $evaluation.getContext().getAttributes();\n\nif (contextAttributes.containsValue('kc.client.network.ip_address', '127.0.0.1')) {\n $evaluation.grant();\n}"
|
"code": "var contextAttributes = $evaluation.getContext().getAttributes();\n\nif (contextAttributes.containsValue('kc.client.network.ip_address', '127.0.0.1')) {\n $evaluation.grant();\n}"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -117,7 +113,6 @@
|
||||||
"logic": "POSITIVE",
|
"logic": "POSITIVE",
|
||||||
"decisionStrategy": "UNANIMOUS",
|
"decisionStrategy": "UNANIMOUS",
|
||||||
"config": {
|
"config": {
|
||||||
"applyPolicies": "[]",
|
|
||||||
"code": "var context = $evaluation.getContext();\nvar identity = context.getIdentity();\nvar attributes = identity.getAttributes();\nvar email = attributes.getValue('email').asString(0);\n\nif (identity.hasRole('admin') || email.endsWith('@keycloak.org')) {\n $evaluation.grant();\n}"
|
"code": "var context = $evaluation.getContext();\nvar identity = context.getIdentity();\nvar attributes = identity.getAttributes();\nvar email = attributes.getValue('email').asString(0);\n\nif (identity.hasRole('admin') || email.endsWith('@keycloak.org')) {\n $evaluation.grant();\n}"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -109,7 +109,12 @@ public class AlbumService {
|
||||||
|
|
||||||
private void createProtectedResource(Album album) {
|
private void createProtectedResource(Album album) {
|
||||||
try {
|
try {
|
||||||
ResourceRepresentation albumResource = new ResourceRepresentation(album.getName(), new HashSet(), "/album/" + album.getId(), "http://photoz.com/album");
|
HashSet<ScopeRepresentation> scopes = new HashSet<>();
|
||||||
|
|
||||||
|
scopes.add(new ScopeRepresentation(SCOPE_ALBUM_VIEW));
|
||||||
|
scopes.add(new ScopeRepresentation(SCOPE_ALBUM_DELETE));
|
||||||
|
|
||||||
|
ResourceRepresentation albumResource = new ResourceRepresentation(album.getName(), scopes, "/album/" + album.getId(), "http://photoz.com/album");
|
||||||
|
|
||||||
albumResource.setOwner(album.getUserId());
|
albumResource.setOwner(album.getUserId());
|
||||||
|
|
||||||
|
|
|
@ -9,17 +9,18 @@
|
||||||
"secret": "secret"
|
"secret": "secret"
|
||||||
},
|
},
|
||||||
"policy-enforcer": {
|
"policy-enforcer": {
|
||||||
|
"user-managed-access" : {},
|
||||||
"paths": [
|
"paths": [
|
||||||
{
|
{
|
||||||
"path" : "/album/*",
|
"path" : "/album/*",
|
||||||
"methods" : [
|
"methods" : [
|
||||||
{
|
|
||||||
"method": "GET",
|
|
||||||
"scopes" : ["urn:photoz.com:scopes:album:view"]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
"scopes" : ["urn:photoz.com:scopes:album:create"]
|
"scopes" : ["urn:photoz.com:scopes:album:create"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "GET",
|
||||||
|
"scopes" : ["urn:photoz.com:scopes:album:view"]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -30,6 +31,10 @@
|
||||||
{
|
{
|
||||||
"method": "DELETE",
|
"method": "DELETE",
|
||||||
"scopes" : ["urn:photoz.com:scopes:album:delete"]
|
"scopes" : ["urn:photoz.com:scopes:album:delete"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "GET",
|
||||||
|
"scopes" : ["urn:photoz.com:scopes:album:view"]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
|
@ -54,7 +54,7 @@
|
||||||
"description": "Defines that adminsitrators can do something",
|
"description": "Defines that adminsitrators can do something",
|
||||||
"type": "role",
|
"type": "role",
|
||||||
"config": {
|
"config": {
|
||||||
"roles": "[\"admin\"]"
|
"roles": "[{\"id\":\"admin\"}]"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -62,7 +62,7 @@
|
||||||
"description": "Defines that any user can do something",
|
"description": "Defines that any user can do something",
|
||||||
"type": "role",
|
"type": "role",
|
||||||
"config": {
|
"config": {
|
||||||
"roles": "[\"user\"]"
|
"roles": "[{\"id\":\"user\"}]"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -71,7 +71,7 @@
|
||||||
"type": "role",
|
"type": "role",
|
||||||
"logic": "POSITIVE",
|
"logic": "POSITIVE",
|
||||||
"config": {
|
"config": {
|
||||||
"roles": "[\"user_premium\"]"
|
"roles": "[{\"id\":\"user_premium\"}]"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -67,7 +67,8 @@ public interface Attributes {
|
||||||
* @return true if any attribute with <code>name</code> and <code>value</code> exist. Otherwise, returns false.
|
* @return true if any attribute with <code>name</code> and <code>value</code> exist. Otherwise, returns false.
|
||||||
*/
|
*/
|
||||||
default boolean containsValue(String name, String value) {
|
default boolean containsValue(String name, String value) {
|
||||||
return toMap().getOrDefault(name, emptyList()).stream().anyMatch(value::equals);
|
Collection<String> values = toMap().get(name);
|
||||||
|
return values != null && values.stream().anyMatch(value::equals);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -32,8 +32,10 @@ import org.keycloak.authorization.store.StoreFactory;
|
||||||
import org.keycloak.representations.idm.authorization.PolicyEnforcementMode;
|
import org.keycloak.representations.idm.authorization.PolicyEnforcementMode;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
@ -130,27 +132,49 @@ public class DefaultPolicyEvaluator implements PolicyEvaluator {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (policy.getScopes().isEmpty()) {
|
Set<Scope> scopes = new HashSet<>(policy.getScopes());
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean hasScope = true;
|
if (scopes.isEmpty()) {
|
||||||
|
Set<Resource> resources = new HashSet<>();
|
||||||
|
|
||||||
for (Scope givenScope : policy.getScopes()) {
|
resources.addAll(policy.getResources());
|
||||||
boolean hasGivenScope = false;
|
|
||||||
|
|
||||||
for (Scope scope : permission.getScopes()) {
|
for (Resource resource : resources) {
|
||||||
if (givenScope.getId().equals(scope.getId())) {
|
scopes.addAll(resource.getScopes());
|
||||||
hasGivenScope = true;
|
}
|
||||||
break;
|
|
||||||
|
if (!resources.isEmpty() && scopes.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scopes.isEmpty()) {
|
||||||
|
Resource resource = permission.getResource();
|
||||||
|
String type = resource.getType();
|
||||||
|
|
||||||
|
if (type != null) {
|
||||||
|
List<Resource> resourcesByType = authorization.getStoreFactory().getResourceStore().findByType(type);
|
||||||
|
|
||||||
|
for (Resource resourceType : resourcesByType) {
|
||||||
|
if (resourceType.getOwner().equals(resource.getResourceServer().getClientId())) {
|
||||||
|
resources.add(resourceType);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hasGivenScope) {
|
for (Resource resource : resources) {
|
||||||
return false;
|
scopes.addAll(resource.getScopes());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return hasScope;
|
for (Scope givenScope : scopes) {
|
||||||
|
for (Scope scope : permission.getScopes()) {
|
||||||
|
if (givenScope.getId().equals(scope.getId())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -240,10 +240,16 @@ public class PolicyEvaluationService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
accessToken.addAccess(clientModel.getClientId());
|
AccessToken.Access clientAccess = accessToken.addAccess(clientModel.getClientId());
|
||||||
AccessToken.Access resourceAccess = accessToken.getResourceAccess(clientModel.getClientId());
|
clientAccess.roles(new HashSet<>());
|
||||||
|
|
||||||
userModel.getClientRoleMappings(clientModel).stream().map(RoleModel::getName).forEach(roleName -> resourceAccess.addRole(roleName));
|
userModel.getClientRoleMappings(clientModel).stream().map(RoleModel::getName).forEach(roleName -> clientAccess.addRole(roleName));
|
||||||
|
|
||||||
|
ClientModel resourceServerClient = realm.getClientById(resourceServer.getClientId());
|
||||||
|
AccessToken.Access resourceServerAccess = accessToken.addAccess(resourceServerClient.getClientId());
|
||||||
|
resourceServerAccess.roles(new HashSet<>());
|
||||||
|
|
||||||
|
userModel.getClientRoleMappings(resourceServerClient).stream().map(RoleModel::getName).forEach(roleName -> resourceServerAccess.addRole(roleName));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
package org.keycloak.authorization.authorization;
|
package org.keycloak.authorization.authorization;
|
||||||
|
|
||||||
import org.jboss.resteasy.spi.HttpRequest;
|
import org.jboss.resteasy.spi.HttpRequest;
|
||||||
|
import org.keycloak.OAuth2Constants;
|
||||||
import org.keycloak.OAuthErrorException;
|
import org.keycloak.OAuthErrorException;
|
||||||
import org.keycloak.authorization.AuthorizationProvider;
|
import org.keycloak.authorization.AuthorizationProvider;
|
||||||
import org.keycloak.authorization.authorization.representation.AuthorizationRequest;
|
import org.keycloak.authorization.authorization.representation.AuthorizationRequest;
|
||||||
|
@ -50,8 +51,6 @@ import javax.ws.rs.container.Suspended;
|
||||||
import javax.ws.rs.core.Context;
|
import javax.ws.rs.core.Context;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
import javax.ws.rs.core.Response.Status;
|
import javax.ws.rs.core.Response.Status;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -104,8 +103,12 @@ public class AuthorizationTokenService {
|
||||||
List<Permission> entitlements = Permissions.allPermits(results);
|
List<Permission> entitlements = Permissions.allPermits(results);
|
||||||
|
|
||||||
if (entitlements.isEmpty()) {
|
if (entitlements.isEmpty()) {
|
||||||
|
HashMap<Object, Object> error = new HashMap<>();
|
||||||
|
|
||||||
|
error.put(OAuth2Constants.ERROR, "not_authorized");
|
||||||
|
|
||||||
asyncResponse.resume(Cors.add(httpRequest, Response.status(Status.FORBIDDEN)
|
asyncResponse.resume(Cors.add(httpRequest, Response.status(Status.FORBIDDEN)
|
||||||
.entity(new ErrorResponseException("not_authorized", "Authorization denied.", Status.FORBIDDEN)))
|
.entity(error))
|
||||||
.allowedOrigins(identity.getAccessToken())
|
.allowedOrigins(identity.getAccessToken())
|
||||||
.exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS).build());
|
.exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS).build());
|
||||||
} else {
|
} else {
|
||||||
|
@ -193,14 +196,7 @@ public class AuthorizationTokenService {
|
||||||
return permissionsToEvaluate.entrySet().stream()
|
return permissionsToEvaluate.entrySet().stream()
|
||||||
.flatMap((Function<Entry<String, Set<String>>, Stream<ResourcePermission>>) entry -> {
|
.flatMap((Function<Entry<String, Set<String>>, Stream<ResourcePermission>>) entry -> {
|
||||||
Resource entryResource = storeFactory.getResourceStore().findById(entry.getKey());
|
Resource entryResource = storeFactory.getResourceStore().findById(entry.getKey());
|
||||||
if (entry.getValue().isEmpty()) {
|
return Permissions.createResourcePermissions(entryResource, entry.getValue(), authorization).stream();
|
||||||
return Arrays.asList(new ResourcePermission(entryResource, Collections.emptyList(), entryResource.getResourceServer())).stream();
|
|
||||||
} else {
|
|
||||||
return entry.getValue().stream()
|
|
||||||
.map(scopeName -> storeFactory.getScopeStore().findByName(scopeName, entryResource.getResourceServer().getId()))
|
|
||||||
.filter(scope -> scope != null)
|
|
||||||
.map(scope -> new ResourcePermission(entryResource, Arrays.asList(scope), entryResource.getResourceServer()));
|
|
||||||
}
|
|
||||||
}).collect(Collectors.toList());
|
}).collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
package org.keycloak.authorization.entitlement;
|
package org.keycloak.authorization.entitlement;
|
||||||
|
|
||||||
import org.jboss.resteasy.spi.HttpRequest;
|
import org.jboss.resteasy.spi.HttpRequest;
|
||||||
|
import org.keycloak.OAuth2Constants;
|
||||||
import org.keycloak.OAuthErrorException;
|
import org.keycloak.OAuthErrorException;
|
||||||
import org.keycloak.authorization.AuthorizationProvider;
|
import org.keycloak.authorization.AuthorizationProvider;
|
||||||
import org.keycloak.authorization.common.KeycloakEvaluationContext;
|
import org.keycloak.authorization.common.KeycloakEvaluationContext;
|
||||||
|
@ -55,8 +56,6 @@ import javax.ws.rs.container.Suspended;
|
||||||
import javax.ws.rs.core.Context;
|
import javax.ws.rs.core.Context;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
import javax.ws.rs.core.Response.Status;
|
import javax.ws.rs.core.Response.Status;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -119,8 +118,12 @@ public class EntitlementService {
|
||||||
List<Permission> entitlements = Permissions.allPermits(results);
|
List<Permission> entitlements = Permissions.allPermits(results);
|
||||||
|
|
||||||
if (entitlements.isEmpty()) {
|
if (entitlements.isEmpty()) {
|
||||||
|
HashMap<Object, Object> error = new HashMap<>();
|
||||||
|
|
||||||
|
error.put(OAuth2Constants.ERROR, "not_authorized");
|
||||||
|
|
||||||
asyncResponse.resume(Cors.add(request, Response.status(Status.FORBIDDEN)
|
asyncResponse.resume(Cors.add(request, Response.status(Status.FORBIDDEN)
|
||||||
.entity(new ErrorResponseException("not_authorized", "Authorization denied.", Status.FORBIDDEN)))
|
.entity(error))
|
||||||
.allowedOrigins(identity.getAccessToken())
|
.allowedOrigins(identity.getAccessToken())
|
||||||
.exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS).build());
|
.exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS).build());
|
||||||
} else {
|
} else {
|
||||||
|
@ -249,15 +252,7 @@ public class EntitlementService {
|
||||||
return permissionsToEvaluate.entrySet().stream()
|
return permissionsToEvaluate.entrySet().stream()
|
||||||
.flatMap((Function<Map.Entry<String, Set<String>>, Stream<ResourcePermission>>) entry -> {
|
.flatMap((Function<Map.Entry<String, Set<String>>, Stream<ResourcePermission>>) entry -> {
|
||||||
Resource entryResource = storeFactory.getResourceStore().findById(entry.getKey());
|
Resource entryResource = storeFactory.getResourceStore().findById(entry.getKey());
|
||||||
|
return Permissions.createResourcePermissions(entryResource, entry.getValue(), authorization).stream();
|
||||||
if (entry.getValue().isEmpty()) {
|
|
||||||
return Arrays.asList(new ResourcePermission(entryResource, Collections.emptyList(), entryResource.getResourceServer())).stream();
|
|
||||||
} else {
|
|
||||||
return entry.getValue().stream()
|
|
||||||
.map(scopeName -> storeFactory.getScopeStore().findByName(scopeName, entryResource.getResourceServer().getId()))
|
|
||||||
.filter(scope -> scope != null)
|
|
||||||
.map(scope -> new ResourcePermission(entryResource, Arrays.asList(scope), entryResource.getResourceServer()));
|
|
||||||
}
|
|
||||||
}).collect(Collectors.toList());
|
}).collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@ import org.keycloak.authorization.model.Scope;
|
||||||
import org.keycloak.authorization.permission.ResourcePermission;
|
import org.keycloak.authorization.permission.ResourcePermission;
|
||||||
import org.keycloak.authorization.policy.evaluation.Result;
|
import org.keycloak.authorization.policy.evaluation.Result;
|
||||||
import org.keycloak.authorization.store.ResourceStore;
|
import org.keycloak.authorization.store.ResourceStore;
|
||||||
|
import org.keycloak.authorization.store.ScopeStore;
|
||||||
import org.keycloak.authorization.store.StoreFactory;
|
import org.keycloak.authorization.store.StoreFactory;
|
||||||
import org.keycloak.representations.idm.authorization.Permission;
|
import org.keycloak.representations.idm.authorization.Permission;
|
||||||
|
|
||||||
|
@ -61,91 +62,95 @@ public final class Permissions {
|
||||||
StoreFactory storeFactory = authorization.getStoreFactory();
|
StoreFactory storeFactory = authorization.getStoreFactory();
|
||||||
ResourceStore resourceStore = storeFactory.getResourceStore();
|
ResourceStore resourceStore = storeFactory.getResourceStore();
|
||||||
|
|
||||||
resourceStore.findByOwner(resourceServer.getClientId()).stream().forEach(resource -> permissions.addAll(createResourcePermissions(resource, resourceServer, authorization)));
|
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, resourceServer, authorization)));
|
resourceStore.findByOwner(identity.getId()).stream().forEach(resource -> permissions.addAll(createResourcePermissions(resource, resource.getScopes().stream().map(Scope::getName).collect(Collectors.toSet()), authorization)));
|
||||||
|
|
||||||
return permissions;
|
return permissions;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<ResourcePermission> createResourcePermissions(Resource resource, ResourceServer resourceServer, AuthorizationProvider authorization) {
|
public static List<ResourcePermission> createResourcePermissions(Resource resource, Set<String> requestedScopes, AuthorizationProvider authorization) {
|
||||||
List<ResourcePermission> permissions = new ArrayList<>();
|
List<ResourcePermission> permissions = new ArrayList<>();
|
||||||
List<Scope> scopes = resource.getScopes();
|
String type = resource.getType();
|
||||||
|
ResourceServer resourceServer = resource.getResourceServer();
|
||||||
if (scopes.isEmpty()) {
|
List<Scope> scopes;
|
||||||
String type = resource.getType();
|
|
||||||
|
|
||||||
|
if (requestedScopes.isEmpty()) {
|
||||||
|
scopes = resource.getScopes();
|
||||||
// check if there is a typed resource whose scopes are inherited by the resource being requested. In this case, we assume that parent resource
|
// 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
|
// is owned by the resource server itself
|
||||||
if (type != null) {
|
if (type != null && !resource.getOwner().equals(resourceServer.getClientId())) {
|
||||||
StoreFactory storeFactory = authorization.getStoreFactory();
|
StoreFactory storeFactory = authorization.getStoreFactory();
|
||||||
ResourceStore resourceStore = storeFactory.getResourceStore();
|
ResourceStore resourceStore = storeFactory.getResourceStore();
|
||||||
resourceStore.findByType(type).forEach(resource1 -> {
|
resourceStore.findByType(type).forEach(resource1 -> {
|
||||||
if (resource1.getOwner().equals(resourceServer.getClientId())) {
|
if (resource1.getOwner().equals(resourceServer.getClientId())) {
|
||||||
scopes.addAll(resource1.getScopes());
|
for (Scope typeScope : resource1.getScopes()) {
|
||||||
|
if (!scopes.contains(typeScope)) {
|
||||||
|
scopes.add(typeScope);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
ScopeStore scopeStore = authorization.getStoreFactory().getScopeStore();
|
||||||
|
scopes = requestedScopes.stream().map(scopeName -> {
|
||||||
|
Scope byName = scopeStore.findByName(scopeName, resource.getResourceServer().getId());
|
||||||
|
return byName;
|
||||||
|
}).collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (scopes.size() > 1) {
|
if (scopes.isEmpty()) {
|
||||||
|
permissions.add(new ResourcePermission(resource, Collections.emptyList(), resource.getResourceServer()));
|
||||||
|
} else {
|
||||||
for (Scope scope : scopes) {
|
for (Scope scope : scopes) {
|
||||||
permissions.add(new ResourcePermission(resource, Arrays.asList(scope), resource.getResourceServer()));
|
permissions.add(new ResourcePermission(resource, Arrays.asList(scope), resource.getResourceServer()));
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
permissions.add(new ResourcePermission(resource, Collections.emptyList(), resource.getResourceServer()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return permissions;
|
return permissions;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<Permission> allPermits(List<Result> evaluation) {
|
public static List<Permission> allPermits(List<Result> evaluation) {
|
||||||
List<Permission> permissions = evaluation.stream()
|
Map<String, Permission> permissions = new HashMap<>();
|
||||||
.filter(evaluationResult -> evaluationResult.getEffect().equals(Effect.PERMIT))
|
|
||||||
.map(evaluationResult -> {
|
|
||||||
ResourcePermission permission = evaluationResult.getPermission();
|
|
||||||
String resourceId = null;
|
|
||||||
String resourceName = null;
|
|
||||||
|
|
||||||
Resource resource = permission.getResource();
|
for (Result evaluationResult : evaluation) {
|
||||||
|
ResourcePermission permission = evaluationResult.getPermission();
|
||||||
if (resource != null) {
|
Set<String> scopes = permission.getScopes().stream().map(Scope::getName).collect(Collectors.toSet());
|
||||||
resourceId = resource.getId();
|
if (evaluationResult.getEffect().equals(Effect.DENY)) {
|
||||||
resourceName = resource.getName();
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
Set<String> scopes = permission.getScopes().stream().map(Scope::getName).collect(Collectors.toSet());
|
|
||||||
|
|
||||||
return new Permission(resourceId, resourceName, scopes);
|
|
||||||
}).collect(Collectors.toList());
|
|
||||||
|
|
||||||
Map<String, Permission> perms = new HashMap<>();
|
|
||||||
|
|
||||||
permissions.forEach(permission -> {
|
|
||||||
Permission evalPermission = perms.get(permission.getResourceSetId());
|
|
||||||
|
|
||||||
if (evalPermission == null) {
|
|
||||||
evalPermission = permission;
|
|
||||||
perms.put(permission.getResourceSetId(), evalPermission);
|
|
||||||
}
|
}
|
||||||
|
Resource resource = permission.getResource();
|
||||||
|
|
||||||
Set<String> permissionScopes = permission.getScopes();
|
if (resource != null) {
|
||||||
|
String resourceId = resource.getId();
|
||||||
|
String resourceName = resource.getName();
|
||||||
|
Permission evalPermission = permissions.get(resource.getId());
|
||||||
|
|
||||||
if (permissionScopes != null && !permissionScopes.isEmpty()) {
|
if (evalPermission == null) {
|
||||||
Set<String> scopes = evalPermission.getScopes();
|
evalPermission = new Permission(resourceId, resourceName, scopes);
|
||||||
|
permissions.put(resourceId, evalPermission);
|
||||||
if (scopes == null) {
|
|
||||||
scopes = new HashSet();
|
|
||||||
evalPermission.setScopes(scopes);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (String scopeName : permissionScopes) {
|
if (scopes != null && !scopes.isEmpty()) {
|
||||||
if (!scopes.contains(scopeName)) {
|
Set<String> finalScopes = evalPermission.getScopes();
|
||||||
scopes.add(scopeName);
|
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
return perms.values().stream().collect(Collectors.toList());
|
return permissions.values().stream().collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -109,7 +109,12 @@ public class AlbumService {
|
||||||
|
|
||||||
private void createProtectedResource(Album album) {
|
private void createProtectedResource(Album album) {
|
||||||
try {
|
try {
|
||||||
ResourceRepresentation albumResource = new ResourceRepresentation(album.getName(), new HashSet(), "/album/" + album.getId(), "http://photoz.com/album");
|
HashSet<ScopeRepresentation> scopes = new HashSet<>();
|
||||||
|
|
||||||
|
scopes.add(new ScopeRepresentation(SCOPE_ALBUM_VIEW));
|
||||||
|
scopes.add(new ScopeRepresentation(SCOPE_ALBUM_DELETE));
|
||||||
|
|
||||||
|
ResourceRepresentation albumResource = new ResourceRepresentation(album.getName(), scopes, "/album/" + album.getId(), "http://photoz.com/album");
|
||||||
|
|
||||||
albumResource.setOwner(album.getUserId());
|
albumResource.setOwner(album.getUserId());
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"realm": "photoz",
|
"realm": "photoz",
|
||||||
"realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
|
"realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
|
||||||
"auth-server-url": "http://localhost:8080/auth",
|
"auth-server-url": "http://localhost:8180/auth",
|
||||||
"ssl-required": "external",
|
"ssl-required": "external",
|
||||||
"resource": "photoz-restful-api",
|
"resource": "photoz-restful-api",
|
||||||
"bearer-only" : true,
|
"bearer-only" : true,
|
||||||
|
@ -9,17 +9,18 @@
|
||||||
"secret": "secret"
|
"secret": "secret"
|
||||||
},
|
},
|
||||||
"policy-enforcer": {
|
"policy-enforcer": {
|
||||||
|
"user-managed-access" : {},
|
||||||
"paths": [
|
"paths": [
|
||||||
{
|
{
|
||||||
"path" : "/album/*",
|
"path" : "/album/*",
|
||||||
"methods" : [
|
"methods" : [
|
||||||
{
|
|
||||||
"method": "GET",
|
|
||||||
"scopes" : ["urn:photoz.com:scopes:album:view"]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
"scopes" : ["urn:photoz.com:scopes:album:create"]
|
"scopes" : ["urn:photoz.com:scopes:album:create"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "GET",
|
||||||
|
"scopes" : ["urn:photoz.com:scopes:album:view"]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -30,6 +31,10 @@
|
||||||
{
|
{
|
||||||
"method": "DELETE",
|
"method": "DELETE",
|
||||||
"scopes" : ["urn:photoz.com:scopes:album:delete"]
|
"scopes" : ["urn:photoz.com:scopes:album:delete"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "GET",
|
||||||
|
"scopes" : ["urn:photoz.com:scopes:album:view"]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
|
@ -94,6 +94,7 @@ public abstract class AbstractPhotozExampleAdapterTest extends AbstractExampleAd
|
||||||
importResourceServerSettings();
|
importResourceServerSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testCreateDeleteAlbum() throws Exception {
|
public void testCreateDeleteAlbum() throws Exception {
|
||||||
try {
|
try {
|
||||||
this.deployer.deploy(RESOURCE_SERVER_ID);
|
this.deployer.deploy(RESOURCE_SERVER_ID);
|
||||||
|
@ -115,6 +116,7 @@ public abstract class AbstractPhotozExampleAdapterTest extends AbstractExampleAd
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testOnlyOwnerCanDeleteAlbum() throws Exception {
|
public void testOnlyOwnerCanDeleteAlbum() throws Exception {
|
||||||
try {
|
try {
|
||||||
this.deployer.deploy(RESOURCE_SERVER_ID);
|
this.deployer.deploy(RESOURCE_SERVER_ID);
|
||||||
|
@ -160,6 +162,7 @@ public abstract class AbstractPhotozExampleAdapterTest extends AbstractExampleAd
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testRegularUserCanNotAccessAdminResources() throws Exception {
|
public void testRegularUserCanNotAccessAdminResources() throws Exception {
|
||||||
try {
|
try {
|
||||||
this.deployer.deploy(RESOURCE_SERVER_ID);
|
this.deployer.deploy(RESOURCE_SERVER_ID);
|
||||||
|
@ -172,6 +175,7 @@ public abstract class AbstractPhotozExampleAdapterTest extends AbstractExampleAd
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testAdminOnlyFromSpecificAddress() throws Exception {
|
public void testAdminOnlyFromSpecificAddress() throws Exception {
|
||||||
try {
|
try {
|
||||||
this.deployer.deploy(RESOURCE_SERVER_ID);
|
this.deployer.deploy(RESOURCE_SERVER_ID);
|
||||||
|
@ -264,6 +268,7 @@ public abstract class AbstractPhotozExampleAdapterTest extends AbstractExampleAd
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testAdminWithoutPermissionsToDeleteScopePermission() throws Exception {
|
public void testAdminWithoutPermissionsToDeleteScopePermission() throws Exception {
|
||||||
try {
|
try {
|
||||||
this.deployer.deploy(RESOURCE_SERVER_ID);
|
this.deployer.deploy(RESOURCE_SERVER_ID);
|
||||||
|
@ -327,6 +332,7 @@ public abstract class AbstractPhotozExampleAdapterTest extends AbstractExampleAd
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testClientRoleRepresentingUserConsent() throws Exception {
|
public void testClientRoleRepresentingUserConsent() throws Exception {
|
||||||
try {
|
try {
|
||||||
this.deployer.deploy(RESOURCE_SERVER_ID);
|
this.deployer.deploy(RESOURCE_SERVER_ID);
|
||||||
|
@ -366,6 +372,7 @@ public abstract class AbstractPhotozExampleAdapterTest extends AbstractExampleAd
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testClientRoleNotRequired() throws Exception {
|
public void testClientRoleNotRequired() throws Exception {
|
||||||
try {
|
try {
|
||||||
this.deployer.deploy(RESOURCE_SERVER_ID);
|
this.deployer.deploy(RESOURCE_SERVER_ID);
|
||||||
|
|
Loading…
Reference in a new issue