[KEYCLOAK-3338] - Adding client roles to role policy and UX improvements
This commit is contained in:
parent
3973aed57d
commit
7e1b97888a
43 changed files with 830 additions and 417 deletions
|
@ -23,9 +23,12 @@ import org.keycloak.authorization.model.Policy;
|
||||||
import org.keycloak.authorization.policy.evaluation.Evaluation;
|
import org.keycloak.authorization.policy.evaluation.Evaluation;
|
||||||
import org.keycloak.authorization.policy.evaluation.EvaluationContext;
|
import org.keycloak.authorization.policy.evaluation.EvaluationContext;
|
||||||
import org.keycloak.authorization.policy.provider.PolicyProvider;
|
import org.keycloak.authorization.policy.provider.PolicyProvider;
|
||||||
|
import org.keycloak.models.ClientModel;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.RoleModel;
|
import org.keycloak.models.RoleModel;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import static org.keycloak.authorization.policy.provider.role.RolePolicyProviderFactory.getRoles;
|
import static org.keycloak.authorization.policy.provider.role.RolePolicyProviderFactory.getRoles;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -47,23 +50,41 @@ public class RolePolicyProvider implements PolicyProvider {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void evaluate(Evaluation evaluation) {
|
public void evaluate(Evaluation evaluation) {
|
||||||
EvaluationContext context = evaluation.getContext();
|
Map<String, Object>[] roleIds = getRoles(this.policy);
|
||||||
String[] roleIds = getRoles(this.policy);
|
|
||||||
|
|
||||||
if (roleIds.length > 0) {
|
if (roleIds.length > 0) {
|
||||||
Identity identity = context.getIdentity();
|
Identity identity = evaluation.getContext().getIdentity();
|
||||||
|
|
||||||
for (String roleId : roleIds) {
|
for (Map<String, Object> current : roleIds) {
|
||||||
RoleModel role = getCurrentRealm().getRoleById(roleId);
|
RoleModel role = getCurrentRealm().getRoleById((String) current.get("id"));
|
||||||
|
|
||||||
if (role != null && identity.hasRole(role.getName())) {
|
if (role != null) {
|
||||||
evaluation.grant();
|
boolean hasRole = hasRole(identity, role);
|
||||||
break;
|
|
||||||
|
if (!hasRole && Boolean.valueOf(isRequired(current))) {
|
||||||
|
evaluation.deny();
|
||||||
|
return;
|
||||||
|
} else if (hasRole) {
|
||||||
|
evaluation.grant();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isRequired(Map<String, Object> current) {
|
||||||
|
return (boolean) current.getOrDefault("required", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasRole(Identity identity, RoleModel role) {
|
||||||
|
String roleName = role.getName();
|
||||||
|
if (role.isClientRole()) {
|
||||||
|
ClientModel clientModel = getCurrentRealm().getClientById(role.getContainerId());
|
||||||
|
return identity.hasClientRole(clientModel.getClientId(), roleName);
|
||||||
|
}
|
||||||
|
return identity.hasRealmRole(roleName);
|
||||||
|
}
|
||||||
|
|
||||||
private RealmModel getCurrentRealm() {
|
private RealmModel getCurrentRealm() {
|
||||||
return this.authorization.getKeycloakSession().getContext().getRealm();
|
return this.authorization.getKeycloakSession().getContext().getRealm();
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,9 @@ import org.keycloak.util.JsonSerialization;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||||
|
@ -81,11 +83,17 @@ public class RolePolicyProviderFactory implements PolicyProviderFactory {
|
||||||
RoleModel removedRole = ((RoleRemovedEvent) event).getRole();
|
RoleModel removedRole = ((RoleRemovedEvent) event).getRole();
|
||||||
|
|
||||||
policyStore.findByType(getId()).forEach(policy -> {
|
policyStore.findByType(getId()).forEach(policy -> {
|
||||||
List<String> roles = new ArrayList<>();
|
List<Map> roles = new ArrayList<>();
|
||||||
|
|
||||||
for (String roleId : getRoles(policy)) {
|
for (Map<String,Object> role : getRoles(policy)) {
|
||||||
if (!roleId.equals(removedRole.getId())) {
|
if (!role.get("id").equals(removedRole.getId())) {
|
||||||
roles.add(roleId);
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,7 +104,9 @@ public class RolePolicyProviderFactory implements PolicyProviderFactory {
|
||||||
});
|
});
|
||||||
policyStore.delete(policy.getId());
|
policyStore.delete(policy.getId());
|
||||||
} else {
|
} else {
|
||||||
policy.getConfig().put("roles", JsonSerialization.writeValueAsString(roles));
|
Map<String, String> config = policy.getConfig();
|
||||||
|
config.put("roles", JsonSerialization.writeValueAsString(roles));
|
||||||
|
policy.setConfig(config);
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new RuntimeException("Error while synchronizing roles with policy [" + policy.getName() + "].", e);
|
throw new RuntimeException("Error while synchronizing roles with policy [" + policy.getName() + "].", e);
|
||||||
|
@ -116,17 +126,17 @@ public class RolePolicyProviderFactory implements PolicyProviderFactory {
|
||||||
return "role";
|
return "role";
|
||||||
}
|
}
|
||||||
|
|
||||||
static String[] getRoles(Policy policy) {
|
static Map<String, Object>[] getRoles(Policy policy) {
|
||||||
String roles = policy.getConfig().get("roles");
|
String roles = policy.getConfig().get("roles");
|
||||||
|
|
||||||
if (roles != null) {
|
if (roles != null) {
|
||||||
try {
|
try {
|
||||||
return JsonSerialization.readValue(roles.getBytes(), String[].class);
|
return JsonSerialization.readValue(roles.getBytes(), Map[].class);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new RuntimeException("Could not parse roles [" + roles + "] from policy config [" + policy.getName() + ".", e);
|
throw new RuntimeException("Could not parse roles [" + roles + "] from policy config [" + policy.getName() + ".", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new String[]{};
|
return new Map[] {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,8 @@ public class RoleRepresentation {
|
||||||
protected Boolean scopeParamRequired;
|
protected Boolean scopeParamRequired;
|
||||||
protected boolean composite;
|
protected boolean composite;
|
||||||
protected Composites composites;
|
protected Composites composites;
|
||||||
|
private Boolean clientRole;
|
||||||
|
private String containerId;
|
||||||
|
|
||||||
public static class Composites {
|
public static class Composites {
|
||||||
protected Set<String> realm;
|
protected Set<String> realm;
|
||||||
|
@ -122,4 +124,20 @@ public class RoleRepresentation {
|
||||||
public void setComposite(boolean composite) {
|
public void setComposite(boolean composite) {
|
||||||
this.composite = composite;
|
this.composite = composite;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Boolean getClientRole() {
|
||||||
|
return clientRole;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setClientRole(Boolean clientRole) {
|
||||||
|
this.clientRole = clientRole;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getContainerId() {
|
||||||
|
return containerId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setContainerId(String containerId) {
|
||||||
|
this.containerId = containerId;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
*/
|
*/
|
||||||
package org.keycloak.representations.idm.authorization;
|
package org.keycloak.representations.idm.authorization;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
|
@ -39,12 +40,14 @@ public class ResourceRepresentation {
|
||||||
private String name;
|
private String name;
|
||||||
private String uri;
|
private String uri;
|
||||||
private String type;
|
private String type;
|
||||||
|
@JsonInclude(JsonInclude.Include.NON_EMPTY)
|
||||||
private Set<ScopeRepresentation> scopes;
|
private Set<ScopeRepresentation> scopes;
|
||||||
|
|
||||||
@JsonProperty("icon_uri")
|
@JsonProperty("icon_uri")
|
||||||
private String iconUri;
|
private String iconUri;
|
||||||
private ResourceOwnerRepresentation owner;
|
private ResourceOwnerRepresentation owner;
|
||||||
|
|
||||||
|
@JsonInclude(JsonInclude.Include.NON_EMPTY)
|
||||||
private List<PolicyRepresentation> policies;
|
private List<PolicyRepresentation> policies;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -34,6 +34,7 @@ public class ScopeRepresentation {
|
||||||
private String name;
|
private String name;
|
||||||
private String iconUri;
|
private String iconUri;
|
||||||
private List<PolicyRepresentation> policies;
|
private List<PolicyRepresentation> policies;
|
||||||
|
private List<ResourceRepresentation> resources;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an instance.
|
* Creates an instance.
|
||||||
|
@ -94,6 +95,14 @@ public class ScopeRepresentation {
|
||||||
this.policies = policies;
|
this.policies = policies;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<ResourceRepresentation> getResources() {
|
||||||
|
return this.resources;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setResources(List<ResourceRepresentation> resources) {
|
||||||
|
this.resources = resources;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
if (this == o) return true;
|
if (this == o) return true;
|
||||||
if (o == null || getClass() != o.getClass()) return false;
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
{
|
{
|
||||||
|
"allowRemoteResourceManagement": false,
|
||||||
|
"policyEnforcementMode": "ENFORCING",
|
||||||
"resources": [
|
"resources": [
|
||||||
{
|
{
|
||||||
"name": "Default Resource",
|
"name": "Default Resource",
|
||||||
|
@ -8,22 +10,26 @@
|
||||||
],
|
],
|
||||||
"policies": [
|
"policies": [
|
||||||
{
|
{
|
||||||
"name": "Only From Realm Policy",
|
"name": "Default Policy",
|
||||||
"description": "A policy that grants access only for users within this realm",
|
"description": "A policy that grants access only for users within this realm",
|
||||||
"type": "js",
|
"type": "js",
|
||||||
|
"logic": "POSITIVE",
|
||||||
|
"decisionStrategy": "AFFIRMATIVE",
|
||||||
"config": {
|
"config": {
|
||||||
"applyPolicies": "[]",
|
"applyPolicies": "[]",
|
||||||
"code": "var context = $evaluation.getContext();\n\n// using attributes from the evaluation context to obtain the realm\nvar contextAttributes = context.getAttributes();\nvar realmName = contextAttributes.getValue('kc.realm.name').asString(0);\n\n// using attributes from the identity to obtain the issuer\nvar identity = context.getIdentity();\nvar identityAttributes = identity.getAttributes();\nvar issuer = identityAttributes.getValue('iss').asString(0);\n\n// only users from the realm have access granted \nif (issuer.endsWith(realmName)) {\n $evaluation.grant();\n}"
|
"code": "// by default, grants any permission associated with this policy\n$evaluation.grant();\n"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Default Permission",
|
"name": "Default Permission",
|
||||||
"description": "A permission that applies to the default resource type",
|
"description": "A permission that applies to the default resource type",
|
||||||
"type": "resource",
|
"type": "resource",
|
||||||
|
"logic": "POSITIVE",
|
||||||
|
"decisionStrategy": "UNANIMOUS",
|
||||||
"config": {
|
"config": {
|
||||||
"defaultResourceType": "urn:hello-world-authz-service:resources:default",
|
"defaultResourceType": "urn:hello-world-authz-service:resources:default",
|
||||||
"default": "true",
|
"default": "true",
|
||||||
"applyPolicies": "[\"Only From Realm Policy\"]"
|
"applyPolicies": "[\"Default Policy\"]"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
|
|
||||||
<body data-ng-controller="TokenCtrl">
|
<body data-ng-controller="TokenCtrl">
|
||||||
|
|
||||||
<a href data-ng-click="showRpt()">Show Requesting Party Token </a> | <a href data-ng-click="showAccessToken()">Show Access Token </a> | <a href data-ng-click="requestEntitlements()">Request Entitlements</a>
|
<a href data-ng-click="showRpt()">Show Requesting Party Token </a> | <a href data-ng-click="showAccessToken()">Show Access Token </a> | <a href data-ng-click="requestEntitlements()">Request Entitlements</a> | <a href="" ng-click="Identity.logout()">Sign Out</a>
|
||||||
|
|
||||||
<div id="content-area" class="col-md-9" role="main">
|
<div id="content-area" class="col-md-9" role="main">
|
||||||
<div id="content" ng-view/>
|
<div id="content" ng-view/>
|
||||||
|
|
|
@ -64,6 +64,8 @@ module.controller('TokenCtrl', function ($scope, Identity) {
|
||||||
$scope.requestEntitlements = function () {
|
$scope.requestEntitlements = function () {
|
||||||
Identity.authorization.entitlement('photoz-restful-api').then(function (rpt) {});
|
Identity.authorization.entitlement('photoz-restful-api').then(function (rpt) {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$scope.Identity = Identity;
|
||||||
});
|
});
|
||||||
|
|
||||||
module.controller('AlbumCtrl', function ($scope, $http, $routeParams, $location, Album) {
|
module.controller('AlbumCtrl', function ($scope, $http, $routeParams, $location, Album) {
|
||||||
|
@ -83,14 +85,13 @@ module.controller('ProfileCtrl', function ($scope, $http, $routeParams, $locatio
|
||||||
$scope.profile = Profile.get();
|
$scope.profile = Profile.get();
|
||||||
});
|
});
|
||||||
|
|
||||||
module.controller('AdminAlbumCtrl', function ($scope, $http, $route, AdminAlbum, Album) {
|
module.controller('AdminAlbumCtrl', function ($scope, $http, $route, $location, AdminAlbum, Album) {
|
||||||
$scope.albums = {};
|
$scope.albums = {};
|
||||||
$http.get(apiUrl + '/admin/album').success(function (data) {
|
$http.get(apiUrl + '/admin/album').success(function (data) {
|
||||||
$scope.albums = data;
|
$scope.albums = data;
|
||||||
});
|
});
|
||||||
$scope.deleteAlbum = function (album) {
|
$scope.deleteAlbum = function (album) {
|
||||||
var newAlbum = new Album(album);
|
new Album(album).$delete({id: album.id}, function () {
|
||||||
newAlbum.$delete({id: album.id}, function () {
|
|
||||||
$route.reload();
|
$route.reload();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
<td>
|
<td>
|
||||||
<ul>
|
<ul>
|
||||||
<li data-ng-repeat="p in value">
|
<li data-ng-repeat="p in value">
|
||||||
<a href="#/album/{{p.id}}">{{p.name}}</a> - [<a href="#" id="delete-{{p.name}}" ng-click="deleteAlbum(p)">X</a>]
|
<a id="view-{{p.name}}" href="#/album/{{p.id}}">{{p.name}}</a> - [<a href="#/admin/album" id="delete-{{p.name}}" ng-click="deleteAlbum(p)">X</a>]
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</td>
|
</td>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<h2><span>Welcome To Photoz, {{Identity.claims.name}}</span> [<a href="" ng-click="Identity.logout()">Sign Out</a>]</h2>
|
<h2><span>Welcome To Photoz, {{Identity.claims.name}}</span></h2>
|
||||||
<div data-ng-show="Identity.isAdmin()"><b>Administration: </b> [<a href="#/admin/album" id="admin-albums">All Albums</a>]</div>
|
<div data-ng-show="Identity.isAdmin()"><b>Administration: </b> [<a href="#/admin/album" id="admin-albums">All Albums</a>]</div>
|
||||||
<hr/>
|
<hr/>
|
||||||
<br/>
|
<br/>
|
||||||
|
@ -15,7 +15,7 @@
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr data-ng-repeat="p in albums">
|
<tr data-ng-repeat="p in albums">
|
||||||
<td><a href="#/album/{{p.id}}">{{p.name}}</a> - [<a href="#" id="delete-{{p.name}}" ng-click="deleteAlbum(p)">X</a>]</td>
|
<td><a id="view-{{p.name}}" href="#/album/{{p.id}}">{{p.name}}</a> - [<a href="#" id="delete-{{p.name}}" ng-click="deleteAlbum(p)">X</a>]</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
|
@ -53,7 +53,7 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"realmRoles": [
|
"realmRoles": [
|
||||||
"user", "admin", "uma_authorization"
|
"admin", "uma_authorization"
|
||||||
],
|
],
|
||||||
"clientRoles": {
|
"clientRoles": {
|
||||||
"realm-management": [
|
"realm-management": [
|
||||||
|
|
|
@ -21,10 +21,10 @@
|
||||||
"name": "urn:photoz.com:scopes:album:view"
|
"name": "urn:photoz.com:scopes:album:view"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "urn:photoz.com:scopes:album:create"
|
"name": "urn:photoz.com:scopes:album:delete"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "urn:photoz.com:scopes:album:delete"
|
"name": "urn:photoz.com:scopes:album:create"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -44,12 +44,15 @@
|
||||||
"name": "Only Owner Policy",
|
"name": "Only Owner Policy",
|
||||||
"description": "Defines that only the resource owner is allowed to do something",
|
"description": "Defines that only the resource owner is allowed to do something",
|
||||||
"type": "drools",
|
"type": "drools",
|
||||||
|
"logic": "POSITIVE",
|
||||||
|
"decisionStrategy": "UNANIMOUS",
|
||||||
"config": {
|
"config": {
|
||||||
"mavenArtifactVersion": "2.1.0-SNAPSHOT",
|
"mavenArtifactVersion": "2.1.0-SNAPSHOT",
|
||||||
"mavenArtifactId": "photoz-authz-policy",
|
"mavenArtifactId": "photoz-authz-policy",
|
||||||
"sessionName": "MainOwnerSession",
|
"sessionName": "MainOwnerSession",
|
||||||
"mavenArtifactGroupId": "org.keycloak",
|
"mavenArtifactGroupId": "org.keycloak",
|
||||||
"moduleName": "PhotozAuthzOwnerPolicy",
|
"moduleName": "PhotozAuthzOwnerPolicy",
|
||||||
|
"applyPolicies": "[]",
|
||||||
"scannerPeriod": "1",
|
"scannerPeriod": "1",
|
||||||
"scannerPeriodUnit": "Hours"
|
"scannerPeriodUnit": "Hours"
|
||||||
}
|
}
|
||||||
|
@ -58,16 +61,22 @@
|
||||||
"name": "Any Admin Policy",
|
"name": "Any Admin Policy",
|
||||||
"description": "Defines that adminsitrators can do something",
|
"description": "Defines that adminsitrators can do something",
|
||||||
"type": "role",
|
"type": "role",
|
||||||
|
"logic": "POSITIVE",
|
||||||
|
"decisionStrategy": "UNANIMOUS",
|
||||||
"config": {
|
"config": {
|
||||||
"roles": "[\"admin\"]"
|
"applyPolicies": "[]",
|
||||||
|
"roles": "[{\"id\":\"admin\",\"required\":true}]"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Any User Policy",
|
"name": "Any User Policy",
|
||||||
"description": "Defines that any user can do something",
|
"description": "Defines that any user can do something",
|
||||||
"type": "role",
|
"type": "role",
|
||||||
|
"logic": "POSITIVE",
|
||||||
|
"decisionStrategy": "UNANIMOUS",
|
||||||
"config": {
|
"config": {
|
||||||
"roles": "[\"user\"]"
|
"applyPolicies": "[]",
|
||||||
|
"roles": "[{\"id\":\"user\"}]"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -77,6 +86,7 @@
|
||||||
"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}"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -84,6 +94,8 @@
|
||||||
"name": "Administration Policy",
|
"name": "Administration Policy",
|
||||||
"description": "Defines that only administrators from a specific network address can do something.",
|
"description": "Defines that only administrators from a specific network address can do something.",
|
||||||
"type": "aggregate",
|
"type": "aggregate",
|
||||||
|
"logic": "POSITIVE",
|
||||||
|
"decisionStrategy": "UNANIMOUS",
|
||||||
"config": {
|
"config": {
|
||||||
"applyPolicies": "[\"Any Admin Policy\",\"Only From a Specific Client Address\"]"
|
"applyPolicies": "[\"Any Admin Policy\",\"Only From a Specific Client Address\"]"
|
||||||
}
|
}
|
||||||
|
@ -92,35 +104,28 @@
|
||||||
"name": "Only Owner and Administrators Policy",
|
"name": "Only Owner and Administrators Policy",
|
||||||
"description": "Defines that only the resource owner and administrators can do something",
|
"description": "Defines that only the resource owner and administrators can do something",
|
||||||
"type": "aggregate",
|
"type": "aggregate",
|
||||||
|
"logic": "POSITIVE",
|
||||||
"decisionStrategy": "AFFIRMATIVE",
|
"decisionStrategy": "AFFIRMATIVE",
|
||||||
"config": {
|
"config": {
|
||||||
"applyPolicies": "[\"Administration Policy\",\"Only Owner Policy\"]"
|
"applyPolicies": "[\"Only Owner Policy\",\"Administration Policy\"]"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Only From @keycloak.org or Admin",
|
"name": "Only From @keycloak.org or Admin",
|
||||||
"description": "Defines that only users from @keycloak.org",
|
"description": "Defines that only users from @keycloak.org",
|
||||||
"type": "js",
|
"type": "js",
|
||||||
|
"logic": "POSITIVE",
|
||||||
|
"decisionStrategy": "UNANIMOUS",
|
||||||
"config": {
|
"config": {
|
||||||
"applyPolicies": "[]",
|
"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}"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "Only in the Period",
|
|
||||||
"description": "Access granted only during the morning",
|
|
||||||
"type": "time",
|
|
||||||
"config": {
|
|
||||||
"noa": "2016-01-03 23:59:59",
|
|
||||||
"expirationUnit": "Minutes",
|
|
||||||
"nbf": "2016-01-01 00:00:00",
|
|
||||||
"expirationTime": "1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "Album Resource Permission",
|
"name": "Album Resource Permission",
|
||||||
"description": "General policies that apply to all album resources.",
|
"description": "General policies that apply to all album resources.",
|
||||||
"type": "resource",
|
"type": "resource",
|
||||||
|
"logic": "POSITIVE",
|
||||||
"decisionStrategy": "AFFIRMATIVE",
|
"decisionStrategy": "AFFIRMATIVE",
|
||||||
"config": {
|
"config": {
|
||||||
"defaultResourceType": "http://photoz.com/album",
|
"defaultResourceType": "http://photoz.com/album",
|
||||||
|
|
|
@ -109,13 +109,7 @@ public class AlbumService {
|
||||||
|
|
||||||
private void createProtectedResource(Album album) {
|
private void createProtectedResource(Album album) {
|
||||||
try {
|
try {
|
||||||
HashSet<ScopeRepresentation> scopes = new HashSet<>();
|
ResourceRepresentation albumResource = new ResourceRepresentation(album.getName(), new HashSet(), "/album/" + album.getId(), "http://photoz.com/album");
|
||||||
|
|
||||||
scopes.add(new ScopeRepresentation(SCOPE_ALBUM_VIEW));
|
|
||||||
scopes.add(new ScopeRepresentation(SCOPE_ALBUM_CREATE));
|
|
||||||
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());
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,7 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||||
|
@ -99,7 +100,7 @@ public class CachedPolicyStore implements PolicyStore {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Policy> findByResourceServer(String resourceServerId) {
|
public List<Policy> findByResourceServer(String resourceServerId) {
|
||||||
return getDelegate().findByResourceServer(resourceServerId);
|
return getDelegate().findByResourceServer(resourceServerId).stream().map(policy -> findById(policy.getId())).collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -179,12 +180,12 @@ public class CachedPolicyStore implements PolicyStore {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Policy> findByType(String type) {
|
public List<Policy> findByType(String type) {
|
||||||
return getDelegate().findByType(type);
|
return getDelegate().findByType(type).stream().map(policy -> findById(policy.getId())).collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Policy> findDependentPolicies(String id) {
|
public List<Policy> findDependentPolicies(String id) {
|
||||||
return getDelegate().findDependentPolicies(id);
|
return getDelegate().findDependentPolicies(id).stream().map(policy -> findById(policy.getId())).collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getCacheKeyForPolicy(String policyId) {
|
private String getCacheKeyForPolicy(String policyId) {
|
||||||
|
|
|
@ -68,7 +68,21 @@ public class CachedResourceStore implements ResourceStore {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void delete(String id) {
|
public void delete(String id) {
|
||||||
this.cache.remove(getCacheKeyForResource(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()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
getDelegate().delete(id);
|
getDelegate().delete(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,12 +123,12 @@ public class CachedResourceStore implements ResourceStore {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Resource> findByResourceServer(String resourceServerId) {
|
public List<Resource> findByResourceServer(String resourceServerId) {
|
||||||
return getDelegate().findByResourceServer(resourceServerId);
|
return getDelegate().findByResourceServer(resourceServerId).stream().map(resource -> findById(resource.getId())).collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Resource> findByScope(String... id) {
|
public List<Resource> findByScope(String... id) {
|
||||||
return getDelegate().findByScope(id);
|
return getDelegate().findByScope(id).stream().map(resource -> findById(resource.getId())).collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -283,7 +297,7 @@ public class CachedResourceStore implements ResourceStore {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
cached = new ArrayList<>();
|
cached = new ArrayList<>();
|
||||||
this.cache.put(getResourceOwnerCacheKey(resource.getOwner()), cached);
|
this.cache.put(cacheKey, cached);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cached != null && !cached.contains(resource.getId())) {
|
if (cached != null && !cached.contains(resource.getId())) {
|
||||||
|
|
|
@ -98,7 +98,7 @@ public class JPAResourceStore implements ResourceStore {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Resource> findByScope(String... id) {
|
public List<Resource> findByScope(String... id) {
|
||||||
Query query = entityManager.createQuery("from ResourceEntity r inner join r.scopes s where s.id in (:scopeIds)");
|
Query query = entityManager.createQuery("select r from ResourceEntity r inner join r.scopes s where s.id in (:scopeIds)");
|
||||||
|
|
||||||
query.setParameter("scopeIds", Arrays.asList(id));
|
query.setParameter("scopeIds", Arrays.asList(id));
|
||||||
|
|
||||||
|
|
|
@ -95,7 +95,7 @@ public interface Attributes {
|
||||||
private final String[] values;
|
private final String[] values;
|
||||||
private final String name;
|
private final String name;
|
||||||
|
|
||||||
Entry(String name, Collection<String> values) {
|
public Entry(String name, Collection<String> values) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.values = values.toArray(new String[values.size()]);
|
this.values = values.toArray(new String[values.size()]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,10 @@ package org.keycloak.authorization.identity;
|
||||||
|
|
||||||
import org.keycloak.authorization.attribute.Attributes;
|
import org.keycloak.authorization.attribute.Attributes;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Represents a security identity, which can be a person or non-person entity that was previously authenticated.
|
* <p>Represents a security identity, which can be a person or non-person entity that was previously authenticated.
|
||||||
*
|
*
|
||||||
|
@ -45,13 +49,53 @@ public interface Identity {
|
||||||
Attributes getAttributes();
|
Attributes getAttributes();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates if this identity is granted with a role with the given <code>roleName</code>.
|
* Indicates if this identity is granted with a role (realm or client) with the given <code>roleName</code>.
|
||||||
*
|
*
|
||||||
* @param roleName the name of the role
|
* @param roleName the name of the role
|
||||||
*
|
*
|
||||||
* @return true if the identity has the given role. Otherwise, it returns false.
|
* @return true if the identity has the given role. Otherwise, it returns false.
|
||||||
*/
|
*/
|
||||||
default boolean hasRole(String roleName) {
|
default boolean hasRole(String roleName) {
|
||||||
return getAttributes().containsValue("roles", roleName);
|
return hasRealmRole(roleName) || hasClientRole(roleName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if this identity is granted with a realm role with the given <code>roleName</code>.
|
||||||
|
*
|
||||||
|
* @param roleName the name of the role
|
||||||
|
*
|
||||||
|
* @return true if the identity has the given role. Otherwise, it returns false.
|
||||||
|
*/
|
||||||
|
default boolean hasRealmRole(String roleName) {
|
||||||
|
return getAttributes().containsValue("kc.realm.roles", roleName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if this identity is granted with a client role with the given <code>roleName</code>.
|
||||||
|
*
|
||||||
|
* @param clientId the client id
|
||||||
|
* @param roleName the name of the role
|
||||||
|
*
|
||||||
|
* @return true if the identity has the given role. Otherwise, it returns false.
|
||||||
|
*/
|
||||||
|
default boolean hasClientRole(String clientId, String roleName) {
|
||||||
|
return getAttributes().containsValue("kc.client." + clientId + ".roles", roleName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if this identity is granted with a client role with the given <code>roleName</code>.
|
||||||
|
*
|
||||||
|
* @param roleName the name of the role
|
||||||
|
*
|
||||||
|
* @return true if the identity has the given role. Otherwise, it returns false.
|
||||||
|
*/
|
||||||
|
default boolean hasClientRole(String roleName) {
|
||||||
|
return getAttributes().toMap().entrySet().stream().filter(entry -> {
|
||||||
|
String key = entry.getKey();
|
||||||
|
if (key.startsWith("kc.client") && key.endsWith(".roles")) {
|
||||||
|
return getAttributes().containsValue(key, roleName);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}).findFirst().isPresent();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,11 +17,22 @@
|
||||||
|
|
||||||
package org.keycloak.migration.migrators;
|
package org.keycloak.migration.migrators;
|
||||||
|
|
||||||
|
import org.keycloak.authorization.AuthorizationProvider;
|
||||||
|
import org.keycloak.authorization.model.ResourceServer;
|
||||||
|
import org.keycloak.authorization.store.PolicyStore;
|
||||||
|
import org.keycloak.authorization.store.StoreFactory;
|
||||||
import org.keycloak.migration.ModelVersion;
|
import org.keycloak.migration.ModelVersion;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.RequiredActionProviderModel;
|
import org.keycloak.models.RequiredActionProviderModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
|
import org.keycloak.util.JsonSerialization;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
@ -33,6 +44,7 @@ public class MigrateTo2_1_0 {
|
||||||
public void migrate(KeycloakSession session) {
|
public void migrate(KeycloakSession session) {
|
||||||
for (RealmModel realm : session.realms().getRealms()) {
|
for (RealmModel realm : session.realms().getRealms()) {
|
||||||
migrateDefaultRequiredAction(realm);
|
migrateDefaultRequiredAction(realm);
|
||||||
|
migrateRolePolicies(realm, session);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,4 +58,46 @@ public class MigrateTo2_1_0 {
|
||||||
|
|
||||||
otpAction.setName("Configure OTP");
|
otpAction.setName("Configure OTP");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// KEYCLOAK-3338: Changes to how role policy config is stored"
|
||||||
|
private void migrateRolePolicies(RealmModel realm, KeycloakSession session) {
|
||||||
|
AuthorizationProvider authorizationProvider = session.getProvider(AuthorizationProvider.class);
|
||||||
|
StoreFactory storeFactory = authorizationProvider.getStoreFactory();
|
||||||
|
PolicyStore policyStore = storeFactory.getPolicyStore();
|
||||||
|
realm.getClients().forEach(clientModel -> {
|
||||||
|
ResourceServer resourceServer = storeFactory.getResourceServerStore().findByClient(clientModel.getId());
|
||||||
|
|
||||||
|
if (resourceServer != null) {
|
||||||
|
policyStore.findByType("role").forEach(policy -> {
|
||||||
|
Map<String, String> config = policy.getConfig();
|
||||||
|
String roles = config.get("roles");
|
||||||
|
List roleConfig;
|
||||||
|
|
||||||
|
try {
|
||||||
|
roleConfig = JsonSerialization.readValue(roles, List.class);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("Malformed configuration for role policy [" + policy.getName() + "].", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!roleConfig.isEmpty() && roleConfig.get(0) instanceof String) {
|
||||||
|
try {
|
||||||
|
config.put("roles", JsonSerialization.writeValueAsString(roleConfig.stream().map(new Function<String, Map>() {
|
||||||
|
@Override
|
||||||
|
public Map apply(String roleId) {
|
||||||
|
Map updated = new HashMap();
|
||||||
|
|
||||||
|
updated.put("id", roleId);
|
||||||
|
|
||||||
|
return updated;
|
||||||
|
}
|
||||||
|
}).collect(Collectors.toList())));
|
||||||
|
policy.setConfig(config);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("Failed to migrate role policy [" + policy.getName() + "].", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -218,6 +218,8 @@ public class ModelToRepresentation {
|
||||||
rep.setDescription(role.getDescription());
|
rep.setDescription(role.getDescription());
|
||||||
rep.setScopeParamRequired(role.isScopeParamRequired());
|
rep.setScopeParamRequired(role.isScopeParamRequired());
|
||||||
rep.setComposite(role.isComposite());
|
rep.setComposite(role.isComposite());
|
||||||
|
rep.setClientRole(role.isClientRole());
|
||||||
|
rep.setContainerId(role.getContainerId());
|
||||||
return rep;
|
return rep;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -136,11 +136,19 @@ public class PolicyEvaluationService {
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<ResourcePermission> createPermissions(PolicyEvaluationRequest representation, EvaluationContext evaluationContext, AuthorizationProvider authorization) {
|
private List<ResourcePermission> createPermissions(PolicyEvaluationRequest representation, EvaluationContext evaluationContext, AuthorizationProvider authorization) {
|
||||||
if (representation.isEntitlements()) {
|
List<PolicyEvaluationRequest.Resource> resources = representation.getResources();
|
||||||
|
|
||||||
|
for (PolicyEvaluationRequest.Resource resource : new ArrayList<>(resources)) {
|
||||||
|
if (resource.getId() == null && (resource.getScopes() == null || resource.getScopes().isEmpty())) {
|
||||||
|
resources.remove(resource);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (representation.isEntitlements() || resources.isEmpty()) {
|
||||||
return Permissions.all(this.resourceServer, evaluationContext.getIdentity(), authorization);
|
return Permissions.all(this.resourceServer, evaluationContext.getIdentity(), authorization);
|
||||||
}
|
}
|
||||||
|
|
||||||
return representation.getResources().stream().flatMap((Function<PolicyEvaluationRequest.Resource, Stream<ResourcePermission>>) resource -> {
|
return resources.stream().flatMap((Function<PolicyEvaluationRequest.Resource, Stream<ResourcePermission>>) resource -> {
|
||||||
Set<String> givenScopes = resource.getScopes();
|
Set<String> givenScopes = resource.getScopes();
|
||||||
|
|
||||||
if (givenScopes == null) {
|
if (givenScopes == null) {
|
||||||
|
@ -157,7 +165,13 @@ public class PolicyEvaluationService {
|
||||||
} else if (resource.getType() != null) {
|
} else if (resource.getType() != null) {
|
||||||
return storeFactory.getResourceStore().findByType(resource.getType()).stream().map(resource1 -> new ResourcePermission(resource1, scopes, resourceServer));
|
return storeFactory.getResourceStore().findByType(resource.getType()).stream().map(resource1 -> new ResourcePermission(resource1, scopes, resourceServer));
|
||||||
} else {
|
} else {
|
||||||
return scopes.stream().map(scope -> new ResourcePermission(null, asList(scope), resourceServer));
|
List<ResourcePermission> collect = scopes.stream().map(scope -> new ResourcePermission(null, asList(scope), resourceServer)).collect(Collectors.toList());
|
||||||
|
|
||||||
|
for (Scope scope : scopes) {
|
||||||
|
collect.addAll(storeFactory.getResourceStore().findByScope(scope.getId()).stream().map(resource12 -> new ResourcePermission(resource12, asList(scope), resourceServer)).collect(Collectors.toList()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return collect.stream();
|
||||||
}
|
}
|
||||||
}).collect(Collectors.toList());
|
}).collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
|
@ -154,7 +154,11 @@ public class PolicyService {
|
||||||
}
|
}
|
||||||
|
|
||||||
policyStore.findDependentPolicies(id).forEach(dependentPolicy -> {
|
policyStore.findDependentPolicies(id).forEach(dependentPolicy -> {
|
||||||
dependentPolicy.removeAssociatedPolicy(policy);
|
if (dependentPolicy.getAssociatedPolicies().size() == 1) {
|
||||||
|
policyStore.delete(dependentPolicy.getId());
|
||||||
|
} else {
|
||||||
|
dependentPolicy.removeAssociatedPolicy(policy);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
policyStore.delete(policy.getId());
|
policyStore.delete(policy.getId());
|
||||||
|
@ -271,7 +275,6 @@ public class PolicyService {
|
||||||
}
|
}
|
||||||
|
|
||||||
StoreFactory storeFactory = authorization.getStoreFactory();
|
StoreFactory storeFactory = authorization.getStoreFactory();
|
||||||
PolicyStore policyStore = storeFactory.getPolicyStore();
|
|
||||||
|
|
||||||
for (String scopeId : scopeIds) {
|
for (String scopeId : scopeIds) {
|
||||||
boolean hasScope = false;
|
boolean hasScope = false;
|
||||||
|
|
|
@ -17,8 +17,6 @@
|
||||||
*/
|
*/
|
||||||
package org.keycloak.authorization.admin;
|
package org.keycloak.authorization.admin;
|
||||||
|
|
||||||
import org.jboss.resteasy.plugins.providers.multipart.InputPart;
|
|
||||||
import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataInput;
|
|
||||||
import org.jboss.resteasy.spi.ResteasyProviderFactory;
|
import org.jboss.resteasy.spi.ResteasyProviderFactory;
|
||||||
import org.keycloak.authorization.AuthorizationProvider;
|
import org.keycloak.authorization.AuthorizationProvider;
|
||||||
import org.keycloak.authorization.admin.util.Models;
|
import org.keycloak.authorization.admin.util.Models;
|
||||||
|
@ -146,7 +144,11 @@ public class ResourceServerService {
|
||||||
.stream().map(resource -> {
|
.stream().map(resource -> {
|
||||||
ResourceRepresentation rep = Models.toRepresentation(resource, resourceServer, authorization);
|
ResourceRepresentation rep = Models.toRepresentation(resource, resourceServer, authorization);
|
||||||
|
|
||||||
rep.getOwner().setId(null);
|
if (rep.getOwner().getId().equals(this.resourceServer.getClientId())) {
|
||||||
|
rep.setOwner(null);
|
||||||
|
} else {
|
||||||
|
rep.getOwner().setId(null);
|
||||||
|
}
|
||||||
rep.setId(null);
|
rep.setId(null);
|
||||||
rep.setPolicies(null);
|
rep.setPolicies(null);
|
||||||
rep.getScopes().forEach(scopeRepresentation -> {
|
rep.getScopes().forEach(scopeRepresentation -> {
|
||||||
|
@ -175,15 +177,8 @@ public class ResourceServerService {
|
||||||
ScopeRepresentation rep = Models.toRepresentation(scope, authorization);
|
ScopeRepresentation rep = Models.toRepresentation(scope, authorization);
|
||||||
|
|
||||||
rep.setId(null);
|
rep.setId(null);
|
||||||
|
rep.setPolicies(null);
|
||||||
rep.getPolicies().forEach(policyRepresentation -> {
|
rep.setResources(null);
|
||||||
policyRepresentation.setId(null);
|
|
||||||
policyRepresentation.setConfig(null);
|
|
||||||
policyRepresentation.setType(null);
|
|
||||||
policyRepresentation.setDecisionStrategy(null);
|
|
||||||
policyRepresentation.setDescription(null);
|
|
||||||
policyRepresentation.setDependentPolicies(null);
|
|
||||||
});
|
|
||||||
|
|
||||||
return rep;
|
return rep;
|
||||||
}).collect(Collectors.toList());
|
}).collect(Collectors.toList());
|
||||||
|
@ -258,131 +253,74 @@ public class ResourceServerService {
|
||||||
String roles = config.get("roles");
|
String roles = config.get("roles");
|
||||||
|
|
||||||
if (roles != null && !roles.isEmpty()) {
|
if (roles != null && !roles.isEmpty()) {
|
||||||
roles = roles.replace("[", "");
|
try {
|
||||||
roles = roles.replace("]", "");
|
List<Map> rolesMap = JsonSerialization.readValue(roles, List.class);
|
||||||
|
config.put("roles", JsonSerialization.writeValueAsString(rolesMap.stream().map(roleConfig -> {
|
||||||
if (!roles.isEmpty()) {
|
roleConfig.put("id", realm.getRole(roleConfig.get("id").toString()).getId());
|
||||||
String roleNames = "";
|
return roleConfig;
|
||||||
|
}).collect(Collectors.toList())));
|
||||||
for (String role : roles.split(",")) {
|
} catch (Exception e) {
|
||||||
if (!roleNames.isEmpty()) {
|
throw new RuntimeException("Error while exporting policy [" + policyRepresentation.getName() + "].", e);
|
||||||
roleNames = roleNames + ",";
|
|
||||||
}
|
|
||||||
|
|
||||||
role = role.replace("\"", "");
|
|
||||||
|
|
||||||
roleNames = roleNames + "\"" + this.realm.getRole(role).getId() + "\"";
|
|
||||||
}
|
|
||||||
|
|
||||||
config.put("roles", "[" + roleNames + "]");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String users = config.get("users");
|
String users = config.get("users");
|
||||||
|
|
||||||
if (users != null) {
|
if (users != null && !users.isEmpty()) {
|
||||||
users = users.replace("[", "");
|
try {
|
||||||
users = users.replace("]", "");
|
List<String> usersMap = JsonSerialization.readValue(users, List.class);
|
||||||
|
config.put("users", JsonSerialization.writeValueAsString(usersMap.stream().map(userName -> this.session.users().getUserByUsername(userName, this.realm).getId()).collect(Collectors.toList())));
|
||||||
if (!users.isEmpty()) {
|
} catch (Exception e) {
|
||||||
String userNames = "";
|
throw new RuntimeException("Error while exporting policy [" + policyRepresentation.getName() + "].", e);
|
||||||
|
|
||||||
for (String user : users.split(",")) {
|
|
||||||
if (!userNames.isEmpty()) {
|
|
||||||
userNames = userNames + ",";
|
|
||||||
}
|
|
||||||
|
|
||||||
user = user.replace("\"", "");
|
|
||||||
|
|
||||||
userNames = userNames + "\"" + this.session.users().getUserByUsername(user, this.realm).getId() + "\"";
|
|
||||||
}
|
|
||||||
|
|
||||||
config.put("users", "[" + userNames + "]");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String scopes = config.get("scopes");
|
String scopes = config.get("scopes");
|
||||||
|
|
||||||
if (scopes != null && !scopes.isEmpty()) {
|
if (scopes != null && !scopes.isEmpty()) {
|
||||||
scopes = scopes.replace("[", "");
|
try {
|
||||||
scopes = scopes.replace("]", "");
|
List<String> scopesMap = JsonSerialization.readValue(scopes, List.class);
|
||||||
|
config.put("scopes", JsonSerialization.writeValueAsString(scopesMap.stream().map(scopeName -> {
|
||||||
if (!scopes.isEmpty()) {
|
Scope newScope = scopeStore.findByName(scopeName, resourceServer.getId());
|
||||||
String scopeNames = "";
|
|
||||||
|
|
||||||
for (String scope : scopes.split(",")) {
|
|
||||||
if (!scopeNames.isEmpty()) {
|
|
||||||
scopeNames = scopeNames + ",";
|
|
||||||
}
|
|
||||||
|
|
||||||
scope = scope.replace("\"", "");
|
|
||||||
|
|
||||||
Scope newScope = scopeStore.findByName(scope, resourceServer.getId());
|
|
||||||
|
|
||||||
if (newScope == null) {
|
if (newScope == null) {
|
||||||
throw new RuntimeException("Scope with name [" + scope + "] not defined.");
|
throw new RuntimeException("Scope with name [" + scopeName + "] not defined.");
|
||||||
}
|
}
|
||||||
|
|
||||||
scopeNames = scopeNames + "\"" + newScope.getId() + "\"";
|
return newScope.getId();
|
||||||
}
|
}).collect(Collectors.toList())));
|
||||||
|
} catch (Exception e) {
|
||||||
config.put("scopes", "[" + scopeNames + "]");
|
throw new RuntimeException("Error while exporting policy [" + policyRepresentation.getName() + "].", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String policyResources = config.get("resources");
|
String policyResources = config.get("resources");
|
||||||
|
|
||||||
if (policyResources != null && !policyResources.isEmpty()) {
|
if (policyResources != null && !policyResources.isEmpty()) {
|
||||||
policyResources = policyResources.replace("[", "");
|
try {
|
||||||
policyResources = policyResources.replace("]", "");
|
List<String> resources = JsonSerialization.readValue(policyResources, List.class);
|
||||||
|
config.put("resources", JsonSerialization.writeValueAsString(resources.stream().map(resourceName -> storeFactory.getResourceStore().findByName(resourceName, resourceServer.getId()).getId()).collect(Collectors.toList())));
|
||||||
if (!policyResources.isEmpty()) {
|
} catch (Exception e) {
|
||||||
String resourceNames = "";
|
throw new RuntimeException("Error while exporting policy [" + policyRepresentation.getName() + "].", e);
|
||||||
|
|
||||||
for (String resource : policyResources.split(",")) {
|
|
||||||
if (!resourceNames.isEmpty()) {
|
|
||||||
resourceNames = resourceNames + ",";
|
|
||||||
}
|
|
||||||
|
|
||||||
resource = resource.replace("\"", "");
|
|
||||||
|
|
||||||
if ("".equals(resource)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
resourceNames = resourceNames + "\"" + storeFactory.getResourceStore().findByName(resource, resourceServer.getId()).getId() + "\"";
|
|
||||||
}
|
|
||||||
|
|
||||||
config.put("resources", "[" + resourceNames + "]");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String applyPolicies = config.get("applyPolicies");
|
String applyPolicies = config.get("applyPolicies");
|
||||||
|
|
||||||
if (applyPolicies != null && !applyPolicies.isEmpty()) {
|
if (applyPolicies != null && !applyPolicies.isEmpty()) {
|
||||||
applyPolicies = applyPolicies.replace("[", "");
|
try {
|
||||||
applyPolicies = applyPolicies.replace("]", "");
|
List<String> policies = JsonSerialization.readValue(applyPolicies, List.class);
|
||||||
|
config.put("applyPolicies", JsonSerialization.writeValueAsString(policies.stream().map(policyName -> {
|
||||||
if (!applyPolicies.isEmpty()) {
|
Policy policy = policyStore.findByName(policyName, resourceServer.getId());
|
||||||
String policyNames = "";
|
|
||||||
|
|
||||||
for (String pId : applyPolicies.split(",")) {
|
|
||||||
if (!policyNames.isEmpty()) {
|
|
||||||
policyNames = policyNames + ",";
|
|
||||||
}
|
|
||||||
|
|
||||||
pId = pId.replace("\"", "").trim();
|
|
||||||
|
|
||||||
Policy policy = policyStore.findByName(pId, resourceServer.getId());
|
|
||||||
|
|
||||||
if (policy == null) {
|
if (policy == null) {
|
||||||
throw new RuntimeException("Policy with name [" + pId + "] not defined.");
|
throw new RuntimeException("Policy with name [" + policyName + "] not defined.");
|
||||||
}
|
}
|
||||||
|
|
||||||
policyNames = policyNames + "\"" + policy.getId() + "\"";
|
return policy.getId();
|
||||||
}
|
}).collect(Collectors.toList())));
|
||||||
|
} catch (Exception e) {
|
||||||
config.put("applyPolicies", "[" + policyNames + "]");
|
throw new RuntimeException("Error while exporting policy [" + policyRepresentation.getName() + "].", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -491,123 +429,59 @@ public class ResourceServerService {
|
||||||
}
|
}
|
||||||
|
|
||||||
private PolicyRepresentation createPolicyRepresentation(StoreFactory storeFactory, Policy policy) {
|
private PolicyRepresentation createPolicyRepresentation(StoreFactory storeFactory, Policy policy) {
|
||||||
PolicyRepresentation rep = Models.toRepresentation(policy, authorization);
|
try {
|
||||||
|
PolicyRepresentation rep = Models.toRepresentation(policy, authorization);
|
||||||
|
|
||||||
rep.setId(null);
|
rep.setId(null);
|
||||||
rep.setDependentPolicies(null);
|
rep.setDependentPolicies(null);
|
||||||
|
|
||||||
Map<String, String> config = rep.getConfig();
|
Map<String, String> config = rep.getConfig();
|
||||||
|
|
||||||
String roles = config.get("roles");
|
String roles = config.get("roles");
|
||||||
|
|
||||||
if (roles != null && !roles.isEmpty()) {
|
if (roles != null && !roles.isEmpty()) {
|
||||||
roles = roles.replace("[", "");
|
List<Map> rolesMap = JsonSerialization.readValue(roles, List.class);
|
||||||
roles = roles.replace("]", "");
|
config.put("roles", JsonSerialization.writeValueAsString(rolesMap.stream().map(roleMap -> {
|
||||||
|
roleMap.put("id", realm.getRoleById(roleMap.get("id").toString()).getName());
|
||||||
if (!roles.isEmpty()) {
|
return roleMap;
|
||||||
String roleNames = "";
|
}).collect(Collectors.toList())));
|
||||||
|
|
||||||
for (String role : roles.split(",")) {
|
|
||||||
if (!roleNames.isEmpty()) {
|
|
||||||
roleNames = roleNames + ",";
|
|
||||||
}
|
|
||||||
|
|
||||||
role = role.replace("\"", "");
|
|
||||||
|
|
||||||
roleNames = roleNames + "\"" + this.realm.getRoleById(role).getName() + "\"";
|
|
||||||
}
|
|
||||||
|
|
||||||
config.put("roles", "[" + roleNames + "]");
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
String users = config.get("users");
|
String users = config.get("users");
|
||||||
|
|
||||||
if (users != null) {
|
if (users != null && !users.isEmpty()) {
|
||||||
users = users.replace("[", "");
|
|
||||||
users = users.replace("]", "");
|
|
||||||
|
|
||||||
if (!users.isEmpty()) {
|
|
||||||
UserFederationManager userManager = this.session.users();
|
UserFederationManager userManager = this.session.users();
|
||||||
String userNames = "";
|
List<String> userIds = JsonSerialization.readValue(users, List.class);
|
||||||
|
config.put("users", JsonSerialization.writeValueAsString(userIds.stream().map(userId -> userManager.getUserById(userId, this.realm).getUsername()).collect(Collectors.toList())));
|
||||||
for (String user : users.split(",")) {
|
|
||||||
if (!userNames.isEmpty()) {
|
|
||||||
userNames = userNames + ",";
|
|
||||||
}
|
|
||||||
|
|
||||||
user = user.replace("\"", "");
|
|
||||||
|
|
||||||
userNames = userNames + "\"" + userManager.getUserById(user, this.realm).getUsername() + "\"";
|
|
||||||
}
|
|
||||||
|
|
||||||
config.put("users", "[" + userNames + "]");
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
String scopes = config.get("scopes");
|
String scopes = config.get("scopes");
|
||||||
|
|
||||||
if (scopes != null && !scopes.isEmpty()) {
|
if (scopes != null && !scopes.isEmpty()) {
|
||||||
scopes = scopes.replace("[", "");
|
|
||||||
scopes = scopes.replace("]", "");
|
|
||||||
|
|
||||||
if (!scopes.isEmpty()) {
|
|
||||||
ScopeStore scopeStore = storeFactory.getScopeStore();
|
ScopeStore scopeStore = storeFactory.getScopeStore();
|
||||||
String scopeNames = "";
|
List<String> scopeIds = JsonSerialization.readValue(scopes, List.class);
|
||||||
|
config.put("scopes", JsonSerialization.writeValueAsString(scopeIds.stream().map(scopeId -> scopeStore.findById(scopeId).getName()).collect(Collectors.toList())));
|
||||||
for (String scope : scopes.split(",")) {
|
|
||||||
if (!scopeNames.isEmpty()) {
|
|
||||||
scopeNames = scopeNames + ",";
|
|
||||||
}
|
|
||||||
|
|
||||||
scope = scope.replace("\"", "");
|
|
||||||
|
|
||||||
scopeNames = scopeNames + "\"" + scopeStore.findById(scope).getName() + "\"";
|
|
||||||
}
|
|
||||||
|
|
||||||
config.put("scopes", "[" + scopeNames + "]");
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
String policyResources = config.get("resources");
|
String policyResources = config.get("resources");
|
||||||
|
|
||||||
if (policyResources != null && !policyResources.isEmpty()) {
|
if (policyResources != null && !policyResources.isEmpty()) {
|
||||||
policyResources = policyResources.replace("[", "");
|
|
||||||
policyResources = policyResources.replace("]", "");
|
|
||||||
|
|
||||||
if (!policyResources.isEmpty()) {
|
|
||||||
ResourceStore resourceStore = storeFactory.getResourceStore();
|
ResourceStore resourceStore = storeFactory.getResourceStore();
|
||||||
String resourceNames = "";
|
List<String> resourceIds = JsonSerialization.readValue(policyResources, List.class);
|
||||||
|
config.put("resources", JsonSerialization.writeValueAsString(resourceIds.stream().map(resourceId -> resourceStore.findById(resourceId).getName()).collect(Collectors.toList())));
|
||||||
for (String resource : policyResources.split(",")) {
|
|
||||||
if (!resourceNames.isEmpty()) {
|
|
||||||
resourceNames = resourceNames + ",";
|
|
||||||
}
|
|
||||||
|
|
||||||
resource = resource.replace("\"", "");
|
|
||||||
|
|
||||||
resourceNames = resourceNames + "\"" + resourceStore.findById(resource).getName() + "\"";
|
|
||||||
}
|
|
||||||
|
|
||||||
config.put("resources", "[" + resourceNames + "]");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String policyNames = "";
|
|
||||||
Set<Policy> associatedPolicies = policy.getAssociatedPolicies();
|
|
||||||
|
|
||||||
if (!associatedPolicies.isEmpty()) {
|
|
||||||
for (Policy associatedPolicy : associatedPolicies) {
|
|
||||||
if (!policyNames.isEmpty()) {
|
|
||||||
policyNames = policyNames + ",";
|
|
||||||
}
|
|
||||||
|
|
||||||
policyNames = policyNames + "\"" + associatedPolicy.getName() + "\"";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
config.put("applyPolicies", "[" + policyNames + "]");
|
Set<Policy> associatedPolicies = policy.getAssociatedPolicies();
|
||||||
}
|
|
||||||
|
|
||||||
return rep;
|
if (!associatedPolicies.isEmpty()) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,14 +21,10 @@ package org.keycloak.authorization.admin.representation;
|
||||||
import org.keycloak.authorization.AuthorizationProvider;
|
import org.keycloak.authorization.AuthorizationProvider;
|
||||||
import org.keycloak.authorization.Decision.Effect;
|
import org.keycloak.authorization.Decision.Effect;
|
||||||
import org.keycloak.authorization.admin.util.Models;
|
import org.keycloak.authorization.admin.util.Models;
|
||||||
import org.keycloak.authorization.model.Resource;
|
|
||||||
import org.keycloak.authorization.model.ResourceServer;
|
import org.keycloak.authorization.model.ResourceServer;
|
||||||
import org.keycloak.authorization.model.Scope;
|
import org.keycloak.authorization.model.Scope;
|
||||||
import org.keycloak.authorization.policy.evaluation.Result;
|
import org.keycloak.authorization.policy.evaluation.Result;
|
||||||
import org.keycloak.authorization.policy.evaluation.Result.PolicyResult;
|
import org.keycloak.authorization.policy.evaluation.Result.PolicyResult;
|
||||||
import org.keycloak.authorization.store.StoreFactory;
|
|
||||||
import org.keycloak.authorization.util.Permissions;
|
|
||||||
import org.keycloak.representations.idm.authorization.Permission;
|
|
||||||
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
|
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
|
||||||
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
|
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
|
||||||
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
|
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
|
||||||
|
@ -90,9 +86,17 @@ public class PolicyEvaluationResponse {
|
||||||
policies.add(toRepresentation(policy, authorization));
|
policies.add(toRepresentation(policy, authorization));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (rep.getResource().getId() != null) {
|
||||||
|
if (!rep.getScopes().isEmpty()) {
|
||||||
|
rep.getResource().setName(rep.getResource().getName() + " with scopes " + rep.getScopes().stream().map(ScopeRepresentation::getName).collect(Collectors.toList()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
rep.setPolicies(policies);
|
rep.setPolicies(policies);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resultsRep.sort((o1, o2) -> o1.getResource().getName().compareTo(o2.getResource().getName()));
|
||||||
|
|
||||||
response.results = resultsRep;
|
response.results = resultsRep;
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
|
|
|
@ -63,13 +63,18 @@ public final class Models {
|
||||||
scope.setId(model.getId());
|
scope.setId(model.getId());
|
||||||
scope.setName(model.getName());
|
scope.setName(model.getName());
|
||||||
scope.setIconUri(model.getIconUri());
|
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<>());
|
scope.setPolicies(new ArrayList<>());
|
||||||
|
|
||||||
Set<Policy> policies = new HashSet<>();
|
policyStore.findByScopeIds(Arrays.asList(model.getId()), model.getResourceServer().getId()).forEach(policyModel -> {
|
||||||
|
|
||||||
policies.addAll(authorizationProvider.getStoreFactory().getPolicyStore().findByScopeIds(Arrays.asList(model.getId()), model.getResourceServer().getId()));
|
|
||||||
|
|
||||||
for (Policy policyModel : policies) {
|
|
||||||
PolicyRepresentation policy = new PolicyRepresentation();
|
PolicyRepresentation policy = new PolicyRepresentation();
|
||||||
|
|
||||||
policy.setId(policyModel.getId());
|
policy.setId(policyModel.getId());
|
||||||
|
@ -79,7 +84,7 @@ public final class Models {
|
||||||
if (!scope.getPolicies().contains(policy)) {
|
if (!scope.getPolicies().contains(policy)) {
|
||||||
scope.getPolicies().add(policy);
|
scope.getPolicies().add(policy);
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
return scope;
|
return scope;
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@ import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.representations.AccessToken;
|
import org.keycloak.representations.AccessToken;
|
||||||
|
import org.keycloak.saml.common.util.StringUtil;
|
||||||
import org.keycloak.services.ErrorResponseException;
|
import org.keycloak.services.ErrorResponseException;
|
||||||
import org.keycloak.util.JsonSerialization;
|
import org.keycloak.util.JsonSerialization;
|
||||||
|
|
||||||
|
@ -37,6 +38,7 @@ import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||||
|
@ -53,61 +55,59 @@ public class KeycloakIdentity implements Identity {
|
||||||
}
|
}
|
||||||
|
|
||||||
public KeycloakIdentity(AccessToken accessToken, KeycloakSession keycloakSession) {
|
public KeycloakIdentity(AccessToken accessToken, KeycloakSession keycloakSession) {
|
||||||
this.accessToken = accessToken;
|
if (accessToken == null) {
|
||||||
|
|
||||||
if (this.accessToken == null) {
|
|
||||||
throw new ErrorResponseException("invalid_bearer_token", "Could not obtain bearer access_token from request.", Status.FORBIDDEN);
|
throw new ErrorResponseException("invalid_bearer_token", "Could not obtain bearer access_token from request.", Status.FORBIDDEN);
|
||||||
}
|
}
|
||||||
|
if (keycloakSession == null) {
|
||||||
|
throw new ErrorResponseException("no_keycloak_session", "No keycloak session", Status.FORBIDDEN);
|
||||||
|
}
|
||||||
|
this.accessToken = accessToken;
|
||||||
this.keycloakSession = keycloakSession;
|
this.keycloakSession = keycloakSession;
|
||||||
this.realm = keycloakSession.getContext().getRealm();
|
this.realm = keycloakSession.getContext().getRealm();
|
||||||
|
|
||||||
HashMap<String, Collection<String>> attributes = new HashMap<>();
|
Map<String, Collection<String>> attributes = new HashMap<>();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
ObjectNode objectNode = JsonSerialization.createObjectNode(this.accessToken);
|
ObjectNode objectNode = JsonSerialization.createObjectNode(this.accessToken);
|
||||||
Iterator<String> iterator = objectNode.fieldNames();
|
Iterator<String> iterator = objectNode.fieldNames();
|
||||||
List<String> roleNames = new ArrayList<>();
|
|
||||||
|
|
||||||
while (iterator.hasNext()) {
|
while (iterator.hasNext()) {
|
||||||
String fieldName = iterator.next();
|
String fieldName = iterator.next();
|
||||||
JsonNode fieldValue = objectNode.get(fieldName);
|
JsonNode fieldValue = objectNode.get(fieldName);
|
||||||
List<String> values = new ArrayList<>();
|
List<String> values = new ArrayList<>();
|
||||||
|
|
||||||
values.add(fieldValue.asText());
|
if (fieldValue.isArray()) {
|
||||||
|
Iterator<JsonNode> valueIterator = fieldValue.iterator();
|
||||||
|
|
||||||
if (fieldName.equals("realm_access")) {
|
while (valueIterator.hasNext()) {
|
||||||
JsonNode grantedRoles = fieldValue.get("roles");
|
values.add(valueIterator.next().asText());
|
||||||
|
|
||||||
if (grantedRoles != null) {
|
|
||||||
Iterator<JsonNode> rolesIt = grantedRoles.iterator();
|
|
||||||
|
|
||||||
while (rolesIt.hasNext()) {
|
|
||||||
roleNames.add(rolesIt.next().asText());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
String value = fieldValue.asText();
|
||||||
|
|
||||||
|
if (StringUtil.isNullOrEmpty(value)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
values.add(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fieldName.equals("resource_access")) {
|
if (!values.isEmpty()) {
|
||||||
Iterator<JsonNode> resourceAccessIt = fieldValue.iterator();
|
attributes.put(fieldName, values);
|
||||||
|
|
||||||
while (resourceAccessIt.hasNext()) {
|
|
||||||
JsonNode grantedRoles = resourceAccessIt.next().get("roles");
|
|
||||||
|
|
||||||
if (grantedRoles != null) {
|
|
||||||
Iterator<JsonNode> rolesIt = grantedRoles.iterator();
|
|
||||||
|
|
||||||
while (rolesIt.hasNext()) {
|
|
||||||
roleNames.add(rolesIt.next().asText());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
attributes.put(fieldName, values);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
attributes.put("roles", roleNames);
|
AccessToken.Access realmAccess = accessToken.getRealmAccess();
|
||||||
|
|
||||||
|
if (realmAccess != null) {
|
||||||
|
attributes.put("kc.realm.roles", realmAccess.getRoles());
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, AccessToken.Access> resourceAccess = accessToken.getResourceAccess();
|
||||||
|
|
||||||
|
if (resourceAccess != null) {
|
||||||
|
resourceAccess.forEach((clientId, access) -> attributes.put("kc.client." + clientId + ".roles", access.getRoles()));
|
||||||
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException("Error while reading attributes from security token.", e);
|
throw new RuntimeException("Error while reading attributes from security token.", e);
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@ import org.keycloak.authorization.protection.permission.PermissionService;
|
||||||
import org.keycloak.authorization.protection.permission.PermissionsService;
|
import org.keycloak.authorization.protection.permission.PermissionsService;
|
||||||
import org.keycloak.authorization.protection.resource.ResourceService;
|
import org.keycloak.authorization.protection.resource.ResourceService;
|
||||||
import org.keycloak.models.ClientModel;
|
import org.keycloak.models.ClientModel;
|
||||||
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.services.ErrorResponseException;
|
import org.keycloak.services.ErrorResponseException;
|
||||||
|
|
||||||
|
@ -48,16 +49,12 @@ public class ProtectionService {
|
||||||
@Path("/resource_set")
|
@Path("/resource_set")
|
||||||
public Object resource() {
|
public Object resource() {
|
||||||
KeycloakIdentity identity = createIdentity();
|
KeycloakIdentity identity = createIdentity();
|
||||||
|
ResourceServer resourceServer = getResourceServer(identity);
|
||||||
if (!identity.hasRole("uma_protection")) {
|
ResourceSetService resourceManager = new ResourceSetService(resourceServer, this.authorization, null);
|
||||||
throw new ErrorResponseException(OAuthErrorException.INVALID_SCOPE, "Requires uma_protection scope.", Status.FORBIDDEN);
|
|
||||||
}
|
|
||||||
|
|
||||||
ResourceSetService resourceManager = new ResourceSetService(getResourceServer(identity), this.authorization, null);
|
|
||||||
|
|
||||||
ResteasyProviderFactory.getInstance().injectProperties(resourceManager);
|
ResteasyProviderFactory.getInstance().injectProperties(resourceManager);
|
||||||
|
|
||||||
ResourceService resource = new ResourceService(getResourceServer(identity), identity, resourceManager, this.authorization);
|
ResourceService resource = new ResourceService(resourceServer, identity, resourceManager, this.authorization);
|
||||||
|
|
||||||
ResteasyProviderFactory.getInstance().injectProperties(resource);
|
ResteasyProviderFactory.getInstance().injectProperties(resource);
|
||||||
|
|
||||||
|
@ -68,10 +65,6 @@ public class ProtectionService {
|
||||||
public Object permission() {
|
public Object permission() {
|
||||||
KeycloakIdentity identity = createIdentity();
|
KeycloakIdentity identity = createIdentity();
|
||||||
|
|
||||||
if (!identity.hasRole("uma_protection")) {
|
|
||||||
throw new ErrorResponseException(OAuthErrorException.INVALID_SCOPE, "Requires uma_protection scope.", Status.FORBIDDEN);
|
|
||||||
}
|
|
||||||
|
|
||||||
PermissionService resource = new PermissionService(identity, getResourceServer(identity), this.authorization);
|
PermissionService resource = new PermissionService(identity, getResourceServer(identity), this.authorization);
|
||||||
|
|
||||||
ResteasyProviderFactory.getInstance().injectProperties(resource);
|
ResteasyProviderFactory.getInstance().injectProperties(resource);
|
||||||
|
@ -83,10 +76,6 @@ public class ProtectionService {
|
||||||
public Object permissions() {
|
public Object permissions() {
|
||||||
KeycloakIdentity identity = createIdentity();
|
KeycloakIdentity identity = createIdentity();
|
||||||
|
|
||||||
if (!identity.hasRole("uma_protection")) {
|
|
||||||
throw new ErrorResponseException(OAuthErrorException.INVALID_SCOPE, "Requires uma_protection scope.", Status.FORBIDDEN);
|
|
||||||
}
|
|
||||||
|
|
||||||
PermissionsService resource = new PermissionsService(identity, getResourceServer(identity), this.authorization);
|
PermissionsService resource = new PermissionsService(identity, getResourceServer(identity), this.authorization);
|
||||||
|
|
||||||
ResteasyProviderFactory.getInstance().injectProperties(resource);
|
ResteasyProviderFactory.getInstance().injectProperties(resource);
|
||||||
|
@ -95,7 +84,17 @@ public class ProtectionService {
|
||||||
}
|
}
|
||||||
|
|
||||||
private KeycloakIdentity createIdentity() {
|
private KeycloakIdentity createIdentity() {
|
||||||
return new KeycloakIdentity(this.authorization.getKeycloakSession());
|
KeycloakIdentity identity = new KeycloakIdentity(this.authorization.getKeycloakSession());
|
||||||
|
ResourceServer resourceServer = getResourceServer(identity);
|
||||||
|
KeycloakSession keycloakSession = authorization.getKeycloakSession();
|
||||||
|
RealmModel realm = keycloakSession.getContext().getRealm();
|
||||||
|
ClientModel client = realm.getClientById(resourceServer.getClientId());
|
||||||
|
|
||||||
|
if (!identity.hasClientRole(client.getClientId(), "uma_protection")) {
|
||||||
|
throw new ErrorResponseException(OAuthErrorException.INVALID_SCOPE, "Requires uma_protection scope.", Status.FORBIDDEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
return identity;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ResourceServer getResourceServer(Identity identity) {
|
private ResourceServer getResourceServer(Identity identity) {
|
||||||
|
|
|
@ -61,20 +61,38 @@ 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)));
|
resourceStore.findByOwner(resourceServer.getClientId()).stream().forEach(resource -> permissions.addAll(createResourcePermissions(resource, resourceServer, authorization)));
|
||||||
resourceStore.findByOwner(identity.getId()).stream().forEach(resource -> permissions.addAll(createResourcePermissions(resource)));
|
resourceStore.findByOwner(identity.getId()).stream().forEach(resource -> permissions.addAll(createResourcePermissions(resource, resourceServer, authorization)));
|
||||||
|
|
||||||
return permissions;
|
return permissions;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<ResourcePermission> createResourcePermissions(Resource resource) {
|
public static List<ResourcePermission> createResourcePermissions(Resource resource, ResourceServer resourceServer, AuthorizationProvider authorization) {
|
||||||
List<ResourcePermission> permissions = new ArrayList<>();
|
List<ResourcePermission> permissions = new ArrayList<>();
|
||||||
List<Scope> scopes = resource.getScopes();
|
List<Scope> scopes = resource.getScopes();
|
||||||
|
|
||||||
permissions.add(new ResourcePermission(resource, Collections.emptyList(), resource.getResourceServer()));
|
if (scopes.isEmpty()) {
|
||||||
|
String type = resource.getType();
|
||||||
|
|
||||||
for (Scope scope : scopes) {
|
// check if there is a typed resource whose scopes are inherited by the resource being requested. In this case, we assume that parent resource
|
||||||
permissions.add(new ResourcePermission(resource, Arrays.asList(scope), resource.getResourceServer()));
|
// is owned by the resource server itself
|
||||||
|
if (type != null) {
|
||||||
|
StoreFactory storeFactory = authorization.getStoreFactory();
|
||||||
|
ResourceStore resourceStore = storeFactory.getResourceStore();
|
||||||
|
resourceStore.findByType(type).forEach(resource1 -> {
|
||||||
|
if (resource1.getOwner().equals(resourceServer.getClientId())) {
|
||||||
|
scopes.addAll(resource1.getScopes());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scopes.size() > 1) {
|
||||||
|
for (Scope scope : scopes) {
|
||||||
|
permissions.add(new ResourcePermission(resource, Arrays.asList(scope), resource.getResourceServer()));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
permissions.add(new ResourcePermission(resource, Collections.emptyList(), resource.getResourceServer()));
|
||||||
}
|
}
|
||||||
|
|
||||||
return permissions;
|
return permissions;
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
|
|
||||||
<body data-ng-controller="TokenCtrl">
|
<body data-ng-controller="TokenCtrl">
|
||||||
|
|
||||||
<a href data-ng-click="showRpt()">Show Requesting Party Token </a> | <a href data-ng-click="showAccessToken()">Show Access Token </a> | <a href data-ng-click="requestEntitlements()">Request Entitlements</a>
|
<a href data-ng-click="showRpt()">Show Requesting Party Token </a> | <a href data-ng-click="showAccessToken()">Show Access Token </a> | <a href data-ng-click="requestEntitlements()">Request Entitlements</a> | <a href="" ng-click="Identity.logout()">Sign Out</a>
|
||||||
|
|
||||||
<div id="content-area" class="col-md-9" role="main">
|
<div id="content-area" class="col-md-9" role="main">
|
||||||
<div id="content" ng-view/>
|
<div id="content" ng-view/>
|
||||||
|
|
|
@ -64,6 +64,8 @@ module.controller('TokenCtrl', function ($scope, Identity) {
|
||||||
$scope.requestEntitlements = function () {
|
$scope.requestEntitlements = function () {
|
||||||
Identity.authorization.entitlement('photoz-restful-api').then(function (rpt) {});
|
Identity.authorization.entitlement('photoz-restful-api').then(function (rpt) {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$scope.Identity = Identity;
|
||||||
});
|
});
|
||||||
|
|
||||||
module.controller('AlbumCtrl', function ($scope, $http, $routeParams, $location, Album) {
|
module.controller('AlbumCtrl', function ($scope, $http, $routeParams, $location, Album) {
|
||||||
|
@ -83,14 +85,13 @@ module.controller('ProfileCtrl', function ($scope, $http, $routeParams, $locatio
|
||||||
$scope.profile = Profile.get();
|
$scope.profile = Profile.get();
|
||||||
});
|
});
|
||||||
|
|
||||||
module.controller('AdminAlbumCtrl', function ($scope, $http, $route, AdminAlbum, Album) {
|
module.controller('AdminAlbumCtrl', function ($scope, $http, $route, $location, AdminAlbum, Album) {
|
||||||
$scope.albums = {};
|
$scope.albums = {};
|
||||||
$http.get(apiUrl + '/admin/album').success(function (data) {
|
$http.get(apiUrl + '/admin/album').success(function (data) {
|
||||||
$scope.albums = data;
|
$scope.albums = data;
|
||||||
});
|
});
|
||||||
$scope.deleteAlbum = function (album) {
|
$scope.deleteAlbum = function (album) {
|
||||||
var newAlbum = new Album(album);
|
new Album(album).$delete({id: album.id}, function () {
|
||||||
newAlbum.$delete({id: album.id}, function () {
|
|
||||||
$route.reload();
|
$route.reload();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +1,19 @@
|
||||||
<h1>All Albums</h1>
|
<h1>All Albums</h1>
|
||||||
<table class="table" data-ng-repeat="(key, value) in albums">
|
<table class="table" data-ng-repeat="(key, value) in albums">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>{{key}}</th>
|
<th>{{key}}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<ul>
|
<ul>
|
||||||
<li data-ng-repeat="p in value">
|
<li data-ng-repeat="p in value">
|
||||||
<a href="#/album/{{p.id}}">{{p.name}}</a> - [<a href="#" id="delete-{{p.name}}" ng-click="deleteAlbum(p)">X</a>]
|
<a id="view-{{p.name}}" href="#/album/{{p.id}}">{{p.name}}</a> - [<a href="#/admin/album" id="delete-{{p.name}}" ng-click="deleteAlbum(p)">X</a>]
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
|
@ -1,4 +1,4 @@
|
||||||
<h2><span>Welcome To Photoz, {{Identity.claims.name}}</span> [<a href="" ng-click="Identity.logout()">Sign Out</a>]</h2>
|
<h2><span>Welcome To Photoz, {{Identity.claims.name}}</span></h2>
|
||||||
<div data-ng-show="Identity.isAdmin()"><b>Administration: </b> [<a href="#/admin/album" id="admin-albums">All Albums</a>]</div>
|
<div data-ng-show="Identity.isAdmin()"><b>Administration: </b> [<a href="#/admin/album" id="admin-albums">All Albums</a>]</div>
|
||||||
<hr/>
|
<hr/>
|
||||||
<br/>
|
<br/>
|
||||||
|
@ -9,14 +9,14 @@
|
||||||
<span data-ng-show="albums.length == 0" id="resource-list-empty">You don't have any albums, yet.</span>
|
<span data-ng-show="albums.length == 0" id="resource-list-empty">You don't have any albums, yet.</span>
|
||||||
<table class="table" data-ng-show="albums.length > 0">
|
<table class="table" data-ng-show="albums.length > 0">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Your Albums</th>
|
<th>Your Albums</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr data-ng-repeat="p in albums">
|
<tr data-ng-repeat="p in albums">
|
||||||
<td><a href="#/album/{{p.id}}">{{p.name}}</a> - [<a href="#" id="delete-{{p.name}}" ng-click="deleteAlbum(p)">X</a>]</td>
|
<td><a id="view-{{p.name}}" href="#/album/{{p.id}}">{{p.name}}</a> - [<a href="#" id="delete-{{p.name}}" ng-click="deleteAlbum(p)">X</a>]</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
|
@ -53,7 +53,7 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"realmRoles": [
|
"realmRoles": [
|
||||||
"user", "admin", "uma_authorization"
|
"admin", "uma_authorization"
|
||||||
],
|
],
|
||||||
"clientRoles": {
|
"clientRoles": {
|
||||||
"realm-management": [
|
"realm-management": [
|
||||||
|
|
|
@ -21,10 +21,10 @@
|
||||||
"name": "urn:photoz.com:scopes:album:view"
|
"name": "urn:photoz.com:scopes:album:view"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "urn:photoz.com:scopes:album:create"
|
"name": "urn:photoz.com:scopes:album:delete"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "urn:photoz.com:scopes:album:delete"
|
"name": "urn:photoz.com:scopes:album:create"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -44,12 +44,15 @@
|
||||||
"name": "Only Owner Policy",
|
"name": "Only Owner Policy",
|
||||||
"description": "Defines that only the resource owner is allowed to do something",
|
"description": "Defines that only the resource owner is allowed to do something",
|
||||||
"type": "drools",
|
"type": "drools",
|
||||||
|
"logic": "POSITIVE",
|
||||||
|
"decisionStrategy": "UNANIMOUS",
|
||||||
"config": {
|
"config": {
|
||||||
"mavenArtifactVersion": "2.1.0-SNAPSHOT",
|
"mavenArtifactVersion": "2.1.0-SNAPSHOT",
|
||||||
"mavenArtifactId": "photoz-authz-policy",
|
"mavenArtifactId": "photoz-authz-policy",
|
||||||
"sessionName": "MainOwnerSession",
|
"sessionName": "MainOwnerSession",
|
||||||
"mavenArtifactGroupId": "org.keycloak.testsuite",
|
"mavenArtifactGroupId": "org.keycloak",
|
||||||
"moduleName": "PhotozAuthzOwnerPolicy",
|
"moduleName": "PhotozAuthzOwnerPolicy",
|
||||||
|
"applyPolicies": "[]",
|
||||||
"scannerPeriod": "1",
|
"scannerPeriod": "1",
|
||||||
"scannerPeriodUnit": "Hours"
|
"scannerPeriodUnit": "Hours"
|
||||||
}
|
}
|
||||||
|
@ -58,16 +61,22 @@
|
||||||
"name": "Any Admin Policy",
|
"name": "Any Admin Policy",
|
||||||
"description": "Defines that adminsitrators can do something",
|
"description": "Defines that adminsitrators can do something",
|
||||||
"type": "role",
|
"type": "role",
|
||||||
|
"logic": "POSITIVE",
|
||||||
|
"decisionStrategy": "UNANIMOUS",
|
||||||
"config": {
|
"config": {
|
||||||
"roles": "[\"admin\"]"
|
"applyPolicies": "[]",
|
||||||
|
"roles": "[{\"id\":\"admin\",\"required\":true}]"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Any User Policy",
|
"name": "Any User Policy",
|
||||||
"description": "Defines that any user can do something",
|
"description": "Defines that any user can do something",
|
||||||
"type": "role",
|
"type": "role",
|
||||||
|
"logic": "POSITIVE",
|
||||||
|
"decisionStrategy": "UNANIMOUS",
|
||||||
"config": {
|
"config": {
|
||||||
"roles": "[\"user\"]"
|
"applyPolicies": "[]",
|
||||||
|
"roles": "[{\"id\":\"user\"}]"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -77,6 +86,7 @@
|
||||||
"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}"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -84,6 +94,8 @@
|
||||||
"name": "Administration Policy",
|
"name": "Administration Policy",
|
||||||
"description": "Defines that only administrators from a specific network address can do something.",
|
"description": "Defines that only administrators from a specific network address can do something.",
|
||||||
"type": "aggregate",
|
"type": "aggregate",
|
||||||
|
"logic": "POSITIVE",
|
||||||
|
"decisionStrategy": "UNANIMOUS",
|
||||||
"config": {
|
"config": {
|
||||||
"applyPolicies": "[\"Any Admin Policy\",\"Only From a Specific Client Address\"]"
|
"applyPolicies": "[\"Any Admin Policy\",\"Only From a Specific Client Address\"]"
|
||||||
}
|
}
|
||||||
|
@ -92,35 +104,28 @@
|
||||||
"name": "Only Owner and Administrators Policy",
|
"name": "Only Owner and Administrators Policy",
|
||||||
"description": "Defines that only the resource owner and administrators can do something",
|
"description": "Defines that only the resource owner and administrators can do something",
|
||||||
"type": "aggregate",
|
"type": "aggregate",
|
||||||
|
"logic": "POSITIVE",
|
||||||
"decisionStrategy": "AFFIRMATIVE",
|
"decisionStrategy": "AFFIRMATIVE",
|
||||||
"config": {
|
"config": {
|
||||||
"applyPolicies": "[\"Administration Policy\",\"Only Owner Policy\"]"
|
"applyPolicies": "[\"Only Owner Policy\",\"Administration Policy\"]"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Only From @keycloak.org or Admin",
|
"name": "Only From @keycloak.org or Admin",
|
||||||
"description": "Defines that only users from @keycloak.org",
|
"description": "Defines that only users from @keycloak.org",
|
||||||
"type": "js",
|
"type": "js",
|
||||||
|
"logic": "POSITIVE",
|
||||||
|
"decisionStrategy": "UNANIMOUS",
|
||||||
"config": {
|
"config": {
|
||||||
"applyPolicies": "[]",
|
"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}"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "Only in the Period",
|
|
||||||
"description": "Access granted only during the morning",
|
|
||||||
"type": "time",
|
|
||||||
"config": {
|
|
||||||
"noa": "2016-01-03 23:59:59",
|
|
||||||
"expirationUnit": "Minutes",
|
|
||||||
"nbf": "2016-01-01 00:00:00",
|
|
||||||
"expirationTime": "1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "Album Resource Permission",
|
"name": "Album Resource Permission",
|
||||||
"description": "General policies that apply to all album resources.",
|
"description": "General policies that apply to all album resources.",
|
||||||
"type": "resource",
|
"type": "resource",
|
||||||
|
"logic": "POSITIVE",
|
||||||
"decisionStrategy": "AFFIRMATIVE",
|
"decisionStrategy": "AFFIRMATIVE",
|
||||||
"config": {
|
"config": {
|
||||||
"defaultResourceType": "http://photoz.com/album",
|
"defaultResourceType": "http://photoz.com/album",
|
||||||
|
|
|
@ -109,13 +109,7 @@ public class AlbumService {
|
||||||
|
|
||||||
private void createProtectedResource(Album album) {
|
private void createProtectedResource(Album album) {
|
||||||
try {
|
try {
|
||||||
HashSet<ScopeRepresentation> scopes = new HashSet<>();
|
ResourceRepresentation albumResource = new ResourceRepresentation(album.getName(), new HashSet(), "/album/" + album.getId(), "http://photoz.com/album");
|
||||||
|
|
||||||
scopes.add(new ScopeRepresentation(SCOPE_ALBUM_VIEW));
|
|
||||||
scopes.add(new ScopeRepresentation(SCOPE_ALBUM_CREATE));
|
|
||||||
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());
|
||||||
|
|
||||||
|
|
|
@ -76,17 +76,28 @@ public class PhotozClientAuthzTestApp extends AbstractPageWithInjectedUrl {
|
||||||
pause(500);
|
pause(500);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void login(String username, String password) {
|
public void login(String username, String password) throws InterruptedException {
|
||||||
navigateTo();
|
navigateTo();
|
||||||
|
Thread.sleep(2000);
|
||||||
if (this.driver.getCurrentUrl().startsWith(getInjectedUrl().toString())) {
|
if (this.driver.getCurrentUrl().startsWith(getInjectedUrl().toString())) {
|
||||||
|
Thread.sleep(2000);
|
||||||
logOut();
|
logOut();
|
||||||
|
navigateTo();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Thread.sleep(2000);
|
||||||
|
|
||||||
this.loginPage.form().login(username, password);
|
this.loginPage.form().login(username, password);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean wasDenied() {
|
public boolean wasDenied() {
|
||||||
return this.driver.findElement(By.id("output")).getText().contains("You can not access");
|
return this.driver.findElement(By.id("output")).getText().contains("You can not access");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void viewAlbum(String name) {
|
||||||
|
By id = By.id("view-" + name);
|
||||||
|
WaitUtils.waitUntilElement(id);
|
||||||
|
this.driver.findElements(id).forEach(WebElement::click);
|
||||||
|
pause(500);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,12 +16,10 @@
|
||||||
*/
|
*/
|
||||||
package org.keycloak.testsuite.adapter.example.authorization;
|
package org.keycloak.testsuite.adapter.example.authorization;
|
||||||
|
|
||||||
import org.apache.commons.io.IOUtils;
|
|
||||||
import org.jboss.arquillian.container.test.api.Deployer;
|
import org.jboss.arquillian.container.test.api.Deployer;
|
||||||
import org.jboss.arquillian.container.test.api.Deployment;
|
import org.jboss.arquillian.container.test.api.Deployment;
|
||||||
import org.jboss.arquillian.graphene.page.Page;
|
import org.jboss.arquillian.graphene.page.Page;
|
||||||
import org.jboss.arquillian.test.api.ArquillianResource;
|
import org.jboss.arquillian.test.api.ArquillianResource;
|
||||||
import org.jboss.shrinkwrap.api.asset.StringAsset;
|
|
||||||
import org.jboss.shrinkwrap.api.spec.WebArchive;
|
import org.jboss.shrinkwrap.api.spec.WebArchive;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.keycloak.admin.client.resource.AuthorizationResource;
|
import org.keycloak.admin.client.resource.AuthorizationResource;
|
||||||
|
@ -39,7 +37,6 @@ import java.io.FileInputStream;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.Consumer;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
|
@ -144,7 +141,6 @@ public abstract class AbstractPhotozExampleAdapterTest extends AbstractExampleAd
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.clientPage.login("admin", "admin");
|
|
||||||
this.clientPage.navigateToAdminAlbum();
|
this.clientPage.navigateToAdminAlbum();
|
||||||
this.clientPage.deleteAlbum("Alice-Family-Album");
|
this.clientPage.deleteAlbum("Alice-Family-Album");
|
||||||
|
|
||||||
|
@ -186,7 +182,6 @@ public abstract class AbstractPhotozExampleAdapterTest extends AbstractExampleAd
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.clientPage.login("admin", "admin");
|
|
||||||
this.clientPage.navigateToAdminAlbum();
|
this.clientPage.navigateToAdminAlbum();
|
||||||
|
|
||||||
assertTrue(this.clientPage.wasDenied());
|
assertTrue(this.clientPage.wasDenied());
|
||||||
|
@ -195,6 +190,121 @@ public abstract class AbstractPhotozExampleAdapterTest extends AbstractExampleAd
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAdminWithoutPermissionsToTypedResource() throws Exception {
|
||||||
|
try {
|
||||||
|
this.deployer.deploy(RESOURCE_SERVER_ID);
|
||||||
|
this.clientPage.login("alice", "alice");
|
||||||
|
this.clientPage.createAlbum("Alice Family Album");
|
||||||
|
|
||||||
|
this.clientPage.login("admin", "admin");
|
||||||
|
this.clientPage.navigateToAdminAlbum();
|
||||||
|
|
||||||
|
assertFalse(this.clientPage.wasDenied());
|
||||||
|
|
||||||
|
this.clientPage.viewAlbum("Alice Family Album");
|
||||||
|
|
||||||
|
assertFalse(this.clientPage.wasDenied());
|
||||||
|
|
||||||
|
for (PolicyRepresentation policy : getAuthorizationResource().policies().policies()) {
|
||||||
|
if ("Album Resource Permission".equals(policy.getName())) {
|
||||||
|
policy.getConfig().put("applyPolicies", "[\"Any User Policy\"]");
|
||||||
|
getAuthorizationResource().policies().policy(policy.getId()).update(policy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.clientPage.navigateToAdminAlbum();
|
||||||
|
this.clientPage.viewAlbum("Alice Family Album");
|
||||||
|
|
||||||
|
assertTrue(this.clientPage.wasDenied());
|
||||||
|
|
||||||
|
for (PolicyRepresentation policy : getAuthorizationResource().policies().policies()) {
|
||||||
|
if ("Album Resource Permission".equals(policy.getName())) {
|
||||||
|
policy.getConfig().put("applyPolicies", "[\"Any User Policy\", \"Administration Policy\"]");
|
||||||
|
getAuthorizationResource().policies().policy(policy.getId()).update(policy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.clientPage.navigateToAdminAlbum();
|
||||||
|
this.clientPage.viewAlbum("Alice Family Album");
|
||||||
|
|
||||||
|
assertFalse(this.clientPage.wasDenied());
|
||||||
|
|
||||||
|
this.clientPage.navigateToAdminAlbum();
|
||||||
|
this.clientPage.deleteAlbum("Alice Family Album");
|
||||||
|
|
||||||
|
List<ResourceRepresentation> resources = getAuthorizationResource().resources().resources();
|
||||||
|
|
||||||
|
assertTrue(resources.stream().filter(resource -> resource.getOwner().getName().equals("alice")).collect(Collectors.toList()).isEmpty());
|
||||||
|
} finally {
|
||||||
|
this.deployer.undeploy(RESOURCE_SERVER_ID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAdminWithoutPermissionsToDeleteScopePermission() throws Exception {
|
||||||
|
try {
|
||||||
|
this.deployer.deploy(RESOURCE_SERVER_ID);
|
||||||
|
this.clientPage.login("alice", "alice");
|
||||||
|
this.clientPage.createAlbum("Alice Family Album");
|
||||||
|
|
||||||
|
this.clientPage.login("admin", "admin");
|
||||||
|
this.clientPage.navigateToAdminAlbum();
|
||||||
|
|
||||||
|
assertFalse(this.clientPage.wasDenied());
|
||||||
|
|
||||||
|
this.clientPage.deleteAlbum("Alice Family Album");
|
||||||
|
|
||||||
|
assertFalse(this.clientPage.wasDenied());
|
||||||
|
|
||||||
|
List<ResourceRepresentation> resources = getAuthorizationResource().resources().resources();
|
||||||
|
|
||||||
|
assertTrue(resources.stream().filter(resource -> resource.getOwner().getName().equals("alice")).collect(Collectors.toList()).isEmpty());
|
||||||
|
|
||||||
|
for (PolicyRepresentation policy : getAuthorizationResource().policies().policies()) {
|
||||||
|
if ("Delete Album Permission".equals(policy.getName())) {
|
||||||
|
policy.getConfig().put("applyPolicies", "[\"Only Owner Policy\"]");
|
||||||
|
getAuthorizationResource().policies().policy(policy.getId()).update(policy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.clientPage.login("alice", "alice");
|
||||||
|
this.clientPage.createAlbum("Alice Family Album");
|
||||||
|
|
||||||
|
this.clientPage.login("admin", "admin");
|
||||||
|
this.clientPage.navigateToAdminAlbum();
|
||||||
|
this.clientPage.viewAlbum("Alice Family Album");
|
||||||
|
|
||||||
|
assertFalse(this.clientPage.wasDenied());
|
||||||
|
resources = getAuthorizationResource().resources().resources();
|
||||||
|
|
||||||
|
assertFalse(resources.stream().filter(resource -> resource.getOwner().getName().equals("alice")).collect(Collectors.toList()).isEmpty());
|
||||||
|
|
||||||
|
this.clientPage.navigateToAdminAlbum();
|
||||||
|
|
||||||
|
this.clientPage.deleteAlbum("Alice Family Album");
|
||||||
|
assertTrue(this.clientPage.wasDenied());
|
||||||
|
|
||||||
|
for (PolicyRepresentation policy : getAuthorizationResource().policies().policies()) {
|
||||||
|
if ("Delete Album Permission".equals(policy.getName())) {
|
||||||
|
policy.getConfig().put("applyPolicies", "[\"Only Owner and Administrators Policy\"]");
|
||||||
|
getAuthorizationResource().policies().policy(policy.getId()).update(policy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.clientPage.navigateToAdminAlbum();
|
||||||
|
this.clientPage.deleteAlbum("Alice Family Album");
|
||||||
|
|
||||||
|
assertFalse(this.clientPage.wasDenied());
|
||||||
|
|
||||||
|
resources = getAuthorizationResource().resources().resources();
|
||||||
|
|
||||||
|
assertTrue(resources.stream().filter(resource -> resource.getOwner().getName().equals("alice")).collect(Collectors.toList()).isEmpty());
|
||||||
|
} finally {
|
||||||
|
this.deployer.undeploy(RESOURCE_SERVER_ID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void importResourceServerSettings() throws FileNotFoundException {
|
private void importResourceServerSettings() throws FileNotFoundException {
|
||||||
getAuthorizationResource().importSettings(loadJson(new FileInputStream(new File(TEST_APPS_HOME_DIR + "/photoz/photoz-restful-api-authz-service.json")), ResourceServerRepresentation.class));
|
getAuthorizationResource().importSettings(loadJson(new FileInputStream(new File(TEST_APPS_HOME_DIR + "/photoz/photoz-restful-api-authz-service.json")), ResourceServerRepresentation.class));
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,6 +49,7 @@ import javax.ws.rs.core.MediaType;
|
||||||
import javax.ws.rs.core.MultivaluedMap;
|
import javax.ws.rs.core.MultivaluedMap;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
@ -56,6 +57,8 @@ import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static org.jboss.aesh.terminal.Key.e;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||||
*/
|
*/
|
||||||
|
@ -279,8 +282,12 @@ public abstract class AbstractPhotozAdminTest extends AbstractAuthorizationTest
|
||||||
RealmModel realm = authorizationProvider.getKeycloakSession().realms().getRealmByName(TEST_REALM_NAME);
|
RealmModel realm = authorizationProvider.getKeycloakSession().realms().getRealmByName(TEST_REALM_NAME);
|
||||||
RoleModel adminRole = realm.getRole("admin");
|
RoleModel adminRole = realm.getRole("admin");
|
||||||
|
|
||||||
|
Map role = new HashMap();
|
||||||
|
|
||||||
|
role.put("id", adminRole.getId());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
config.put("roles", JsonSerialization.writeValueAsString(new String[] {adminRole.getId()}));
|
config.put("roles", JsonSerialization.writeValueAsString(new Map[] {role}));
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
|
@ -352,10 +359,14 @@ public abstract class AbstractPhotozAdminTest extends AbstractAuthorizationTest
|
||||||
Policy policy = policyStore.create("Any User Policy", "role", resourceServer);
|
Policy policy = policyStore.create("Any User Policy", "role", resourceServer);
|
||||||
HashedMap config = new HashedMap();
|
HashedMap config = new HashedMap();
|
||||||
RealmModel realm = authorizationProvider.getKeycloakSession().realms().getRealmByName(TEST_REALM_NAME);
|
RealmModel realm = authorizationProvider.getKeycloakSession().realms().getRealmByName(TEST_REALM_NAME);
|
||||||
RoleModel adminRole = realm.getRole("user");
|
RoleModel userRole = realm.getRole("user");
|
||||||
|
|
||||||
|
Map role = new HashMap();
|
||||||
|
|
||||||
|
role.put("id", userRole.getId());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
config.put("roles", JsonSerialization.writeValueAsString(new String[] {adminRole.getId()}));
|
config.put("roles", JsonSerialization.writeValueAsString(new Map[] {role}));
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
|
|
|
@ -953,6 +953,7 @@ authz-no-resources=No resources
|
||||||
authz-result=Result
|
authz-result=Result
|
||||||
authz-authorization-services-enabled=Authorization Enabled
|
authz-authorization-services-enabled=Authorization Enabled
|
||||||
authz-authorization-services-enabled.tooltip=Enable/Disable fine-grained authorization support for a client
|
authz-authorization-services-enabled.tooltip=Enable/Disable fine-grained authorization support for a client
|
||||||
|
authz-required=Required
|
||||||
|
|
||||||
# Authz Settings
|
# Authz Settings
|
||||||
authz-import-config.tooltip=Import a JSON file containing authorization settings for this resource server.
|
authz-import-config.tooltip=Import a JSON file containing authorization settings for this resource server.
|
||||||
|
@ -1016,7 +1017,9 @@ authz-select-a-policy=Select a policy
|
||||||
# Authz Role Policy Detail
|
# Authz Role Policy Detail
|
||||||
authz-add-role-policy=Add Role Policy
|
authz-add-role-policy=Add Role Policy
|
||||||
authz-no-roles-assigned=No roles assigned.
|
authz-no-roles-assigned=No roles assigned.
|
||||||
authz-policy-role-roles.tooltip=Specifies which role(s) are allowed by this policy.
|
authz-policy-role-realm-roles.tooltip=Specifies which *realm* role(s) are allowed by this policy.
|
||||||
|
authz-policy-role-clients.tooltip=Selects a client in order to filter the client roles that can be applied to this policy.
|
||||||
|
authz-policy-role-client-roles.tooltip=Specifies which *client* role(s) are allowed by this policy.
|
||||||
|
|
||||||
# Authz User Policy Detail
|
# Authz User Policy Detail
|
||||||
authz-add-user-policy=Add User Policy
|
authz-add-user-policy=Add User Policy
|
||||||
|
|
|
@ -150,6 +150,14 @@ module.controller('ResourceServerResourceDetailCtrl', function($scope, $http, $r
|
||||||
client : client.id,
|
client : client.id,
|
||||||
rsrid : $route.current.params.rsrid,
|
rsrid : $route.current.params.rsrid,
|
||||||
}, function(data) {
|
}, function(data) {
|
||||||
|
if (!data.scopes) {
|
||||||
|
data.scopes = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!data.policies) {
|
||||||
|
data.policies = [];
|
||||||
|
}
|
||||||
|
|
||||||
$scope.resource = angular.copy(data);
|
$scope.resource = angular.copy(data);
|
||||||
$scope.changed = false;
|
$scope.changed = false;
|
||||||
|
|
||||||
|
@ -157,9 +165,7 @@ module.controller('ResourceServerResourceDetailCtrl', function($scope, $http, $r
|
||||||
$scope.resource.scopes[i] = $scope.resource.scopes[i].name;
|
$scope.resource.scopes[i] = $scope.resource.scopes[i].name;
|
||||||
}
|
}
|
||||||
|
|
||||||
data = angular.copy($scope.resource);
|
$scope.originalResource = angular.copy($scope.resource);
|
||||||
|
|
||||||
$scope.originalResource = data;
|
|
||||||
|
|
||||||
$scope.$watch('resource', function() {
|
$scope.$watch('resource', function() {
|
||||||
if (!angular.equals($scope.resource, data)) {
|
if (!angular.equals($scope.resource, data)) {
|
||||||
|
@ -237,6 +243,10 @@ module.controller('ResourceServerScopeCtrl', function($scope, $http, $route, $lo
|
||||||
ResourceServerScope.query({realm : realm.realm, client : client.id}, function (data) {
|
ResourceServerScope.query({realm : realm.realm, client : client.id}, function (data) {
|
||||||
$scope.scopes = data;
|
$scope.scopes = data;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$scope.createPolicy = function(scope) {
|
||||||
|
$location.path('/realms/' + $route.current.params.realm + '/clients/' + client.id + '/authz/resource-server/permission/scope/create').search({scpid: scope.id});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -534,7 +544,7 @@ module.controller('ResourceServerPolicyResourceDetailCtrl', function($scope, $ro
|
||||||
}, realm, client, $scope);
|
}, realm, client, $scope);
|
||||||
});
|
});
|
||||||
|
|
||||||
module.controller('ResourceServerPolicyScopeDetailCtrl', function($scope, $route, realm, client, PolicyController, ResourceServerPolicy, ResourceServerResource, ResourceServerScope) {
|
module.controller('ResourceServerPolicyScopeDetailCtrl', function($scope, $route, $location, realm, client, PolicyController, ResourceServerPolicy, ResourceServerResource, ResourceServerScope) {
|
||||||
PolicyController.onInit({
|
PolicyController.onInit({
|
||||||
getPolicyType : function() {
|
getPolicyType : function() {
|
||||||
return "scope";
|
return "scope";
|
||||||
|
@ -624,6 +634,12 @@ module.controller('ResourceServerPolicyScopeDetailCtrl', function($scope, $route
|
||||||
newPolicy.decisionStrategy = 'UNANIMOUS';
|
newPolicy.decisionStrategy = 'UNANIMOUS';
|
||||||
newPolicy.config = {};
|
newPolicy.config = {};
|
||||||
newPolicy.config.resources = '';
|
newPolicy.config.resources = '';
|
||||||
|
|
||||||
|
var scopeId = $location.search()['scpid'];
|
||||||
|
|
||||||
|
if (scopeId) {
|
||||||
|
newPolicy.config.scopes = [scopeId];
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
onCreate : function() {
|
onCreate : function() {
|
||||||
|
@ -712,7 +728,7 @@ module.controller('ResourceServerPolicyUserDetailCtrl', function($scope, $route,
|
||||||
}, realm, client, $scope);
|
}, realm, client, $scope);
|
||||||
});
|
});
|
||||||
|
|
||||||
module.controller('ResourceServerPolicyRoleDetailCtrl', function($scope, $route, realm, client, PolicyController, Role, RoleById) {
|
module.controller('ResourceServerPolicyRoleDetailCtrl', function($scope, $route, realm, client, Client, ClientRole, PolicyController, Role, RoleById) {
|
||||||
PolicyController.onInit({
|
PolicyController.onInit({
|
||||||
getPolicyType : function() {
|
getPolicyType : function() {
|
||||||
return "role";
|
return "role";
|
||||||
|
@ -723,6 +739,10 @@ module.controller('ResourceServerPolicyRoleDetailCtrl', function($scope, $route,
|
||||||
$scope.roles = data;
|
$scope.roles = data;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Client.query({realm: $route.current.params.realm}, function (data) {
|
||||||
|
$scope.clients = data;
|
||||||
|
});
|
||||||
|
|
||||||
$scope.selectedRoles = [];
|
$scope.selectedRoles = [];
|
||||||
|
|
||||||
$scope.selectRole = function(role) {
|
$scope.selectRole = function(role) {
|
||||||
|
@ -732,10 +752,55 @@ module.controller('ResourceServerPolicyRoleDetailCtrl', function($scope, $route,
|
||||||
|
|
||||||
$scope.selectedRole = {};
|
$scope.selectedRole = {};
|
||||||
$scope.selectedRoles.push(role);
|
$scope.selectedRoles.push(role);
|
||||||
|
|
||||||
|
var clientRoles = [];
|
||||||
|
|
||||||
|
if ($scope.clientRoles) {
|
||||||
|
for (i = 0; i < $scope.clientRoles.length; i++) {
|
||||||
|
if ($scope.clientRoles[i].id != role.id) {
|
||||||
|
clientRoles.push($scope.clientRoles[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$scope.clientRoles = clientRoles;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$scope.removeFromList = function(list, index) {
|
$scope.removeFromList = function(role) {
|
||||||
list.splice(index, 1);
|
if ($scope.clientRoles && $scope.selectedClient && $scope.selectedClient.id == role.containerId) {
|
||||||
|
$scope.clientRoles.push(role);
|
||||||
|
}
|
||||||
|
var index = $scope.selectedRoles.indexOf(role);
|
||||||
|
if (index != -1) {
|
||||||
|
$scope.selectedRoles.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.selectClient = function() {
|
||||||
|
if (!$scope.selectedClient) {
|
||||||
|
$scope.clientRoles = [];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ClientRole.query({realm: $route.current.params.realm, client: $scope.selectedClient.id}, function(data) {
|
||||||
|
var roles = [];
|
||||||
|
|
||||||
|
for (j = 0; j < data.length; j++) {
|
||||||
|
var defined = false;
|
||||||
|
|
||||||
|
for (i = 0; i < $scope.selectedRoles.length; i++) {
|
||||||
|
if ($scope.selectedRoles[i].id == data[j].id) {
|
||||||
|
defined = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!defined) {
|
||||||
|
data[j].container = {};
|
||||||
|
data[j].container.name = $scope.selectedClient.clientId;
|
||||||
|
roles.push(data[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$scope.clientRoles = roles;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -746,7 +811,18 @@ module.controller('ResourceServerPolicyRoleDetailCtrl', function($scope, $route,
|
||||||
var roles = eval(policy.config.roles);
|
var roles = eval(policy.config.roles);
|
||||||
|
|
||||||
for (i = 0; i < roles.length; i++) {
|
for (i = 0; i < roles.length; i++) {
|
||||||
RoleById.get({realm: $route.current.params.realm, role: roles[i]}, function(data) {
|
RoleById.get({realm: $route.current.params.realm, role: roles[i].id}, function(data) {
|
||||||
|
for (i = 0; i < roles.length; i++) {
|
||||||
|
if (roles[i].id == data.id) {
|
||||||
|
data.required = roles[i].required ? true : false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (i = 0; i < $scope.clients.length; i++) {
|
||||||
|
if ($scope.clients[i].id == data.containerId) {
|
||||||
|
data.container = {};
|
||||||
|
data.container.name = $scope.clients[i].clientId;
|
||||||
|
}
|
||||||
|
}
|
||||||
selectedRoles.push(data);
|
selectedRoles.push(data);
|
||||||
$scope.selectedRoles = angular.copy(selectedRoles);
|
$scope.selectedRoles = angular.copy(selectedRoles);
|
||||||
});
|
});
|
||||||
|
@ -764,7 +840,12 @@ module.controller('ResourceServerPolicyRoleDetailCtrl', function($scope, $route,
|
||||||
var roles = [];
|
var roles = [];
|
||||||
|
|
||||||
for (i = 0; i < $scope.selectedRoles.length; i++) {
|
for (i = 0; i < $scope.selectedRoles.length; i++) {
|
||||||
roles.push($scope.selectedRoles[i].id);
|
var role = {};
|
||||||
|
role.id = $scope.selectedRoles[i].id;
|
||||||
|
if ($scope.selectedRoles[i].required) {
|
||||||
|
role.required = $scope.selectedRoles[i].required;
|
||||||
|
}
|
||||||
|
roles.push(role);
|
||||||
}
|
}
|
||||||
|
|
||||||
$scope.policy.config.roles = JSON.stringify(roles);
|
$scope.policy.config.roles = JSON.stringify(roles);
|
||||||
|
@ -774,12 +855,35 @@ module.controller('ResourceServerPolicyRoleDetailCtrl', function($scope, $route,
|
||||||
var roles = [];
|
var roles = [];
|
||||||
|
|
||||||
for (i = 0; i < $scope.selectedRoles.length; i++) {
|
for (i = 0; i < $scope.selectedRoles.length; i++) {
|
||||||
roles.push($scope.selectedRoles[i].id);
|
var role = {};
|
||||||
|
role.id = $scope.selectedRoles[i].id;
|
||||||
|
if ($scope.selectedRoles[i].required) {
|
||||||
|
role.required = $scope.selectedRoles[i].required;
|
||||||
|
}
|
||||||
|
roles.push(role);
|
||||||
}
|
}
|
||||||
|
|
||||||
$scope.policy.config.roles = JSON.stringify(roles);
|
$scope.policy.config.roles = JSON.stringify(roles);
|
||||||
}
|
}
|
||||||
}, realm, client, $scope);
|
}, realm, client, $scope);
|
||||||
|
|
||||||
|
$scope.hasRealmRole = function () {
|
||||||
|
for (i = 0; i < $scope.selectedRoles.length; i++) {
|
||||||
|
if (!$scope.selectedRoles[i].clientRole) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.hasClientRole = function () {
|
||||||
|
for (i = 0; i < $scope.selectedRoles.length; i++) {
|
||||||
|
if ($scope.selectedRoles[i].clientRole) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
module.controller('ResourceServerPolicyJSDetailCtrl', function($scope, $route, $location, realm, PolicyController, client) {
|
module.controller('ResourceServerPolicyJSDetailCtrl', function($scope, $route, $location, realm, PolicyController, client) {
|
||||||
|
|
|
@ -49,31 +49,85 @@
|
||||||
<kc-tooltip>{{:: 'authz-policy-description.tooltip' | translate}}</kc-tooltip>
|
<kc-tooltip>{{:: 'authz-policy-description.tooltip' | translate}}</kc-tooltip>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group clearfix">
|
<div class="form-group clearfix">
|
||||||
<label class="col-md-2 control-label" for="roles">{{:: 'roles' | translate}} <span class="required">*</span></label>
|
<label class="col-md-2 control-label" for="roles">{{:: 'realm-roles' | translate}} <span class="required">*</span></label>
|
||||||
|
|
||||||
<div class="col-md-6">
|
<div class="col-md-4">
|
||||||
<select ui-select2="{ minimumInputLength: 1}" id="roles" data-ng-model="selectedRole" data-ng-change="selectRole(selectedRole);" data-placeholder="{{:: 'select-a-role' | translate}}..."
|
<select ui-select2="{ minimumInputLength: 1}" id="roles" data-ng-model="selectedRole" data-ng-change="selectRole(selectedRole);" data-placeholder="{{:: 'select-a-role' | translate}}..."
|
||||||
ng-options="role as role.name for role in roles" data-ng-required="selectedUsers.length == 0 && selectedRoles.length == 0">
|
ng-options="role as role.name for role in roles" data-ng-required="selectedUsers.length == 0 && selectedRoles.length == 0">
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<kc-tooltip>{{:: 'authz-policy-role-roles.tooltip' | translate}}</kc-tooltip>
|
<kc-tooltip>{{:: 'authz-policy-role-realm-roles.tooltip' | translate}}</kc-tooltip>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group clearfix" style="margin-top: -15px;">
|
<div class="form-group clearfix" style="margin-top: -15px;">
|
||||||
<label class="col-md-2 control-label"></label>
|
<label class="col-md-2 control-label"></label>
|
||||||
<div class="col-sm-3">
|
<div class="col-sm-4" data-ng-show="hasRealmRole()">
|
||||||
<table class="table table-striped table-bordered">
|
<table class="table table-striped table-bordered">
|
||||||
<thead>
|
<thead>
|
||||||
<tr data-ng-hide="!selectedRoles.length">
|
<tr>
|
||||||
<th>{{:: 'name' | translate}}</th>
|
<th class="col-sm-5">{{:: 'name' | translate}}</th>
|
||||||
|
<th>{{:: 'authz-required' | translate}}</th>
|
||||||
|
<th>{{:: 'actions' | translate}}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr ng-repeat="role in selectedRoles | orderBy:'name'" ng-if="!role.clientRole">
|
||||||
|
<td>{{role.name}}</td>
|
||||||
|
<td><input type="checkbox" ng-model="role.required" id="{{role.id}}"></td>
|
||||||
|
<td class="kc-action-cell">
|
||||||
|
<button class="btn btn-default btn-block btn-sm" ng-click="removeFromList(role);">{{:: 'remove' | translate}}</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr data-ng-show="!selectedRoles.length">
|
||||||
|
<td class="text-muted" colspan="3">{{:: 'authz-no-roles-assigned' | translate}}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group clearfix">
|
||||||
|
<label class="col-md-2 control-label" for="clients">{{:: 'clients' | translate}}</label>
|
||||||
|
|
||||||
|
<div class="col-md-4">
|
||||||
|
<select class="form-control" id="clients"
|
||||||
|
ng-model="selectedClient"
|
||||||
|
ng-change="selectClient()"
|
||||||
|
data-ng-options="current as current.clientId for current in clients">
|
||||||
|
<option value="">{{:: 'selectOne' | translate}}...</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<kc-tooltip>{{:: 'authz-policy-role-clients.tooltip' | translate}}</kc-tooltip>
|
||||||
|
</div>
|
||||||
|
<div class="form-group clearfix">
|
||||||
|
<label class="col-md-2 control-label" for="clientRoles">{{:: 'client-roles' | translate}} <span class="required">*</span></label>
|
||||||
|
|
||||||
|
<div class="col-md-4">
|
||||||
|
<select ui-select2="{ minimumInputLength: 1}" id="clientRoles" data-ng-model="selectedRole" data-ng-change="selectRole(selectedRole);" data-placeholder="{{:: 'select-a-role' | translate}}..."
|
||||||
|
ng-options="role as role.name for role in clientRoles" data-ng-required="selectedRoles.length == 0" data-ng-disabled="!selectedClient">
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<kc-tooltip>{{:: 'authz-policy-role-client-roles.tooltip' | translate}}</kc-tooltip>
|
||||||
|
</div>
|
||||||
|
<div class="form-group clearfix" style="margin-top: -15px;">
|
||||||
|
<label class="col-md-2 control-label"></label>
|
||||||
|
<div class="col-sm-4" data-ng-show="hasClientRole()">
|
||||||
|
<table class="table table-striped table-bordered">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="col-sm-5">{{:: 'name' | translate}}</th>
|
||||||
|
<th class="col-sm-5">{{:: 'client' | translate}}</th>
|
||||||
|
<th>{{:: 'authz-required' | translate}}</th>
|
||||||
<th>{{:: 'actions' | translate}}</th>
|
<th>{{:: 'actions' | translate}}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr ng-repeat="role in selectedRoles | orderBy:'name'">
|
<tr ng-repeat="role in selectedRoles | orderBy:'name'" ng-if="role.clientRole">
|
||||||
<td>{{role.name}}</td>
|
<td>{{role.name}}</td>
|
||||||
|
<td>{{role.container.name}}</td>
|
||||||
|
<td><input type="checkbox" ng-model="role.required" id="{{role.id}}"></td>
|
||||||
<td class="kc-action-cell">
|
<td class="kc-action-cell">
|
||||||
<button class="btn btn-default btn-block btn-sm" ng-click="removeFromList(selectedRoles, $index);">{{:: 'remove' | translate}}</button>
|
<button class="btn btn-default btn-block btn-sm" ng-click="removeFromList(role);">{{:: 'remove' | translate}}</button>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr data-ng-show="!selectedRoles.length">
|
<tr data-ng-show="!selectedRoles.length">
|
||||||
|
@ -98,7 +152,6 @@
|
||||||
</div>
|
</div>
|
||||||
<input type="hidden" data-ng-model="policy.type"/>
|
<input type="hidden" data-ng-model="policy.type"/>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<div class="form-group" data-ng-show="access.manageAuthorization">
|
<div class="form-group" data-ng-show="access.manageAuthorization">
|
||||||
<div class="col-md-10 col-md-offset-2">
|
<div class="col-md-10 col-md-offset-2">
|
||||||
<button kc-save data-ng-disabled="!changed">{{:: 'save' | translate}}</button>
|
<button kc-save data-ng-disabled="!changed">{{:: 'save' | translate}}</button>
|
||||||
|
|
|
@ -43,14 +43,14 @@
|
||||||
<ul>
|
<ul>
|
||||||
<li data-ng-repeat="policyResult in result.policies">
|
<li data-ng-repeat="policyResult in result.policies">
|
||||||
<strong><a
|
<strong><a
|
||||||
href="#/realms/{{realm.realm}}/authz/resource-server/{{server.id}}/policy/{{policyResult.policy.type}}/{{policyResult.policy.id}}">{{policyResult.policy.name}}</a></strong>
|
href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/permission/{{policyResult.policy.type}}/{{policyResult.policy.id}}">{{policyResult.policy.name}}</a></strong>
|
||||||
decision was <span style="color: green" data-ng-show="policyResult.status == 'PERMIT'"><strong>{{policyResult.status}}</strong></span>
|
decision was <span style="color: green" data-ng-show="policyResult.status == 'PERMIT'"><strong>{{policyResult.status}}</strong></span>
|
||||||
<span style="color: red" data-ng-hide="policyResult.status == 'PERMIT'"><strong>{{policyResult.status}}</strong></span>
|
<span style="color: red" data-ng-hide="policyResult.status == 'PERMIT'"><strong>{{policyResult.status}}</strong></span>
|
||||||
by <strong>{{policyResult.policy.decisionStrategy}}</strong> decision.</a>
|
by <strong>{{policyResult.policy.decisionStrategy}}</strong> decision.</a>
|
||||||
<ul>
|
<ul>
|
||||||
<li data-ng-repeat="subPolicy in policyResult.associatedPolicies">
|
<li data-ng-repeat="subPolicy in policyResult.associatedPolicies">
|
||||||
<strong><a
|
<strong><a
|
||||||
href="#/realms/{{realm.realm}}/authz/resource-server/{{server.id}}/policy/{{subPolicy.policy.type}}/{{subPolicy.policy.id}}">{{subPolicy.policy.name}}</a></strong>
|
href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/policy/{{subPolicy.policy.type}}/{{subPolicy.policy.id}}">{{subPolicy.policy.name}}</a></strong>
|
||||||
voted to <span style="color: green"
|
voted to <span style="color: green"
|
||||||
data-ng-show="subPolicy.status == 'PERMIT'"><strong>{{subPolicy.status}}</strong></span>
|
data-ng-show="subPolicy.status == 'PERMIT'"><strong>{{subPolicy.status}}</strong></span>
|
||||||
<span style="color: red" data-ng-hide="subPolicy.status == 'PERMIT'"><strong>{{subPolicy.status}}</strong></span>.</a>
|
<span style="color: red" data-ng-hide="subPolicy.status == 'PERMIT'"><strong>{{subPolicy.status}}</strong></span>.</a>
|
||||||
|
|
|
@ -24,11 +24,33 @@
|
||||||
</tr>
|
</tr>
|
||||||
<tr data-ng-hide="scopes.length == 0">
|
<tr data-ng-hide="scopes.length == 0">
|
||||||
<th>{{:: 'name' | translate}}</th>
|
<th>{{:: 'name' | translate}}</th>
|
||||||
|
<th>{{:: 'authz-resources' | translate}}</th>
|
||||||
|
<th>{{:: 'authz-permissions' | translate}}</th>
|
||||||
|
<th>{{:: 'actions' | translate}}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr ng-repeat="scope in scopes | filter:search | orderBy:'name'">
|
<tr ng-repeat="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><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>
|
||||||
|
<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>
|
||||||
|
<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>
|
||||||
</tr>
|
</tr>
|
||||||
<tr data-ng-show="(scopes | filter:search).length == 0">
|
<tr data-ng-show="(scopes | filter:search).length == 0">
|
||||||
<td class="text-muted" colspan="3" data-ng-show="search.name">{{:: 'no-results' | translate}}</td>
|
<td class="text-muted" colspan="3" data-ng-show="search.name">{{:: 'no-results' | translate}}</td>
|
||||||
|
|
Loading…
Reference in a new issue