keycloak-aplcache
Changes
integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ClientsResource.java 4(+4 -0)
model/jpa/src/main/java/org/keycloak/authorization/jpa/entities/ResourceServerEntity.java 22(+0 -22)
model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAResourceServerStore.java 51(+38 -13)
server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/DecisionResult.java 51(+51 -0)
server-spi-private/src/main/java/org/keycloak/authorization/store/syncronization/RealmSynchronizer.java 6(+3 -3)
services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationAuth.java 85(+58 -27)
services/src/main/java/org/keycloak/services/resources/admin/AttackDetectionResource.java 26(+17 -9)
services/src/main/java/org/keycloak/services/resources/admin/AuthenticationManagementResource.java 68(+34 -34)
services/src/main/java/org/keycloak/services/resources/admin/ClientAttributeCertificateResource.java 48(+14 -34)
services/src/main/java/org/keycloak/services/resources/admin/ClientInitialAccessResource.java 12(+6 -6)
services/src/main/java/org/keycloak/services/resources/admin/ClientRegistrationPolicyResource.java 6(+3 -3)
services/src/main/java/org/keycloak/services/resources/admin/ClientRoleMappingsResource.java 59(+28 -31)
services/src/main/java/org/keycloak/services/resources/admin/ClientTemplateResource.java 31(+12 -19)
services/src/main/java/org/keycloak/services/resources/admin/ClientTemplatesResource.java 33(+17 -16)
services/src/main/java/org/keycloak/services/resources/admin/IdentityProviderResource.java 25(+13 -12)
services/src/main/java/org/keycloak/services/resources/admin/IdentityProvidersResource.java 18(+9 -9)
services/src/main/java/org/keycloak/services/resources/admin/permissions/AdminPermissionEvaluator.java 57(+57 -0)
services/src/main/java/org/keycloak/services/resources/admin/permissions/AdminPermissionManagement.java 38(+38 -0)
services/src/main/java/org/keycloak/services/resources/admin/permissions/AdminPermissions.java 80(+80 -0)
services/src/main/java/org/keycloak/services/resources/admin/permissions/ClientPermissionEvaluator.java 84(+84 -0)
services/src/main/java/org/keycloak/services/resources/admin/permissions/ClientPermissionManagement.java 57(+57 -0)
services/src/main/java/org/keycloak/services/resources/admin/permissions/ClientPermissions.java 568(+568 -0)
services/src/main/java/org/keycloak/services/resources/admin/permissions/GroupPermissionEvaluator.java 61(+61 -0)
services/src/main/java/org/keycloak/services/resources/admin/permissions/GroupPermissionManagement.java 46(+46 -0)
services/src/main/java/org/keycloak/services/resources/admin/permissions/GroupPermissions.java 483(+483 -0)
services/src/main/java/org/keycloak/services/resources/admin/permissions/MgmtPermissions.java 364(+364 -0)
services/src/main/java/org/keycloak/services/resources/admin/permissions/RealmPermissionEvaluator.java 61(+61 -0)
services/src/main/java/org/keycloak/services/resources/admin/permissions/RealmPermissions.java 189(+189 -0)
services/src/main/java/org/keycloak/services/resources/admin/permissions/RealmsPermissionEvaluator.java 35(+35 -0)
services/src/main/java/org/keycloak/services/resources/admin/permissions/RolePermissionEvaluator.java 56(+56 -0)
services/src/main/java/org/keycloak/services/resources/admin/permissions/RolePermissionManagement.java 56(+56 -0)
services/src/main/java/org/keycloak/services/resources/admin/permissions/RolePermissions.java 585(+585 -0)
services/src/main/java/org/keycloak/services/resources/admin/permissions/UserPermissionEvaluator.java 65(+65 -0)
services/src/main/java/org/keycloak/services/resources/admin/permissions/UserPermissionManagement.java 49(+49 -0)
services/src/main/java/org/keycloak/services/resources/admin/permissions/UserPermissions.java 635(+635 -0)
services/src/main/java/org/keycloak/services/resources/admin/ProtocolMappersResource.java 60(+20 -40)
services/src/main/java/org/keycloak/services/resources/admin/RoleContainerResource.java 134(+76 -58)
services/src/main/java/org/keycloak/services/resources/admin/ScopeMappedClientResource.java 43(+15 -28)
services/src/main/java/org/keycloak/services/resources/admin/UserStorageProviderResource.java 15(+7 -8)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/AdminClientUtil.java 14(+13 -1)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractKeycloakTest.java 8(+7 -1)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ClientTest.java 8(+4 -4)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/FineGrainAdminLocalTest.java 215(+0 -215)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/FineGrainAdminUnitTest.java 705(+705 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/IllegalAdminUpgradeTest.java 648(+648 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/PermissionsTest.java 378(+211 -167)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/PolicyEvaluationCompositeRoleTest.java 4(+3 -1)
themes/src/main/resources/theme/base/admin/resources/partials/authz/mgmt/client-permissions.html 39(+39 -0)
themes/src/main/resources/theme/base/admin/resources/partials/authz/mgmt/client-role-permissions.html 40(+40 -0)
themes/src/main/resources/theme/base/admin/resources/partials/authz/mgmt/group-permissions.html 39(+39 -0)
themes/src/main/resources/theme/base/admin/resources/partials/authz/mgmt/realm-role-permissions.html 39(+39 -0)
themes/src/main/resources/theme/base/admin/resources/partials/authz/mgmt/users-permissions.html 35(+35 -0)
themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-generic.html 4(+2 -2)
themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-jwt-key-export.html 4(+2 -2)
themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-jwt-key-import.html 2(+1 -1)
themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-secret.html 4(+2 -2)
themes/src/main/resources/theme/base/admin/resources/partials/client-registration-access-token.html 4(+2 -2)
Details
diff --git a/core/src/main/java/org/keycloak/representations/idm/ClientRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/ClientRepresentation.java
index cc9e54b..a7ca83e 100755
--- a/core/src/main/java/org/keycloak/representations/idm/ClientRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/ClientRepresentation.java
@@ -65,6 +65,7 @@ public class ClientRepresentation {
private Boolean useTemplateScope;
private Boolean useTemplateMappers;
private ResourceServerRepresentation authorizationSettings;
+ private Map<String, Boolean> access;
public String getId() {
@@ -366,4 +367,12 @@ public class ClientRepresentation {
public void setAuthorizationSettings(ResourceServerRepresentation authorizationSettings) {
this.authorizationSettings = authorizationSettings;
}
+
+ public Map<String, Boolean> getAccess() {
+ return access;
+ }
+
+ public void setAccess(Map<String, Boolean> access) {
+ this.access = access;
+ }
}
diff --git a/core/src/main/java/org/keycloak/representations/idm/GroupRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/GroupRepresentation.java
index 180db64..9c96970 100755
--- a/core/src/main/java/org/keycloak/representations/idm/GroupRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/GroupRepresentation.java
@@ -34,6 +34,7 @@ public class GroupRepresentation {
protected List<String> realmRoles;
protected Map<String, List<String>> clientRoles;
protected List<GroupRepresentation> subGroups;
+ private Map<String, Boolean> access;
public String getId() {
return id;
@@ -97,4 +98,12 @@ public class GroupRepresentation {
public void setSubGroups(List<GroupRepresentation> subGroups) {
this.subGroups = subGroups;
}
+
+ public Map<String, Boolean> getAccess() {
+ return access;
+ }
+
+ public void setAccess(Map<String, Boolean> access) {
+ this.access = access;
+ }
}
diff --git a/core/src/main/java/org/keycloak/representations/idm/ManagementPermissionReference.java b/core/src/main/java/org/keycloak/representations/idm/ManagementPermissionReference.java
new file mode 100644
index 0000000..22550d6
--- /dev/null
+++ b/core/src/main/java/org/keycloak/representations/idm/ManagementPermissionReference.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.representations.idm;
+
+import java.util.Map;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class ManagementPermissionReference {
+ private boolean enabled;
+ private String resource;
+ private Map<String, String> scopePermissions;
+
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ public String getResource() {
+ return resource;
+ }
+
+ public void setResource(String resource) {
+ this.resource = resource;
+ }
+
+ public Map<String, String> getScopePermissions() {
+ return scopePermissions;
+ }
+
+ public void setScopePermissions(Map<String, String> scopePermissions) {
+ this.scopePermissions = scopePermissions;
+ }
+}
diff --git a/core/src/main/java/org/keycloak/representations/idm/UserRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/UserRepresentation.java
index 2a4ec62..4dcea95 100755
--- a/core/src/main/java/org/keycloak/representations/idm/UserRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/UserRepresentation.java
@@ -62,6 +62,7 @@ public class UserRepresentation {
protected List<SocialLinkRepresentation> socialLinks;
protected List<String> groups;
+ private Map<String, Boolean> access;
public String getSelf() {
return self;
@@ -264,4 +265,12 @@ public class UserRepresentation {
public void setDisableableCredentialTypes(Set<String> disableableCredentialTypes) {
this.disableableCredentialTypes = disableableCredentialTypes;
}
+
+ public Map<String, Boolean> getAccess() {
+ return access;
+ }
+
+ public void setAccess(Map<String, Boolean> access) {
+ this.access = access;
+ }
}
diff --git a/core/src/main/java/org/keycloak/representations/JsonWebToken.java b/core/src/main/java/org/keycloak/representations/JsonWebToken.java
index e499271..04071b6 100755
--- a/core/src/main/java/org/keycloak/representations/JsonWebToken.java
+++ b/core/src/main/java/org/keycloak/representations/JsonWebToken.java
@@ -141,6 +141,7 @@ public class JsonWebToken implements Serializable {
}
public boolean hasAudience(String audience) {
+ if (this.audience == null) return false;
for (String a : this.audience) {
if (a.equals(audience)) {
return true;
diff --git a/core/src/test/java/org/keycloak/RSAVerifierTest.java b/core/src/test/java/org/keycloak/RSAVerifierTest.java
index 58e1f38..e606e35 100755
--- a/core/src/test/java/org/keycloak/RSAVerifierTest.java
+++ b/core/src/test/java/org/keycloak/RSAVerifierTest.java
@@ -234,13 +234,16 @@ public class RSAVerifierTest {
public void testTokenAuth() throws Exception {
token = new AccessToken();
token.subject("CN=Client")
- .issuer("domain")
+ .issuer("http://localhost:8080/auth/realms/demo")
.addAccess("service").addRole("admin").verifyCaller(true);
+ token.setEmail("bill@jboss.org");
String encoded = new JWSBuilder()
.jsonContent(token)
.rsa256(idpPair.getPrivate());
+ System.out.println("token size: " + encoded.length());
+
AccessToken v = null;
try {
v = verifySkeletonKeyToken(encoded);
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ClientsResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ClientsResource.java
index 47b4db4..3c8552e 100755
--- a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ClientsResource.java
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ClientsResource.java
@@ -48,6 +48,10 @@ public interface ClientsResource {
@GET
@Produces(MediaType.APPLICATION_JSON)
+ public List<ClientRepresentation> findAll(@QueryParam("viewableOnly") boolean viewableOnly);
+
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
public List<ClientRepresentation> findByClientId(@QueryParam("clientId") String clientId);
diff --git a/model/jpa/src/main/java/org/keycloak/authorization/jpa/entities/ResourceServerEntity.java b/model/jpa/src/main/java/org/keycloak/authorization/jpa/entities/ResourceServerEntity.java
index 9ee63b6..fabdd9c 100644
--- a/model/jpa/src/main/java/org/keycloak/authorization/jpa/entities/ResourceServerEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/authorization/jpa/entities/ResourceServerEntity.java
@@ -59,12 +59,6 @@ public class ResourceServerEntity {
@Column(name = "POLICY_ENFORCE_MODE")
private PolicyEnforcementMode policyEnforcementMode = PolicyEnforcementMode.ENFORCING;
- @OneToMany(mappedBy = "resourceServer")
- private List<ResourceEntity> resources;
-
- @OneToMany (mappedBy = "resourceServer")
- private List<ScopeEntity> scopes;
-
public String getId() {
return this.id;
}
@@ -97,22 +91,6 @@ public class ResourceServerEntity {
this.policyEnforcementMode = policyEnforcementMode;
}
- public List<ResourceEntity> getResources() {
- return this.resources;
- }
-
- public void setResources(final List<ResourceEntity> resources) {
- this.resources = resources;
- }
-
- public List<ScopeEntity> getScopes() {
- return this.scopes;
- }
-
- public void setScopes(final List<ScopeEntity> scopes) {
- this.scopes = scopes;
- }
-
@Override
public boolean equals(Object o) {
if (this == o) return true;
diff --git a/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAResourceServerStore.java b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAResourceServerStore.java
index 20404e5..8eb1037 100644
--- a/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAResourceServerStore.java
+++ b/model/jpa/src/main/java/org/keycloak/authorization/jpa/store/JPAResourceServerStore.java
@@ -19,9 +19,13 @@ package org.keycloak.authorization.jpa.store;
import org.keycloak.authorization.AuthorizationProvider;
import org.keycloak.authorization.jpa.entities.PolicyEntity;
+import org.keycloak.authorization.jpa.entities.ResourceEntity;
import org.keycloak.authorization.jpa.entities.ResourceServerEntity;
+import org.keycloak.authorization.jpa.entities.ScopeEntity;
import org.keycloak.authorization.model.Policy;
+import org.keycloak.authorization.model.Resource;
import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.model.Scope;
import org.keycloak.authorization.store.ResourceServerStore;
import org.keycloak.models.utils.KeycloakModelUtils;
@@ -65,24 +69,45 @@ public class JPAResourceServerStore implements ResourceServerStore {
//entityManager.createNamedQuery("deletePolicyByResourceServer")
// .setParameter("serverId", id).executeUpdate();
- TypedQuery<String> query = entityManager.createNamedQuery("findPolicyIdByServerId", String.class);
- query.setParameter("serverId", id);
- List<String> result = query.getResultList();
- List<Policy> list = new LinkedList<>();
- for (String policyId : result) {
- entityManager.remove(entityManager.getReference(PolicyEntity.class, policyId));
+ {
+ TypedQuery<String> query = entityManager.createNamedQuery("findPolicyIdByServerId", String.class);
+ query.setParameter("serverId", id);
+ List<String> result = query.getResultList();
+ for (String policyId : result) {
+ entityManager.remove(entityManager.getReference(PolicyEntity.class, policyId));
+ }
}
- entityManager.flush();
- entityManager.createNamedQuery("deleteResourceByResourceServer")
- .setParameter("serverId", id).executeUpdate();
- entityManager.flush();
- entityManager.createNamedQuery("deleteScopeByResourceServer")
- .setParameter("serverId", id).executeUpdate();
- entityManager.flush();
+ //entityManager.createNamedQuery("deleteResourceByResourceServer")
+ // .setParameter("serverId", id).executeUpdate();
+ {
+ TypedQuery<String> query = entityManager.createNamedQuery("findResourceIdByServerId", String.class);
+
+ query.setParameter("serverId", id);
+
+ List<String> result = query.getResultList();
+ List<Resource> list = new LinkedList<>();
+ for (String resourceId : result) {
+ entityManager.remove(entityManager.getReference(ResourceEntity.class, resourceId));
+ }
+ }
+
+ //entityManager.createNamedQuery("deleteScopeByResourceServer")
+ // .setParameter("serverId", id).executeUpdate();
+ {
+ TypedQuery<String> query = entityManager.createNamedQuery("findScopeIdByResourceServer", String.class);
+
+ query.setParameter("serverId", id);
+
+ List<String> result = query.getResultList();
+ for (String scopeId : result) {
+ entityManager.remove(entityManager.getReference(ScopeEntity.class, scopeId));
+ }
+ }
this.entityManager.remove(entity);
entityManager.flush();
+ entityManager.detach(entity);
}
@Override
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProvider.java b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProvider.java
index 5c53d45..219b9a5 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProvider.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProvider.java
@@ -360,6 +360,24 @@ public class JpaRealmProvider implements RealmProvider {
return false;
}
+ GroupModel.GroupRemovedEvent event = new GroupModel.GroupRemovedEvent() {
+ @Override
+ public RealmModel getRealm() {
+ return realm;
+ }
+
+ @Override
+ public GroupModel getGroup() {
+ return group;
+ }
+
+ @Override
+ public KeycloakSession getKeycloakSession() {
+ return session;
+ }
+ };
+ session.getKeycloakSessionFactory().publish(event);
+
session.users().preRemove(realm, group);
realm.removeDefaultGroup(group);
diff --git a/server-spi/src/main/java/org/keycloak/models/GroupModel.java b/server-spi/src/main/java/org/keycloak/models/GroupModel.java
index 5a0e006..aac0ddb 100755
--- a/server-spi/src/main/java/org/keycloak/models/GroupModel.java
+++ b/server-spi/src/main/java/org/keycloak/models/GroupModel.java
@@ -17,6 +17,8 @@
package org.keycloak.models;
+import org.keycloak.provider.ProviderEvent;
+
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -26,6 +28,11 @@ import java.util.Set;
* @version $Revision: 1 $
*/
public interface GroupModel extends RoleMapperModel {
+ interface GroupRemovedEvent extends ProviderEvent {
+ RealmModel getRealm();
+ GroupModel getGroup();
+ KeycloakSession getKeycloakSession();
+ }
String getId();
String getName();
diff --git a/server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/DecisionResult.java b/server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/DecisionResult.java
new file mode 100644
index 0000000..2189c32
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/DecisionResult.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.authorization.policy.evaluation;
+
+import java.util.List;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class DecisionResult extends DecisionResultCollector {
+ protected List<Result> results;
+ protected Throwable error;
+
+ @Override
+ protected void onComplete(List<Result> results) {
+ this.results = results;
+
+ }
+
+ @Override
+ public void onError(Throwable cause) {
+ this.error = cause;
+ }
+
+ public boolean completed() {
+ return results != null && error == null;
+ }
+
+ public List<Result> getResults() {
+ return results;
+ }
+
+ public Throwable getError() {
+ return error;
+ }
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/authorization/store/syncronization/RealmSynchronizer.java b/server-spi-private/src/main/java/org/keycloak/authorization/store/syncronization/RealmSynchronizer.java
index ed24c53..971ae31 100644
--- a/server-spi-private/src/main/java/org/keycloak/authorization/store/syncronization/RealmSynchronizer.java
+++ b/server-spi-private/src/main/java/org/keycloak/authorization/store/syncronization/RealmSynchronizer.java
@@ -40,9 +40,9 @@ public class RealmSynchronizer implements Synchronizer<RealmRemovedEvent> {
if (resourceServer != null) {
String id = resourceServer.getId();
- storeFactory.getResourceStore().findByResourceServer(id).forEach(resource -> storeFactory.getResourceStore().delete(resource.getId()));
- storeFactory.getScopeStore().findByResourceServer(id).forEach(scope -> storeFactory.getScopeStore().delete(scope.getId()));
- storeFactory.getPolicyStore().findByResourceServer(id).forEach(scope -> storeFactory.getPolicyStore().delete(scope.getId()));
+ //storeFactory.getResourceStore().findByResourceServer(id).forEach(resource -> storeFactory.getResourceStore().delete(resource.getId()));
+ //storeFactory.getScopeStore().findByResourceServer(id).forEach(scope -> storeFactory.getScopeStore().delete(scope.getId()));
+ //storeFactory.getPolicyStore().findByResourceServer(id).forEach(scope -> storeFactory.getPolicyStore().delete(scope.getId()));
storeFactory.getResourceServerStore().delete(id);
}
});
diff --git a/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo3_2_0.java b/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo3_2_0.java
index 85f2296..17cd0ac 100644
--- a/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo3_2_0.java
+++ b/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo3_2_0.java
@@ -14,18 +14,27 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
package org.keycloak.migration.migrators;
-
+import org.keycloak.migration.ModelVersion;
+import org.keycloak.models.AdminRoles;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.Constants;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleModel;
import org.keycloak.migration.ModelVersion;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.PasswordPolicy;
import org.keycloak.models.RealmModel;
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
public class MigrateTo3_2_0 implements Migration {
- public static final ModelVersion VERSION = new ModelVersion("3.1.0");
+ public static final ModelVersion VERSION = new ModelVersion("3.2.0");
@Override
public void migrate(KeycloakSession session) {
@@ -34,6 +43,33 @@ public class MigrateTo3_2_0 implements Migration {
if (!builder.contains(PasswordPolicy.HASH_ALGORITHM_ID) && "20000".equals(builder.get(PasswordPolicy.HASH_ITERATIONS_ID))) {
realm.setPasswordPolicy(builder.remove(PasswordPolicy.HASH_ITERATIONS_ID).build(session));
}
+
+ ClientModel realmAccess = realm.getClientByClientId(Constants.REALM_MANAGEMENT_CLIENT_ID);
+ if (realmAccess != null) {
+ addRoles(realmAccess);
+ }
+ ClientModel masterAdminClient = realm.getMasterAdminClient();
+ if (masterAdminClient != null) {
+ addRoles(masterAdminClient);
+
+ }
+
+ }
+ }
+
+ public void addRoles(ClientModel realmAccess) {
+ RoleModel queryClients = realmAccess.addRole(AdminRoles.QUERY_CLIENTS);
+ RoleModel queryUsers = realmAccess.addRole(AdminRoles.QUERY_USERS);
+ RoleModel queryGroups = realmAccess.addRole(AdminRoles.QUERY_GROUPS);
+
+ RoleModel viewClients = realmAccess.getRole(AdminRoles.VIEW_CLIENTS);
+ if (viewClients != null) {
+ viewClients.addCompositeRole(queryClients);
+ }
+ RoleModel viewUsers = realmAccess.getRole(AdminRoles.VIEW_USERS);
+ if (viewUsers != null) {
+ viewUsers.addCompositeRole(queryUsers);
+ viewUsers.addCompositeRole(queryGroups);
}
}
@@ -41,5 +77,4 @@ public class MigrateTo3_2_0 implements Migration {
public ModelVersion getVersion() {
return VERSION;
}
-
}
diff --git a/server-spi-private/src/main/java/org/keycloak/models/AdminRoles.java b/server-spi-private/src/main/java/org/keycloak/models/AdminRoles.java
index 24455b8..c2304e8 100755
--- a/server-spi-private/src/main/java/org/keycloak/models/AdminRoles.java
+++ b/server-spi-private/src/main/java/org/keycloak/models/AdminRoles.java
@@ -17,6 +17,9 @@
package org.keycloak.models;
+import java.util.HashSet;
+import java.util.Set;
+
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
@@ -46,6 +49,21 @@ public class AdminRoles {
public static String MANAGE_EVENTS = "manage-events";
public static String MANAGE_AUTHORIZATION = "manage-authorization";
- public static String[] ALL_REALM_ROLES = {CREATE_CLIENT, VIEW_REALM, VIEW_USERS, VIEW_CLIENTS, VIEW_EVENTS, VIEW_IDENTITY_PROVIDERS, VIEW_AUTHORIZATION, MANAGE_REALM, MANAGE_USERS, MANAGE_CLIENTS, MANAGE_EVENTS, MANAGE_IDENTITY_PROVIDERS, MANAGE_AUTHORIZATION};
+ public static String QUERY_USERS = "query-users";
+ public static String QUERY_CLIENTS = "query-clients";
+ public static String QUERY_REALMS = "query-realms";
+ public static String QUERY_GROUPS = "query-groups";
+
+ public static String[] ALL_REALM_ROLES = {CREATE_CLIENT, VIEW_REALM, VIEW_USERS, VIEW_CLIENTS, VIEW_EVENTS, VIEW_IDENTITY_PROVIDERS, VIEW_AUTHORIZATION, MANAGE_REALM, MANAGE_USERS, MANAGE_CLIENTS, MANAGE_EVENTS, MANAGE_IDENTITY_PROVIDERS, MANAGE_AUTHORIZATION, QUERY_USERS, QUERY_CLIENTS, QUERY_REALMS, QUERY_GROUPS};
+ public static Set<String> ALL_ROLES = new HashSet<>();
+ static {
+ for (String name : ALL_REALM_ROLES) {
+ ALL_ROLES.add(name);
+ }
+ ALL_ROLES.add(ImpersonationConstants.IMPERSONATION_ROLE);
+ ALL_ROLES.add(ADMIN);
+ ALL_ROLES.add(CREATE_REALM);
+ ALL_ROLES.add(CREATE_CLIENT);
+ }
}
diff --git a/server-spi-private/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java b/server-spi-private/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
index bdde153..6a7aeaa 100755
--- a/server-spi-private/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
+++ b/server-spi-private/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
@@ -168,7 +168,6 @@ public class ModelToRepresentation {
return rep;
}
-
public static UserRepresentation toRepresentation(KeycloakSession session, RealmModel realm, UserModel user) {
UserRepresentation rep = new UserRepresentation();
rep.setId(user.getId());
diff --git a/services/src/main/java/org/keycloak/authorization/admin/AuthorizationService.java b/services/src/main/java/org/keycloak/authorization/admin/AuthorizationService.java
index bc9dd1f..535634b 100644
--- a/services/src/main/java/org/keycloak/authorization/admin/AuthorizationService.java
+++ b/services/src/main/java/org/keycloak/authorization/admin/AuthorizationService.java
@@ -23,8 +23,8 @@ import org.keycloak.authorization.AuthorizationProvider;
import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
+import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
import org.keycloak.services.resources.admin.AdminEventBuilder;
-import org.keycloak.services.resources.admin.RealmAuth;
import javax.ws.rs.Path;
@@ -33,22 +33,20 @@ import javax.ws.rs.Path;
*/
public class AuthorizationService {
- private final RealmAuth auth;
+ private final AdminPermissionEvaluator auth;
private final ClientModel client;
+ private final KeycloakSession session;
private final ResourceServer resourceServer;
private final AuthorizationProvider authorization;
private final AdminEventBuilder adminEvent;
- public AuthorizationService(KeycloakSession session, ClientModel client, RealmAuth auth, AdminEventBuilder adminEvent) {
+ public AuthorizationService(KeycloakSession session, ClientModel client, AdminPermissionEvaluator auth, AdminEventBuilder adminEvent) {
+ this.session = session;
this.client = client;
this.authorization = session.getProvider(AuthorizationProvider.class);
this.adminEvent = adminEvent;
this.resourceServer = this.authorization.getStoreFactory().getResourceServerStore().findByClient(this.client.getId());
this.auth = auth;
-
- if (auth != null) {
- this.auth.init(RealmAuth.Resource.AUTHORIZATION);
- }
}
@Path("/resource-server")
diff --git a/services/src/main/java/org/keycloak/authorization/admin/PermissionService.java b/services/src/main/java/org/keycloak/authorization/admin/PermissionService.java
index ea4677d..6973b4d 100644
--- a/services/src/main/java/org/keycloak/authorization/admin/PermissionService.java
+++ b/services/src/main/java/org/keycloak/authorization/admin/PermissionService.java
@@ -22,15 +22,15 @@ import java.util.Map;
import org.keycloak.authorization.AuthorizationProvider;
import org.keycloak.authorization.model.Policy;
import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
import org.keycloak.services.resources.admin.AdminEventBuilder;
-import org.keycloak.services.resources.admin.RealmAuth;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public class PermissionService extends PolicyService {
- public PermissionService(ResourceServer resourceServer, AuthorizationProvider authorization, RealmAuth auth, AdminEventBuilder adminEvent) {
+ public PermissionService(ResourceServer resourceServer, AuthorizationProvider authorization, AdminPermissionEvaluator auth, AdminEventBuilder adminEvent) {
super(resourceServer, authorization, auth, adminEvent);
}
diff --git a/services/src/main/java/org/keycloak/authorization/admin/PolicyEvaluationService.java b/services/src/main/java/org/keycloak/authorization/admin/PolicyEvaluationService.java
index 5d9114f..ecebaae 100644
--- a/services/src/main/java/org/keycloak/authorization/admin/PolicyEvaluationService.java
+++ b/services/src/main/java/org/keycloak/authorization/admin/PolicyEvaluationService.java
@@ -71,7 +71,7 @@ import org.keycloak.representations.idm.authorization.ScopeRepresentation;
import org.keycloak.services.ErrorResponseException;
import org.keycloak.services.Urls;
import org.keycloak.services.managers.AuthenticationManager;
-import org.keycloak.services.resources.admin.RealmAuth;
+import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
import org.keycloak.sessions.AuthenticationSessionModel;
/**
@@ -80,13 +80,13 @@ import org.keycloak.sessions.AuthenticationSessionModel;
public class PolicyEvaluationService {
private final AuthorizationProvider authorization;
- private final RealmAuth auth;
+ private final AdminPermissionEvaluator auth;
@Context
private HttpRequest httpRequest;
private final ResourceServer resourceServer;
- PolicyEvaluationService(ResourceServer resourceServer, AuthorizationProvider authorization, RealmAuth auth) {
+ PolicyEvaluationService(ResourceServer resourceServer, AuthorizationProvider authorization, AdminPermissionEvaluator auth) {
this.resourceServer = resourceServer;
this.authorization = authorization;
this.auth = auth;
@@ -118,7 +118,7 @@ public class PolicyEvaluationService {
@Consumes("application/json")
@Produces("application/json")
public Response evaluate(PolicyEvaluationRequest evaluationRequest) throws Throwable {
- this.auth.requireView();
+ this.auth.realm().requireViewAuthorization();
CloseableKeycloakIdentity identity = createIdentity(evaluationRequest);
try {
return Response.ok(PolicyEvaluationResponseBuilder.build(evaluate(evaluationRequest, createEvaluationContext(evaluationRequest, identity)), resourceServer, authorization, identity)).build();
diff --git a/services/src/main/java/org/keycloak/authorization/admin/PolicyResourceService.java b/services/src/main/java/org/keycloak/authorization/admin/PolicyResourceService.java
index c0728bc..b32899f 100644
--- a/services/src/main/java/org/keycloak/authorization/admin/PolicyResourceService.java
+++ b/services/src/main/java/org/keycloak/authorization/admin/PolicyResourceService.java
@@ -46,8 +46,8 @@ import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentati
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
+import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
import org.keycloak.services.resources.admin.AdminEventBuilder;
-import org.keycloak.services.resources.admin.RealmAuth;
import org.keycloak.util.JsonSerialization;
/**
@@ -58,10 +58,10 @@ public class PolicyResourceService {
private final Policy policy;
protected final ResourceServer resourceServer;
protected final AuthorizationProvider authorization;
- protected final RealmAuth auth;
+ protected final AdminPermissionEvaluator auth;
private final AdminEventBuilder adminEvent;
- public PolicyResourceService(Policy policy, ResourceServer resourceServer, AuthorizationProvider authorization, RealmAuth auth, AdminEventBuilder adminEvent) {
+ public PolicyResourceService(Policy policy, ResourceServer resourceServer, AuthorizationProvider authorization, AdminPermissionEvaluator auth, AdminEventBuilder adminEvent) {
this.policy = policy;
this.resourceServer = resourceServer;
this.authorization = authorization;
@@ -73,8 +73,8 @@ public class PolicyResourceService {
@Consumes("application/json")
@Produces("application/json")
@NoCache
- public Response update(@Context UriInfo uriInfo, String payload) {
- this.auth.requireManage();
+ public Response update(@Context UriInfo uriInfo,String payload) {
+ this.auth.realm().requireManageAuthorization();
AbstractPolicyRepresentation representation = doCreateRepresentation(payload);
@@ -94,7 +94,7 @@ public class PolicyResourceService {
@DELETE
public Response delete(@Context UriInfo uriInfo) {
- this.auth.requireManage();
+ this.auth.realm().requireManageAuthorization();
if (policy == null) {
return Response.status(Status.NOT_FOUND).build();
@@ -119,7 +119,7 @@ public class PolicyResourceService {
@Produces("application/json")
@NoCache
public Response findById() {
- this.auth.requireView();
+ this.auth.realm().requireViewAuthorization();
if (policy == null) {
return Response.status(Status.NOT_FOUND).build();
@@ -137,7 +137,7 @@ public class PolicyResourceService {
@Produces("application/json")
@NoCache
public Response getDependentPolicies() {
- this.auth.requireView();
+ this.auth.realm().requireViewAuthorization();
if (policy == null) {
return Response.status(Status.NOT_FOUND).build();
@@ -161,7 +161,7 @@ public class PolicyResourceService {
@Produces("application/json")
@NoCache
public Response getScopes() {
- this.auth.requireView();
+ this.auth.realm().requireViewAuthorization();
if (policy == null) {
return Response.status(Status.NOT_FOUND).build();
@@ -182,7 +182,7 @@ public class PolicyResourceService {
@Produces("application/json")
@NoCache
public Response getResources() {
- this.auth.requireView();
+ this.auth.realm().requireViewAuthorization();
if (policy == null) {
return Response.status(Status.NOT_FOUND).build();
@@ -203,7 +203,7 @@ public class PolicyResourceService {
@Produces("application/json")
@NoCache
public Response getAssociatedPolicies() {
- this.auth.requireView();
+ this.auth.realm().requireViewAuthorization();
if (policy == null) {
return Response.status(Status.NOT_FOUND).build();
diff --git a/services/src/main/java/org/keycloak/authorization/admin/PolicyService.java b/services/src/main/java/org/keycloak/authorization/admin/PolicyService.java
index e8d4aa8..33e6299 100644
--- a/services/src/main/java/org/keycloak/authorization/admin/PolicyService.java
+++ b/services/src/main/java/org/keycloak/authorization/admin/PolicyService.java
@@ -57,8 +57,8 @@ import org.keycloak.representations.idm.authorization.PolicyProviderRepresentati
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
import org.keycloak.services.ErrorResponseException;
+import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
import org.keycloak.services.resources.admin.AdminEventBuilder;
-import org.keycloak.services.resources.admin.RealmAuth;
import org.keycloak.util.JsonSerialization;
/**
@@ -68,10 +68,10 @@ public class PolicyService {
protected final ResourceServer resourceServer;
protected final AuthorizationProvider authorization;
- protected final RealmAuth auth;
+ protected final AdminPermissionEvaluator auth;
protected final AdminEventBuilder adminEvent;
- public PolicyService(ResourceServer resourceServer, AuthorizationProvider authorization, RealmAuth auth, AdminEventBuilder adminEvent) {
+ public PolicyService(ResourceServer resourceServer, AuthorizationProvider authorization, AdminPermissionEvaluator auth, AdminEventBuilder adminEvent) {
this.resourceServer = resourceServer;
this.authorization = authorization;
this.auth = auth;
@@ -103,8 +103,8 @@ public class PolicyService {
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@NoCache
- public Response create(@Context UriInfo uriInfo, String payload) {
- this.auth.requireManage();
+ public Response create(@Context UriInfo uriInfo, String payload) {
+ this.auth.realm().requireManageAuthorization();
AbstractPolicyRepresentation representation = doCreateRepresentation(payload);
Policy policy = create(representation);
@@ -144,7 +144,7 @@ public class PolicyService {
@Produces(MediaType.APPLICATION_JSON)
@NoCache
public Response findByName(@QueryParam("name") String name) {
- this.auth.requireView();
+ this.auth.realm().requireViewAuthorization();
StoreFactory storeFactory = authorization.getStoreFactory();
if (name == null) {
@@ -171,7 +171,7 @@ public class PolicyService {
@QueryParam("permission") Boolean permission,
@QueryParam("first") Integer firstResult,
@QueryParam("max") Integer maxResult) {
- this.auth.requireView();
+ this.auth.realm().requireViewAuthorization();
Map<String, String[]> search = new HashMap<>();
@@ -250,7 +250,7 @@ public class PolicyService {
@Produces(MediaType.APPLICATION_JSON)
@NoCache
public Response findPolicyProviders() {
- this.auth.requireView();
+ this.auth.realm().requireViewAuthorization();
return Response.ok(
authorization.getProviderFactories().stream()
.map(provider -> {
@@ -268,7 +268,7 @@ public class PolicyService {
@Path("evaluate")
public PolicyEvaluationService getPolicyEvaluateResource() {
- this.auth.requireView();
+ this.auth.realm().requireViewAuthorization();
PolicyEvaluationService resource = new PolicyEvaluationService(this.resourceServer, this.authorization, this.auth);
ResteasyProviderFactory.getInstance().injectProperties(resource);
diff --git a/services/src/main/java/org/keycloak/authorization/admin/PolicyTypeResourceService.java b/services/src/main/java/org/keycloak/authorization/admin/PolicyTypeResourceService.java
index 9044c0d..1297b6a 100644
--- a/services/src/main/java/org/keycloak/authorization/admin/PolicyTypeResourceService.java
+++ b/services/src/main/java/org/keycloak/authorization/admin/PolicyTypeResourceService.java
@@ -24,8 +24,8 @@ import org.keycloak.authorization.model.ResourceServer;
import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
import org.keycloak.models.utils.ModelToRepresentation;
import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentation;
+import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
import org.keycloak.services.resources.admin.AdminEventBuilder;
-import org.keycloak.services.resources.admin.RealmAuth;
import org.keycloak.util.JsonSerialization;
/**
@@ -33,7 +33,7 @@ import org.keycloak.util.JsonSerialization;
*/
public class PolicyTypeResourceService extends PolicyResourceService {
- public PolicyTypeResourceService(Policy policy, ResourceServer resourceServer, AuthorizationProvider authorization, RealmAuth auth, AdminEventBuilder adminEvent) {
+ public PolicyTypeResourceService(Policy policy, ResourceServer resourceServer, AuthorizationProvider authorization, AdminPermissionEvaluator auth, AdminEventBuilder adminEvent) {
super(policy, resourceServer, authorization, auth, adminEvent);
}
diff --git a/services/src/main/java/org/keycloak/authorization/admin/PolicyTypeService.java b/services/src/main/java/org/keycloak/authorization/admin/PolicyTypeService.java
index 6f37f6c..4b41e94 100644
--- a/services/src/main/java/org/keycloak/authorization/admin/PolicyTypeService.java
+++ b/services/src/main/java/org/keycloak/authorization/admin/PolicyTypeService.java
@@ -30,8 +30,8 @@ import org.keycloak.authorization.policy.provider.PolicyProviderAdminService;
import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
import org.keycloak.models.utils.ModelToRepresentation;
import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentation;
+import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
import org.keycloak.services.resources.admin.AdminEventBuilder;
-import org.keycloak.services.resources.admin.RealmAuth;
import org.keycloak.util.JsonSerialization;
/**
@@ -41,7 +41,7 @@ public class PolicyTypeService extends PolicyService {
private final String type;
- PolicyTypeService(String type, ResourceServer resourceServer, AuthorizationProvider authorization, RealmAuth auth, AdminEventBuilder adminEvent) {
+ PolicyTypeService(String type, ResourceServer resourceServer, AuthorizationProvider authorization, AdminPermissionEvaluator auth, AdminEventBuilder adminEvent) {
super(resourceServer, authorization, auth, adminEvent);
this.type = type;
}
diff --git a/services/src/main/java/org/keycloak/authorization/admin/ResourceServerService.java b/services/src/main/java/org/keycloak/authorization/admin/ResourceServerService.java
index 777d843..e52da9a 100644
--- a/services/src/main/java/org/keycloak/authorization/admin/ResourceServerService.java
+++ b/services/src/main/java/org/keycloak/authorization/admin/ResourceServerService.java
@@ -56,8 +56,8 @@ import org.keycloak.representations.idm.authorization.PolicyRepresentation;
import org.keycloak.representations.idm.authorization.ResourcePermissionRepresentation;
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
import org.keycloak.representations.idm.authorization.ResourceServerRepresentation;
+import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
import org.keycloak.services.resources.admin.AdminEventBuilder;
-import org.keycloak.services.resources.admin.RealmAuth;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
@@ -65,7 +65,7 @@ import org.keycloak.services.resources.admin.RealmAuth;
public class ResourceServerService {
private final AuthorizationProvider authorization;
- private final RealmAuth auth;
+ private final AdminPermissionEvaluator auth;
private final AdminEventBuilder adminEvent;
private final KeycloakSession session;
private ResourceServer resourceServer;
@@ -74,7 +74,7 @@ public class ResourceServerService {
@Context
private UriInfo uriInfo;
- public ResourceServerService(AuthorizationProvider authorization, ResourceServer resourceServer, ClientModel client, RealmAuth auth, AdminEventBuilder adminEvent) {
+ public ResourceServerService(AuthorizationProvider authorization, ResourceServer resourceServer, ClientModel client, AdminPermissionEvaluator auth, AdminEventBuilder adminEvent) {
this.authorization = authorization;
this.session = authorization.getKeycloakSession();
this.client = client;
@@ -84,7 +84,7 @@ public class ResourceServerService {
}
public void create(boolean newClient) {
- this.auth.requireManage();
+ this.auth.realm().requireManageAuthorization();
UserModel serviceAccount = this.session.users().getServiceAccount(client);
@@ -101,8 +101,8 @@ public class ResourceServerService {
@PUT
@Consumes("application/json")
@Produces("application/json")
- public Response update(@Context UriInfo uriInfo, ResourceServerRepresentation server) {
- this.auth.requireManage();
+ public Response update(ResourceServerRepresentation server) {
+ this.auth.realm().requireManageAuthorization();
this.resourceServer.setAllowRemoteResourceManagement(server.isAllowRemoteResourceManagement());
this.resourceServer.setPolicyEnforcementMode(server.getPolicyEnforcementMode());
audit(OperationType.UPDATE, uriInfo, false);
@@ -110,7 +110,7 @@ public class ResourceServerService {
}
public void delete() {
- this.auth.requireManage();
+ this.auth.realm().requireManageAuthorization();
StoreFactory storeFactory = authorization.getStoreFactory();
ResourceStore resourceStore = storeFactory.getResourceStore();
String id = resourceServer.getId();
@@ -133,7 +133,7 @@ public class ResourceServerService {
@GET
@Produces("application/json")
public Response findById() {
- this.auth.requireView();
+ this.auth.realm().requireViewAuthorization();
return Response.ok(toRepresentation(this.resourceServer, this.client)).build();
}
@@ -141,7 +141,7 @@ public class ResourceServerService {
@GET
@Produces("application/json")
public Response exportSettings() {
- this.auth.requireManage();
+ this.auth.realm().requireManageAuthorization();
return Response.ok(ExportUtils.exportAuthorizationSettings(session, client)).build();
}
@@ -149,7 +149,7 @@ public class ResourceServerService {
@POST
@Consumes(MediaType.APPLICATION_JSON)
public Response importSettings(@Context final UriInfo uriInfo, ResourceServerRepresentation rep) throws IOException {
- this.auth.requireManage();
+ this.auth.realm().requireManageAuthorization();
rep.setClientId(client.getId());
@@ -189,7 +189,7 @@ public class ResourceServerService {
@Path("/permission")
public Object getPermissionTypeResource() {
- this.auth.requireView();
+ this.auth.realm().requireViewAuthorization();
PermissionService resource = new PermissionService(this.resourceServer, this.authorization, this.auth, adminEvent);
ResteasyProviderFactory.getInstance().injectProperties(resource);
diff --git a/services/src/main/java/org/keycloak/authorization/admin/ResourceSetService.java b/services/src/main/java/org/keycloak/authorization/admin/ResourceSetService.java
index 3c7f34f..7c95281 100644
--- a/services/src/main/java/org/keycloak/authorization/admin/ResourceSetService.java
+++ b/services/src/main/java/org/keycloak/authorization/admin/ResourceSetService.java
@@ -39,8 +39,8 @@ import org.keycloak.representations.idm.authorization.ResourceOwnerRepresentatio
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
import org.keycloak.services.ErrorResponse;
+import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
import org.keycloak.services.resources.admin.AdminEventBuilder;
-import org.keycloak.services.resources.admin.RealmAuth;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
@@ -74,11 +74,11 @@ import static org.keycloak.models.utils.RepresentationToModel.toModel;
public class ResourceSetService {
private final AuthorizationProvider authorization;
- private final RealmAuth auth;
+ private final AdminPermissionEvaluator auth;
private final AdminEventBuilder adminEvent;
private ResourceServer resourceServer;
- public ResourceSetService(ResourceServer resourceServer, AuthorizationProvider authorization, RealmAuth auth, AdminEventBuilder adminEvent) {
+ public ResourceSetService(ResourceServer resourceServer, AuthorizationProvider authorization, AdminPermissionEvaluator auth, AdminEventBuilder adminEvent) {
this.resourceServer = resourceServer;
this.authorization = authorization;
this.auth = auth;
@@ -292,7 +292,7 @@ public class ResourceSetService {
@NoCache
@Produces("application/json")
public Response find(@QueryParam("name") String name) {
- this.auth.requireView();
+ this.auth.realm().requireViewAuthorization();
StoreFactory storeFactory = authorization.getStoreFactory();
if (name == null) {
@@ -387,13 +387,13 @@ public class ResourceSetService {
private void requireView() {
if (this.auth != null) {
- this.auth.requireView();
+ this.auth.realm().requireViewAuthorization();
}
}
private void requireManage() {
if (this.auth != null) {
- this.auth.requireManage();
+ this.auth.realm().requireManageAuthorization();
}
}
diff --git a/services/src/main/java/org/keycloak/authorization/admin/ScopeService.java b/services/src/main/java/org/keycloak/authorization/admin/ScopeService.java
index a2a2320..1ab3546 100644
--- a/services/src/main/java/org/keycloak/authorization/admin/ScopeService.java
+++ b/services/src/main/java/org/keycloak/authorization/admin/ScopeService.java
@@ -32,8 +32,8 @@ import org.keycloak.representations.idm.authorization.PolicyRepresentation;
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
import org.keycloak.services.ErrorResponse;
+import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
import org.keycloak.services.resources.admin.AdminEventBuilder;
-import org.keycloak.services.resources.admin.RealmAuth;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
@@ -65,11 +65,11 @@ import static org.keycloak.models.utils.RepresentationToModel.toModel;
public class ScopeService {
private final AuthorizationProvider authorization;
- private final RealmAuth auth;
+ private final AdminPermissionEvaluator auth;
private final AdminEventBuilder adminEvent;
private ResourceServer resourceServer;
- public ScopeService(ResourceServer resourceServer, AuthorizationProvider authorization, RealmAuth auth, AdminEventBuilder adminEvent) {
+ public ScopeService(ResourceServer resourceServer, AuthorizationProvider authorization, AdminPermissionEvaluator auth, AdminEventBuilder adminEvent) {
this.resourceServer = resourceServer;
this.authorization = authorization;
this.auth = auth;
@@ -81,7 +81,7 @@ public class ScopeService {
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response create(@Context UriInfo uriInfo, ScopeRepresentation scope) {
- this.auth.requireManage();
+ this.auth.realm().requireManageAuthorization();
Scope model = toModel(scope, this.resourceServer, authorization);
scope.setId(model.getId());
@@ -96,7 +96,7 @@ public class ScopeService {
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response update(@Context UriInfo uriInfo, @PathParam("id") String id, ScopeRepresentation scope) {
- this.auth.requireManage();
+ this.auth.realm().requireManageAuthorization();
scope.setId(id);
StoreFactory storeFactory = authorization.getStoreFactory();
Scope model = storeFactory.getScopeStore().findById(scope.getId(), resourceServer.getId());
@@ -115,7 +115,7 @@ public class ScopeService {
@Path("{id}")
@DELETE
public Response delete(@Context UriInfo uriInfo, @PathParam("id") String id) {
- this.auth.requireManage();
+ this.auth.realm().requireManageAuthorization();
StoreFactory storeFactory = authorization.getStoreFactory();
List<Resource> resources = storeFactory.getResourceStore().findByScope(Arrays.asList(id), resourceServer.getId());
@@ -154,7 +154,7 @@ public class ScopeService {
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public Response findById(@PathParam("id") String id) {
- this.auth.requireView();
+ this.auth.realm().requireViewAuthorization();
Scope model = this.authorization.getStoreFactory().getScopeStore().findById(id, resourceServer.getId());
if (model == null) {
@@ -169,7 +169,7 @@ public class ScopeService {
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public Response getResources(@PathParam("id") String id) {
- this.auth.requireView();
+ this.auth.realm().requireViewAuthorization();
StoreFactory storeFactory = this.authorization.getStoreFactory();
Scope model = storeFactory.getScopeStore().findById(id, resourceServer.getId());
@@ -192,7 +192,7 @@ public class ScopeService {
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public Response getPermissions(@PathParam("id") String id) {
- this.auth.requireView();
+ this.auth.realm().requireViewAuthorization();
StoreFactory storeFactory = this.authorization.getStoreFactory();
Scope model = storeFactory.getScopeStore().findById(id, resourceServer.getId());
@@ -218,7 +218,7 @@ public class ScopeService {
@Produces(MediaType.APPLICATION_JSON)
@NoCache
public Response find(@QueryParam("name") String name) {
- this.auth.requireView();
+ this.auth.realm().requireViewAuthorization();
StoreFactory storeFactory = authorization.getStoreFactory();
if (name == null) {
@@ -241,7 +241,7 @@ public class ScopeService {
@QueryParam("name") String name,
@QueryParam("first") Integer firstResult,
@QueryParam("max") Integer maxResult) {
- this.auth.requireView();
+ this.auth.realm().requireViewAuthorization();
Map<String, String[]> search = new HashMap<>();
diff --git a/services/src/main/java/org/keycloak/authorization/common/KeycloakEvaluationContext.java b/services/src/main/java/org/keycloak/authorization/common/KeycloakEvaluationContext.java
index fc929ec..da3cb0f 100644
--- a/services/src/main/java/org/keycloak/authorization/common/KeycloakEvaluationContext.java
+++ b/services/src/main/java/org/keycloak/authorization/common/KeycloakEvaluationContext.java
@@ -18,34 +18,28 @@
package org.keycloak.authorization.common;
-import org.keycloak.authorization.attribute.Attributes;
import org.keycloak.authorization.identity.Identity;
-import org.keycloak.authorization.policy.evaluation.EvaluationContext;
import org.keycloak.models.KeycloakSession;
import org.keycloak.representations.AccessToken;
-import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Collection;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.List;
+import java.util.Map;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
-public class KeycloakEvaluationContext implements EvaluationContext {
+public class KeycloakEvaluationContext extends DefaultEvaluationContext {
private final KeycloakIdentity identity;
- private final KeycloakSession keycloakSession;
public KeycloakEvaluationContext(KeycloakSession keycloakSession) {
this(new KeycloakIdentity(keycloakSession), keycloakSession);
}
public KeycloakEvaluationContext(KeycloakIdentity identity, KeycloakSession keycloakSession) {
+ super(identity, keycloakSession);
this.identity = identity;
- this.keycloakSession = keycloakSession;
}
@Override
@@ -54,27 +48,13 @@ public class KeycloakEvaluationContext implements EvaluationContext {
}
@Override
- public Attributes getAttributes() {
- HashMap<String, Collection<String>> attributes = new HashMap<>();
-
- attributes.put("kc.time.date_time", Arrays.asList(new SimpleDateFormat("MM/dd/yyyy hh:mm:ss").format(new Date())));
- attributes.put("kc.client.network.ip_address", Arrays.asList(this.keycloakSession.getContext().getConnection().getRemoteAddr()));
- attributes.put("kc.client.network.host", Arrays.asList(this.keycloakSession.getContext().getConnection().getRemoteHost()));
-
+ public Map<String, Collection<String>> getBaseAttributes() {
+ Map<String, Collection<String>> attributes = super.getBaseAttributes();
AccessToken accessToken = this.identity.getAccessToken();
if (accessToken != null) {
attributes.put("kc.client.id", Arrays.asList(accessToken.getIssuedFor()));
}
-
- List<String> userAgents = this.keycloakSession.getContext().getRequestHeaders().getRequestHeader("User-Agent");
-
- if (userAgents != null) {
- attributes.put("kc.client.user_agent", userAgents);
- }
-
- attributes.put("kc.realm.name", Arrays.asList(this.keycloakSession.getContext().getRealm().getName()));
-
- return Attributes.from(attributes);
+ return attributes;
}
}
diff --git a/services/src/main/java/org/keycloak/authorization/common/KeycloakIdentity.java b/services/src/main/java/org/keycloak/authorization/common/KeycloakIdentity.java
index 9ea53e2..59963ba 100644
--- a/services/src/main/java/org/keycloak/authorization/common/KeycloakIdentity.java
+++ b/services/src/main/java/org/keycloak/authorization/common/KeycloakIdentity.java
@@ -53,6 +53,74 @@ public class KeycloakIdentity implements Identity {
this(Tokens.getAccessToken(keycloakSession), keycloakSession);
}
+ public KeycloakIdentity(KeycloakSession keycloakSession, AccessToken accessToken) {
+ this(accessToken, keycloakSession, keycloakSession.getContext().getRealm());
+ }
+
+ public KeycloakIdentity(AccessToken accessToken, KeycloakSession keycloakSession, RealmModel realm) {
+ if (accessToken == null) {
+ 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);
+ }
+ if (realm == null) {
+ throw new ErrorResponseException("no_keycloak_session", "No realm set", Status.FORBIDDEN);
+ }
+ this.accessToken = accessToken;
+ this.keycloakSession = keycloakSession;
+ this.realm = realm;
+
+ Map<String, Collection<String>> attributes = new HashMap<>();
+
+ try {
+ ObjectNode objectNode = JsonSerialization.createObjectNode(this.accessToken);
+ Iterator<String> iterator = objectNode.fieldNames();
+
+ while (iterator.hasNext()) {
+ String fieldName = iterator.next();
+ JsonNode fieldValue = objectNode.get(fieldName);
+ List<String> values = new ArrayList<>();
+
+ if (fieldValue.isArray()) {
+ Iterator<JsonNode> valueIterator = fieldValue.iterator();
+
+ while (valueIterator.hasNext()) {
+ values.add(valueIterator.next().asText());
+ }
+ } else {
+ String value = fieldValue.asText();
+
+ if (StringUtil.isNullOrEmpty(value)) {
+ continue;
+ }
+
+ values.add(value);
+ }
+
+ if (!values.isEmpty()) {
+ attributes.put(fieldName, values);
+ }
+ }
+
+ 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) {
+ throw new RuntimeException("Error while reading attributes from security token.", e);
+ }
+
+ this.attributes = Attributes.from(attributes);
+ }
+
public KeycloakIdentity(AccessToken accessToken, KeycloakSession keycloakSession) {
if (accessToken == null) {
throw new ErrorResponseException("invalid_bearer_token", "Could not obtain bearer access_token from request.", Status.FORBIDDEN);
diff --git a/services/src/main/java/org/keycloak/authorization/common/UserModelIdentity.java b/services/src/main/java/org/keycloak/authorization/common/UserModelIdentity.java
index 67e6bb4..c54e4c0 100644
--- a/services/src/main/java/org/keycloak/authorization/common/UserModelIdentity.java
+++ b/services/src/main/java/org/keycloak/authorization/common/UserModelIdentity.java
@@ -34,7 +34,8 @@ public class UserModelIdentity implements Identity {
protected RealmModel realm;
protected UserModel user;
- public UserModelIdentity(UserModel user) {
+ public UserModelIdentity(RealmModel realm, UserModel user) {
+ this.realm = realm;
this.user = user;
}
diff --git a/services/src/main/java/org/keycloak/authorization/util/Permissions.java b/services/src/main/java/org/keycloak/authorization/util/Permissions.java
index 325c056..b0e5daa 100644
--- a/services/src/main/java/org/keycloak/authorization/util/Permissions.java
+++ b/services/src/main/java/org/keycloak/authorization/util/Permissions.java
@@ -19,6 +19,8 @@
package org.keycloak.authorization.util;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
@@ -48,6 +50,10 @@ import org.keycloak.representations.idm.authorization.Permission;
*/
public final class Permissions {
+ public static List<ResourcePermission> permission(ResourceServer server, Resource resource, Scope scope) {
+ return Arrays.asList(new ResourcePermission(resource, Arrays.asList(scope), server));
+ }
+
/**
* Returns a list of permissions for all resources and scopes that belong to the given <code>resourceServer</code> and
* <code>identity</code>.
diff --git a/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationAuth.java b/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationAuth.java
index 9b2b284..dfed5aa 100644
--- a/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationAuth.java
+++ b/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationAuth.java
@@ -31,12 +31,15 @@ import org.keycloak.models.ClientModel;
import org.keycloak.models.Constants;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleModel;
+import org.keycloak.models.UserModel;
import org.keycloak.protocol.oidc.utils.AuthorizeClientUtil;
import org.keycloak.representations.JsonWebToken;
import org.keycloak.services.ErrorResponseException;
import org.keycloak.services.clientregistration.policy.RegistrationAuth;
import org.keycloak.services.clientregistration.policy.ClientRegistrationPolicyException;
import org.keycloak.services.clientregistration.policy.ClientRegistrationPolicyManager;
+import org.keycloak.services.managers.RealmManager;
import org.keycloak.util.TokenUtil;
import javax.ws.rs.core.HttpHeaders;
@@ -231,42 +234,70 @@ public class ClientRegistrationAuth {
return initialAccessModel;
}
- private boolean hasRole(String... role) {
+ private boolean hasRole(String... roles) {
try {
- Map<String, Object> otherClaims = jwt.getOtherClaims();
- if (otherClaims != null) {
- Map<String, Map<String, List<String>>> resourceAccess = (Map<String, Map<String, List<String>>>) jwt.getOtherClaims().get("resource_access");
- if (resourceAccess == null) {
- return false;
- }
+ if (jwt.getIssuedFor().equals(Constants.ADMIN_CLI_CLIENT_ID)
+ || jwt.getIssuedFor().equals(Constants.ADMIN_CONSOLE_CLIENT_ID)) {
+ return hasRoleInModel(roles);
- List<String> roles = null;
+ } else {
+ return hasRoleInToken(roles);
+ }
+ } catch (Throwable t) {
+ return false;
+ }
+ }
- Map<String, List<String>> map;
- if (realm.getName().equals(Config.getAdminRealm())) {
- map = resourceAccess.get(realm.getMasterAdminClient().getClientId());
- } else {
- map = resourceAccess.get(Constants.REALM_MANAGEMENT_CLIENT_ID);
- }
+ private boolean hasRoleInModel(String[] roles) {
+ ClientModel roleNamespace;
+ UserModel user = session.users().getUserById(jwt.getSubject(), realm);
+ if (user == null) {
+ return false;
+ }
+ if (realm.getName().equals(Config.getAdminRealm())) {
+ roleNamespace = realm.getMasterAdminClient();
+ } else {
+ roleNamespace = realm.getClientByClientId(Constants.REALM_MANAGEMENT_CLIENT_ID);
+ }
+ for (String role : roles) {
+ RoleModel roleModel = roleNamespace.getRole(role);
+ if (user.hasRole(roleModel)) return true;
+ }
+ return false;
+ }
- if (map != null) {
- roles = map.get("roles");
- }
+ private boolean hasRoleInToken(String[] role) {
+ Map<String, Object> otherClaims = jwt.getOtherClaims();
+ if (otherClaims != null) {
+ Map<String, Map<String, List<String>>> resourceAccess = (Map<String, Map<String, List<String>>>) jwt.getOtherClaims().get("resource_access");
+ if (resourceAccess == null) {
+ return false;
+ }
- if (roles == null) {
- return false;
- }
+ List<String> roles = null;
+
+ Map<String, List<String>> map;
+ if (realm.getName().equals(Config.getAdminRealm())) {
+ map = resourceAccess.get(realm.getMasterAdminClient().getClientId());
+ } else {
+ map = resourceAccess.get(Constants.REALM_MANAGEMENT_CLIENT_ID);
+ }
- for (String r : role) {
- if (roles.contains(r)) {
- return true;
- }
+ if (map != null) {
+ roles = map.get("roles");
+ }
+
+ if (roles == null) {
+ return false;
+ }
+
+ for (String r : role) {
+ if (roles.contains(r)) {
+ return true;
}
}
- return false;
- } catch (Throwable t) {
- return false;
}
+ return false;
}
private boolean authenticateClient(ClientModel client) {
diff --git a/services/src/main/java/org/keycloak/services/DefaultKeycloakSessionFactory.java b/services/src/main/java/org/keycloak/services/DefaultKeycloakSessionFactory.java
index 0134229..0869c5d 100755
--- a/services/src/main/java/org/keycloak/services/DefaultKeycloakSessionFactory.java
+++ b/services/src/main/java/org/keycloak/services/DefaultKeycloakSessionFactory.java
@@ -30,6 +30,7 @@ import org.keycloak.provider.ProviderManager;
import org.keycloak.provider.ProviderManagerDeployer;
import org.keycloak.provider.ProviderManagerRegistry;
import org.keycloak.provider.Spi;
+import org.keycloak.services.resources.admin.permissions.AdminPermissions;
import java.util.HashMap;
import java.util.HashSet;
@@ -93,6 +94,7 @@ public class DefaultKeycloakSessionFactory implements KeycloakSessionFactory, Pr
}
// make the session factory ready for hot deployment
ProviderManagerRegistry.SINGLETON.setDeployer(this);
+ AdminPermissions.registerListener(this);
}
protected Map<Class<? extends Provider>, Map<String, ProviderFactory>> getFactoriesCopy() {
diff --git a/services/src/main/java/org/keycloak/services/managers/RealmManager.java b/services/src/main/java/org/keycloak/services/managers/RealmManager.java
index a83d80d..bab0440 100755
--- a/services/src/main/java/org/keycloak/services/managers/RealmManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/RealmManager.java
@@ -159,7 +159,6 @@ public class RealmManager {
ClientModel realmAdminApp = realm.getClientByClientId(realmAdminApplicationClientId);
adminRole = realmAdminApp.getRole(AdminRoles.REALM_ADMIN);
}
- adminConsole.addScopeMapping(adminRole);
}
protected void setupAdminConsoleLocaleMapper(RealmModel realm) {
@@ -194,10 +193,21 @@ public class RealmManager {
ClientModel realmAdminApp = realm.getClientByClientId(realmAdminApplicationClientId);
adminRole = realmAdminApp.getRole(AdminRoles.REALM_ADMIN);
}
- adminCli.addScopeMapping(adminRole);
}
}
+ public void addQueryCompositeRoles(ClientModel realmAccess) {
+ RoleModel queryClients = realmAccess.getRole(AdminRoles.QUERY_CLIENTS);
+ RoleModel queryUsers = realmAccess.getRole(AdminRoles.QUERY_USERS);
+ RoleModel queryGroups = realmAccess.getRole(AdminRoles.QUERY_GROUPS);
+
+ RoleModel viewClients = realmAccess.getRole(AdminRoles.VIEW_CLIENTS);
+ viewClients.addCompositeRole(queryClients);
+ RoleModel viewUsers = realmAccess.getRole(AdminRoles.VIEW_USERS);
+ viewUsers.addCompositeRole(queryUsers);
+ viewUsers.addCompositeRole(queryGroups);
+ }
+
public String getRealmAdminClientId(RealmModel realm) {
return Constants.REALM_MANAGEMENT_CLIENT_ID;
@@ -325,6 +335,7 @@ public class RealmManager {
role.setScopeParamRequired(false);
adminRole.addCompositeRole(role);
}
+ addQueryCompositeRoles(realmAdminApp);
}
private void checkMasterAdminManagementRoles(RealmModel realm) {
@@ -338,6 +349,7 @@ public class RealmManager {
addAndSetAdminRole(r, masterAdminClient, adminRole);
}
}
+ addQueryCompositeRoles(masterAdminClient);
}
@@ -360,6 +372,7 @@ public class RealmManager {
for (String r : AdminRoles.ALL_REALM_ROLES) {
addAndSetAdminRole(r, realmAdminClient, adminRole);
}
+ addQueryCompositeRoles(realmAdminClient);
}
private void addAndSetAdminRole(String roleName, ClientModel parentClient, RoleModel parentRole) {
@@ -383,6 +396,7 @@ public class RealmManager {
addAndSetAdminRole(r, realmAdminClient, adminRole);
}
}
+ addQueryCompositeRoles(realmAdminClient);
}
@@ -500,7 +514,8 @@ public class RealmManager {
// I need to postpone impersonation because it needs "realm-management" client and its roles set
if (postponeImpersonationSetup) {
setupImpersonationService(realm);
- }
+ String realmAdminClientId = getRealmAdminClientId(realm);
+ }
if (postponeAdminCliSetup) {
setupAdminCli(realm);
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/AdminAuth.java b/services/src/main/java/org/keycloak/services/resources/admin/AdminAuth.java
index 924b35e..98d4538 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/AdminAuth.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/AdminAuth.java
@@ -98,4 +98,7 @@ public class AdminAuth {
return false;
}
+ public enum Resource {
+ CLIENT, USER, REALM, EVENTS, IDENTITY_PROVIDER, IMPERSONATION, AUTHORIZATION
+ }
}
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/AdminRoot.java b/services/src/main/java/org/keycloak/services/resources/admin/AdminRoot.java
index 5db1ea4..650ac75 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/AdminRoot.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/AdminRoot.java
@@ -38,6 +38,8 @@ import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.services.resources.Cors;
import org.keycloak.services.resources.admin.info.ServerInfoAdminResource;
+import org.keycloak.services.resources.admin.permissions.AdminPermissions;
+import org.keycloak.services.resources.admin.permissions.RealmsPermissionEvaluator;
import org.keycloak.theme.Theme;
import org.keycloak.theme.ThemeProvider;
@@ -229,7 +231,7 @@ public class AdminRoot {
handlePreflightRequest();
AdminAuth auth = authenticateRealmAdminRequest(headers);
- if (!isAdmin(auth)) {
+ if (!AdminPermissions.realms(session, auth).isAdmin()) {
throw new ForbiddenException();
}
@@ -244,26 +246,6 @@ public class AdminRoot {
return adminResource;
}
- protected boolean isAdmin(AdminAuth auth) {
-
- RealmManager realmManager = new RealmManager(session);
- if (auth.getRealm().equals(realmManager.getKeycloakAdminstrationRealm())) {
- if (auth.hasOneOfRealmRole(AdminRoles.ADMIN, AdminRoles.CREATE_REALM)) {
- return true;
- }
- for (RealmModel realm : session.realms().getRealms()) {
- ClientModel client = realm.getMasterAdminClient();
- if (auth.hasOneOfAppRole(client, AdminRoles.ALL_REALM_ROLES)) {
- return true;
- }
- }
- return false;
- } else {
- ClientModel client = auth.getRealm().getClientByClientId(realmManager.getRealmAdminClientId(auth.getRealm()));
- return auth.hasOneOfAppRole(client, AdminRoles.ALL_REALM_ROLES);
- }
- }
-
protected void handlePreflightRequest() {
if (request.getHttpMethod().equalsIgnoreCase("OPTIONS")) {
logger.debug("Cors admin pre-flight");
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/AttackDetectionResource.java b/services/src/main/java/org/keycloak/services/resources/admin/AttackDetectionResource.java
index 5ac7593..c6064bb 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/AttackDetectionResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/AttackDetectionResource.java
@@ -18,6 +18,7 @@ package org.keycloak.services.resources.admin;
import org.jboss.logging.Logger;
import org.jboss.resteasy.annotations.cache.NoCache;
+import org.jboss.resteasy.spi.NotFoundException;
import org.keycloak.common.ClientConnection;
import org.keycloak.events.admin.OperationType;
import org.keycloak.events.admin.ResourceType;
@@ -26,6 +27,7 @@ import org.keycloak.models.RealmModel;
import org.keycloak.models.UserLoginFailureModel;
import org.keycloak.models.UserModel;
import org.keycloak.services.managers.BruteForceProtector;
+import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
@@ -48,7 +50,7 @@ import java.util.Map;
*/
public class AttackDetectionResource {
protected static final Logger logger = Logger.getLogger(AttackDetectionResource.class);
- protected RealmAuth auth;
+ protected AdminPermissionEvaluator auth;
protected RealmModel realm;
private AdminEventBuilder adminEvent;
@@ -64,12 +66,10 @@ public class AttackDetectionResource {
@Context
protected HttpHeaders headers;
- public AttackDetectionResource(RealmAuth auth, RealmModel realm, AdminEventBuilder adminEvent) {
+ public AttackDetectionResource(AdminPermissionEvaluator auth, RealmModel realm, AdminEventBuilder adminEvent) {
this.auth = auth;
this.realm = realm;
this.adminEvent = adminEvent.realm(realm).resource(ResourceType.USER_LOGIN_FAILURE);
-
- auth.init(RealmAuth.Resource.USER);
}
/**
@@ -83,7 +83,12 @@ public class AttackDetectionResource {
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public Map<String, Object> bruteForceUserStatus(@PathParam("userId") String userId) {
- auth.requireView();
+ UserModel user = session.users().getUserById(userId, realm);
+ if (user == null) {
+ auth.users().requireView();
+ } else {
+ auth.users().requireView(user);
+ }
Map<String, Object> data = new HashMap<>();
data.put("disabled", false);
@@ -92,7 +97,6 @@ public class AttackDetectionResource {
data.put("lastIPFailure", "n/a");
if (!realm.isBruteForceProtected()) return data;
- UserModel user = session.users().getUserById(userId, realm);
UserLoginFailureModel model = session.sessions().getUserLoginFailure(realm, userId);
if (model == null) return data;
@@ -115,8 +119,12 @@ public class AttackDetectionResource {
@Path("brute-force/users/{userId}")
@DELETE
public void clearBruteForceForUser(@PathParam("userId") String userId) {
- auth.requireManage();
-
+ UserModel user = session.users().getUserById(userId, realm);
+ if (user == null) {
+ auth.users().requireManage();
+ } else {
+ auth.users().requireManage(user);
+ }
UserLoginFailureModel model = session.sessions().getUserLoginFailure(realm, userId);
if (model != null) {
session.sessions().removeUserLoginFailure(realm, userId);
@@ -133,7 +141,7 @@ public class AttackDetectionResource {
@Path("brute-force/users")
@DELETE
public void clearAllBruteForce() {
- auth.requireManage();
+ auth.users().requireManage();
session.sessions().removeAllUserLoginFailures(realm);
adminEvent.operation(OperationType.DELETE).resourcePath(uriInfo).success();
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/AuthenticationManagementResource.java b/services/src/main/java/org/keycloak/services/resources/admin/AuthenticationManagementResource.java
index 2f7362b..61f6254 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/AuthenticationManagementResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/AuthenticationManagementResource.java
@@ -50,6 +50,7 @@ import org.keycloak.representations.idm.AuthenticatorConfigRepresentation;
import org.keycloak.representations.idm.ConfigPropertyRepresentation;
import org.keycloak.representations.idm.RequiredActionProviderRepresentation;
import org.keycloak.services.ErrorResponse;
+import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
import org.keycloak.utils.CredentialHelper;
import javax.ws.rs.Consumes;
@@ -80,18 +81,17 @@ public class AuthenticationManagementResource {
private final RealmModel realm;
private final KeycloakSession session;
- private RealmAuth auth;
+ private AdminPermissionEvaluator auth;
private AdminEventBuilder adminEvent;
@Context
private UriInfo uriInfo;
protected static final Logger logger = Logger.getLogger(AuthenticationManagementResource.class);
- public AuthenticationManagementResource(RealmModel realm, KeycloakSession session, RealmAuth auth, AdminEventBuilder adminEvent) {
+ public AuthenticationManagementResource(RealmModel realm, KeycloakSession session, AdminPermissionEvaluator auth, AdminEventBuilder adminEvent) {
this.realm = realm;
this.session = session;
this.auth = auth;
- this.auth.init(RealmAuth.Resource.REALM);
this.adminEvent = adminEvent.resource(ResourceType.AUTH_FLOW);
}
@@ -105,7 +105,7 @@ public class AuthenticationManagementResource {
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public List<Map<String, Object>> getFormProviders() {
- auth.requireView();
+ auth.realm().requireViewRealm();
List<ProviderFactory> factories = session.getKeycloakSessionFactory().getProviderFactories(FormAuthenticator.class);
return buildProviderMetadata(factories);
@@ -121,7 +121,7 @@ public class AuthenticationManagementResource {
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public List<Map<String, Object>> getAuthenticatorProviders() {
- auth.requireView();
+ auth.realm().requireViewRealm();
List<ProviderFactory> factories = session.getKeycloakSessionFactory().getProviderFactories(Authenticator.class);
return buildProviderMetadata(factories);
@@ -137,7 +137,7 @@ public class AuthenticationManagementResource {
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public List<Map<String, Object>> getClientAuthenticatorProviders() {
- auth.requireAny();
+ auth.requireAnyAdminRole();
List<ProviderFactory> factories = session.getKeycloakSessionFactory().getProviderFactories(ClientAuthenticator.class);
return buildProviderMetadata(factories);
@@ -167,7 +167,7 @@ public class AuthenticationManagementResource {
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public List<Map<String, Object>> getFormActionProviders() {
- auth.requireView();
+ auth.realm().requireViewRealm();
List<ProviderFactory> factories = session.getKeycloakSessionFactory().getProviderFactories(FormAction.class);
return buildProviderMetadata(factories);
@@ -184,7 +184,7 @@ public class AuthenticationManagementResource {
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public List<AuthenticationFlowRepresentation> getFlows() {
- auth.requireAny();
+ auth.realm().requireViewRealm();
List<AuthenticationFlowRepresentation> flows = new LinkedList<>();
for (AuthenticationFlowModel flow : realm.getAuthenticationFlows()) {
@@ -207,7 +207,7 @@ public class AuthenticationManagementResource {
@NoCache
@Consumes(MediaType.APPLICATION_JSON)
public Response createFlow(AuthenticationFlowRepresentation flow) {
- auth.requireManage();
+ auth.realm().requireManageRealm();
if (flow.getAlias() == null || flow.getAlias().isEmpty()) {
return ErrorResponse.exists("Failed to create flow with empty alias name");
@@ -235,7 +235,7 @@ public class AuthenticationManagementResource {
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public AuthenticationFlowRepresentation getFlow(@PathParam("id") String id) {
- auth.requireView();
+ auth.realm().requireViewRealm();
AuthenticationFlowModel flow = realm.getAuthenticationFlowById(id);
if (flow == null) {
@@ -253,7 +253,7 @@ public class AuthenticationManagementResource {
@DELETE
@NoCache
public void deleteFlow(@PathParam("id") String id) {
- auth.requireManage();
+ auth.realm().requireManageRealm();
deleteFlow(id, true);
}
@@ -292,7 +292,7 @@ public class AuthenticationManagementResource {
@NoCache
@Consumes(MediaType.APPLICATION_JSON)
public Response copy(@PathParam("flowAlias") String flowAlias, Map<String, String> data) {
- auth.requireManage();
+ auth.realm().requireManageRealm();
String newName = data.get("newName");
if (realm.getFlowByAlias(newName) != null) {
@@ -351,7 +351,7 @@ public class AuthenticationManagementResource {
@NoCache
@Consumes(MediaType.APPLICATION_JSON)
public Response addExecutionFlow(@PathParam("flowAlias") String flowAlias, Map<String, String> data) {
- auth.requireManage();
+ auth.realm().requireManageRealm();
AuthenticationFlowModel parentFlow = realm.getFlowByAlias(flowAlias);
if (parentFlow == null) {
@@ -403,7 +403,7 @@ public class AuthenticationManagementResource {
@NoCache
@Consumes(MediaType.APPLICATION_JSON)
public void addExecution(@PathParam("flowAlias") String flowAlias, Map<String, String> data) {
- auth.requireManage();
+ auth.realm().requireManageRealm();
AuthenticationFlowModel parentFlow = realm.getFlowByAlias(flowAlias);
if (parentFlow == null) {
@@ -450,7 +450,7 @@ public class AuthenticationManagementResource {
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public Response getExecutions(@PathParam("flowAlias") String flowAlias) {
- auth.requireView();
+ auth.realm().requireViewRealm();
AuthenticationFlowModel flow = realm.getFlowByAlias(flowAlias);
if (flow == null) {
@@ -535,7 +535,7 @@ public class AuthenticationManagementResource {
@NoCache
@Consumes(MediaType.APPLICATION_JSON)
public void updateExecutions(@PathParam("flowAlias") String flowAlias, AuthenticationExecutionInfoRepresentation rep) {
- auth.requireManage();
+ auth.realm().requireManageRealm();
AuthenticationFlowModel flow = realm.getFlowByAlias(flowAlias);
if (flow == null) {
@@ -566,7 +566,7 @@ public class AuthenticationManagementResource {
@NoCache
@Consumes(MediaType.APPLICATION_JSON)
public Response addExecution(AuthenticationExecutionRepresentation execution) {
- auth.requireManage();
+ auth.realm().requireManageRealm();
AuthenticationExecutionModel model = RepresentationToModel.toModel(realm, execution);
AuthenticationFlowModel parentFlow = getParentFlow(model);
@@ -601,7 +601,7 @@ public class AuthenticationManagementResource {
@POST
@NoCache
public void raisePriority(@PathParam("executionId") String execution) {
- auth.requireManage();
+ auth.realm().requireManageRealm();
AuthenticationExecutionModel model = realm.getAuthenticationExecutionById(execution);
if (model == null) {
@@ -647,7 +647,7 @@ public class AuthenticationManagementResource {
@POST
@NoCache
public void lowerPriority(@PathParam("executionId") String execution) {
- auth.requireManage();
+ auth.realm().requireManageRealm();
AuthenticationExecutionModel model = realm.getAuthenticationExecutionById(execution);
if (model == null) {
@@ -687,7 +687,7 @@ public class AuthenticationManagementResource {
@DELETE
@NoCache
public void removeExecution(@PathParam("executionId") String execution) {
- auth.requireManage();
+ auth.realm().requireManageRealm();
AuthenticationExecutionModel model = realm.getAuthenticationExecutionById(execution);
if (model == null) {
@@ -723,7 +723,7 @@ public class AuthenticationManagementResource {
@NoCache
@Consumes(MediaType.APPLICATION_JSON)
public Response newExecutionConfig(@PathParam("executionId") String execution, AuthenticatorConfigRepresentation json) {
- auth.requireManage();
+ auth.realm().requireManageRealm();
AuthenticationExecutionModel model = realm.getAuthenticationExecutionById(execution);
if (model == null) {
@@ -753,7 +753,7 @@ public class AuthenticationManagementResource {
@Produces(MediaType.APPLICATION_JSON)
@NoCache
public AuthenticatorConfigRepresentation getAuthenticatorConfig(@PathParam("executionId") String execution,@PathParam("id") String id) {
- auth.requireView();
+ auth.realm().requireViewRealm();
AuthenticatorConfigModel config = realm.getAuthenticatorConfigById(id);
if (config == null) {
@@ -773,7 +773,7 @@ public class AuthenticationManagementResource {
@Produces(MediaType.APPLICATION_JSON)
@NoCache
public List<Map<String, String>> getUnregisteredRequiredActions() {
- auth.requireView();
+ auth.realm().requireViewRealm();
List<ProviderFactory> factories = session.getKeycloakSessionFactory().getProviderFactories(RequiredActionProvider.class);
List<Map<String, String>> unregisteredList = new LinkedList<>();
@@ -807,7 +807,7 @@ public class AuthenticationManagementResource {
@Consumes(MediaType.APPLICATION_JSON)
@NoCache
public void registerRequiredAction(Map<String, String> data) {
- auth.requireManage();
+ auth.realm().requireManageRealm();
String providerId = data.get("providerId");
String name = data.get("name");
@@ -834,7 +834,7 @@ public class AuthenticationManagementResource {
@Produces(MediaType.APPLICATION_JSON)
@NoCache
public List<RequiredActionProviderRepresentation> getRequiredActions() {
- auth.requireAny();
+ auth.requireAnyAdminRole();
List<RequiredActionProviderRepresentation> list = new LinkedList<>();
for (RequiredActionProviderModel model : realm.getRequiredActionProviders()) {
@@ -863,7 +863,7 @@ public class AuthenticationManagementResource {
@Produces(MediaType.APPLICATION_JSON)
@NoCache
public RequiredActionProviderRepresentation getRequiredAction(@PathParam("alias") String alias) {
- auth.requireView();
+ auth.realm().requireViewRealm();
RequiredActionProviderModel model = realm.getRequiredActionProviderByAlias(alias);
if (model == null) {
@@ -883,7 +883,7 @@ public class AuthenticationManagementResource {
@PUT
@Consumes(MediaType.APPLICATION_JSON)
public void updateRequiredAction(@PathParam("alias") String alias, RequiredActionProviderRepresentation rep) {
- auth.requireManage();
+ auth.realm().requireManageRealm();
RequiredActionProviderModel model = realm.getRequiredActionProviderByAlias(alias);
if (model == null) {
@@ -909,7 +909,7 @@ public class AuthenticationManagementResource {
@Path("required-actions/{alias}")
@DELETE
public void removeRequiredAction(@PathParam("alias") String alias) {
- auth.requireManage();
+ auth.realm().requireManageRealm();
RequiredActionProviderModel model = realm.getRequiredActionProviderByAlias(alias);
if (model == null) {
@@ -928,7 +928,7 @@ public class AuthenticationManagementResource {
@Produces(MediaType.APPLICATION_JSON)
@NoCache
public AuthenticatorConfigInfoRepresentation getAuthenticatorConfigDescription(@PathParam("providerId") String providerId) {
- auth.requireView();
+ auth.realm().requireViewRealm();
ConfigurableAuthenticatorFactory factory = CredentialHelper.getConfigurableAuthenticatorFactory(session, providerId);
if (factory == null) {
@@ -959,7 +959,7 @@ public class AuthenticationManagementResource {
@Produces(MediaType.APPLICATION_JSON)
@NoCache
public Map<String, List<ConfigPropertyRepresentation>> getPerClientConfigDescription() {
- auth.requireAny();
+ auth.requireAnyAdminRole();
List<ProviderFactory> factories = session.getKeycloakSessionFactory().getProviderFactories(ClientAuthenticator.class);
@@ -991,7 +991,7 @@ public class AuthenticationManagementResource {
@NoCache
@Consumes(MediaType.APPLICATION_JSON)
public Response createAuthenticatorConfig(AuthenticatorConfigRepresentation rep) {
- auth.requireManage();
+ auth.realm().requireManageRealm();
AuthenticatorConfigModel config = realm.addAuthenticatorConfig(RepresentationToModel.toModel(rep));
adminEvent.operation(OperationType.CREATE).resource(ResourceType.AUTHENTICATOR_CONFIG).resourcePath(uriInfo, config.getId()).representation(rep).success();
@@ -1007,7 +1007,7 @@ public class AuthenticationManagementResource {
@Produces(MediaType.APPLICATION_JSON)
@NoCache
public AuthenticatorConfigRepresentation getAuthenticatorConfig(@PathParam("id") String id) {
- auth.requireView();
+ auth.realm().requireViewRealm();
AuthenticatorConfigModel config = realm.getAuthenticatorConfigById(id);
if (config == null) {
@@ -1025,7 +1025,7 @@ public class AuthenticationManagementResource {
@DELETE
@NoCache
public void removeAuthenticatorConfig(@PathParam("id") String id) {
- auth.requireManage();
+ auth.realm().requireManageRealm();
AuthenticatorConfigModel config = realm.getAuthenticatorConfigById(id);
if (config == null) {
@@ -1057,7 +1057,7 @@ public class AuthenticationManagementResource {
@Consumes(MediaType.APPLICATION_JSON)
@NoCache
public void updateAuthenticatorConfig(@PathParam("id") String id, AuthenticatorConfigRepresentation rep) {
- auth.requireManage();
+ auth.realm().requireManageRealm();
AuthenticatorConfigModel exists = realm.getAuthenticatorConfigById(id);
if (exists == null) {
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ClientAttributeCertificateResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ClientAttributeCertificateResource.java
index 1f4277b..a798414 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/ClientAttributeCertificateResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ClientAttributeCertificateResource.java
@@ -37,10 +37,12 @@ import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.representations.KeyStoreConfig;
import org.keycloak.representations.idm.CertificateRepresentation;
import org.keycloak.services.ErrorResponseException;
+import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
import org.keycloak.services.util.CertificateInfoHelper;
import org.keycloak.util.JWKSUtils;
import org.keycloak.util.JsonSerialization;
+import javax.ws.rs.BadRequestException;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
@@ -73,13 +75,13 @@ public class ClientAttributeCertificateResource {
public static final String JSON_WEB_KEY_SET = "JSON Web Key Set";
protected RealmModel realm;
- private RealmAuth auth;
+ private AdminPermissionEvaluator auth;
protected ClientModel client;
protected KeycloakSession session;
protected AdminEventBuilder adminEvent;
protected String attributePrefix;
- public ClientAttributeCertificateResource(RealmModel realm, RealmAuth auth, ClientModel client, KeycloakSession session, String attributePrefix, AdminEventBuilder adminEvent) {
+ public ClientAttributeCertificateResource(RealmModel realm, AdminPermissionEvaluator auth, ClientModel client, KeycloakSession session, String attributePrefix, AdminEventBuilder adminEvent) {
this.realm = realm;
this.auth = auth;
this.client = client;
@@ -97,11 +99,7 @@ public class ClientAttributeCertificateResource {
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public CertificateRepresentation getKeyInfo() {
- auth.requireView();
-
- if (client == null) {
- throw new NotFoundException("Could not find client");
- }
+ auth.clients().requireView(client);
CertificateRepresentation info = CertificateInfoHelper.getCertificateFromClient(client, attributePrefix);
return info;
@@ -117,11 +115,7 @@ public class ClientAttributeCertificateResource {
@Path("generate")
@Produces(MediaType.APPLICATION_JSON)
public CertificateRepresentation generate() {
- auth.requireManage();
-
- if (client == null) {
- throw new NotFoundException("Could not find client");
- }
+ auth.clients().requireConfigure(client);
CertificateRepresentation info = KeycloakModelUtils.generateKeyPairCertificate(client.getClientId());
@@ -145,11 +139,7 @@ public class ClientAttributeCertificateResource {
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Produces(MediaType.APPLICATION_JSON)
public CertificateRepresentation uploadJks(@Context final UriInfo uriInfo, MultipartFormDataInput input) throws IOException {
- auth.requireManage();
-
- if (client == null) {
- throw new NotFoundException("Could not find client");
- }
+ auth.clients().requireConfigure(client);
try {
CertificateRepresentation info = getCertFromRequest(input);
@@ -175,11 +165,7 @@ public class ClientAttributeCertificateResource {
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Produces(MediaType.APPLICATION_JSON)
public CertificateRepresentation uploadJksCertificate(@Context final UriInfo uriInfo, MultipartFormDataInput input) throws IOException {
- auth.requireManage();
-
- if (client == null) {
- throw new NotFoundException("Could not find client");
- }
+ auth.clients().requireConfigure(client);
try {
CertificateRepresentation info = getCertFromRequest(input);
@@ -194,10 +180,12 @@ public class ClientAttributeCertificateResource {
}
private CertificateRepresentation getCertFromRequest(MultipartFormDataInput input) throws IOException {
- auth.requireManage();
+ auth.clients().requireManage(client);
CertificateRepresentation info = new CertificateRepresentation();
Map<String, List<InputPart>> uploadForm = input.getFormDataMap();
- String keystoreFormat = uploadForm.get("keystoreFormat").get(0).getBodyAsString();
+ List<InputPart> keystoreFormatPart = uploadForm.get("keystoreFormat");
+ if (keystoreFormatPart == null) throw new BadRequestException();
+ String keystoreFormat = keystoreFormatPart.get(0).getBodyAsString();
List<InputPart> inputParts = uploadForm.get("file");
if (keystoreFormat.equals(CERTIFICATE_PEM)) {
String pem = StreamUtil.readString(inputParts.get(0).getBody(InputStream.class, null));
@@ -279,11 +267,7 @@ public class ClientAttributeCertificateResource {
@Produces(MediaType.APPLICATION_OCTET_STREAM)
@Consumes(MediaType.APPLICATION_JSON)
public byte[] getKeystore(final KeyStoreConfig config) {
- auth.requireView();
-
- if (client == null) {
- throw new NotFoundException("Could not find client");
- }
+ auth.clients().requireView(client);
if (config.getFormat() != null && !config.getFormat().equals("JKS") && !config.getFormat().equals("PKCS12")) {
throw new NotAcceptableException("Only support jks or pkcs12 format.");
@@ -322,11 +306,7 @@ public class ClientAttributeCertificateResource {
@Produces(MediaType.APPLICATION_OCTET_STREAM)
@Consumes(MediaType.APPLICATION_JSON)
public byte[] generateAndGetKeystore(final KeyStoreConfig config) {
- auth.requireManage();
-
- if (client == null) {
- throw new NotFoundException("Could not find client");
- }
+ auth.clients().requireConfigure(client);
if (config.getFormat() != null && !config.getFormat().equals("JKS") && !config.getFormat().equals("PKCS12")) {
throw new NotAcceptableException("Only support jks or pkcs12 format.");
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ClientInitialAccessResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ClientInitialAccessResource.java
index 0fe0090..609b9b0 100644
--- a/services/src/main/java/org/keycloak/services/resources/admin/ClientInitialAccessResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ClientInitialAccessResource.java
@@ -25,6 +25,7 @@ import org.keycloak.models.RealmModel;
import org.keycloak.representations.idm.ClientInitialAccessCreatePresentation;
import org.keycloak.representations.idm.ClientInitialAccessPresentation;
import org.keycloak.services.clientregistration.ClientRegistrationTokenUtils;
+import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.Consumes;
@@ -48,7 +49,7 @@ import java.util.List;
*/
public class ClientInitialAccessResource {
- private final RealmAuth auth;
+ private final AdminPermissionEvaluator auth;
private final RealmModel realm;
private final AdminEventBuilder adminEvent;
@@ -58,12 +59,11 @@ public class ClientInitialAccessResource {
@Context
protected UriInfo uriInfo;
- public ClientInitialAccessResource(RealmModel realm, RealmAuth auth, AdminEventBuilder adminEvent) {
+ public ClientInitialAccessResource(RealmModel realm, AdminPermissionEvaluator auth, AdminEventBuilder adminEvent) {
this.auth = auth;
this.realm = realm;
this.adminEvent = adminEvent.resource(ResourceType.CLIENT_INITIAL_ACCESS_MODEL);
- auth.init(RealmAuth.Resource.CLIENT);
}
/**
@@ -76,7 +76,7 @@ public class ClientInitialAccessResource {
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public ClientInitialAccessPresentation create(ClientInitialAccessCreatePresentation config, @Context final HttpServletResponse response) {
- auth.requireManage();
+ auth.clients().requireManage();
int expiration = config.getExpiration() != null ? config.getExpiration() : 0;
int count = config.getCount() != null ? config.getCount() : 1;
@@ -99,7 +99,7 @@ public class ClientInitialAccessResource {
@GET
@Produces(MediaType.APPLICATION_JSON)
public List<ClientInitialAccessPresentation> list() {
- auth.requireView();
+ auth.clients().requireView();
List<ClientInitialAccessModel> models = session.realms().listClientInitialAccess(realm);
List<ClientInitialAccessPresentation> reps = new LinkedList<>();
@@ -113,7 +113,7 @@ public class ClientInitialAccessResource {
@DELETE
@Path("{id}")
public void delete(final @PathParam("id") String id) {
- auth.requireManage();
+ auth.clients().requireManage();
session.realms().removeClientInitialAccessModel(realm, id);
adminEvent.operation(OperationType.DELETE).resourcePath(uriInfo).success();
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ClientRegistrationPolicyResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ClientRegistrationPolicyResource.java
index f8c57e2..9250326 100644
--- a/services/src/main/java/org/keycloak/services/resources/admin/ClientRegistrationPolicyResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ClientRegistrationPolicyResource.java
@@ -37,6 +37,7 @@ import org.keycloak.provider.ProviderFactory;
import org.keycloak.representations.idm.ComponentTypeRepresentation;
import org.keycloak.services.clientregistration.policy.ClientRegistrationPolicy;
import org.keycloak.services.clientregistration.policy.ClientRegistrationPolicyFactory;
+import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
/**
* @resource Client Registration Policy
@@ -44,7 +45,7 @@ import org.keycloak.services.clientregistration.policy.ClientRegistrationPolicyF
*/
public class ClientRegistrationPolicyResource {
- private final RealmAuth auth;
+ private final AdminPermissionEvaluator auth;
private final RealmModel realm;
private final AdminEventBuilder adminEvent;
@@ -54,12 +55,11 @@ public class ClientRegistrationPolicyResource {
@Context
protected UriInfo uriInfo;
- public ClientRegistrationPolicyResource(RealmModel realm, RealmAuth auth, AdminEventBuilder adminEvent) {
+ public ClientRegistrationPolicyResource(RealmModel realm, AdminPermissionEvaluator auth, AdminEventBuilder adminEvent) {
this.auth = auth;
this.realm = realm;
this.adminEvent = adminEvent.resource(ResourceType.CLIENT_INITIAL_ACCESS_MODEL);
- auth.init(RealmAuth.Resource.CLIENT);
}
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ClientResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ClientResource.java
index 43c897c..e7d611e 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/ClientResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ClientResource.java
@@ -27,10 +27,12 @@ import org.keycloak.events.admin.OperationType;
import org.keycloak.events.admin.ResourceType;
import org.keycloak.models.AuthenticatedClientSessionModel;
import org.keycloak.models.ClientModel;
+import org.keycloak.models.ClientTemplateModel;
import org.keycloak.models.Constants;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ModelDuplicateException;
import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleModel;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
@@ -40,7 +42,9 @@ import org.keycloak.models.utils.RepresentationToModel;
import org.keycloak.protocol.ClientInstallationProvider;
import org.keycloak.representations.adapters.action.GlobalRequestResult;
import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.representations.idm.ClientTemplateRepresentation;
import org.keycloak.representations.idm.CredentialRepresentation;
+import org.keycloak.representations.idm.ManagementPermissionReference;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.representations.idm.UserSessionRepresentation;
import org.keycloak.services.ErrorResponse;
@@ -51,6 +55,9 @@ import org.keycloak.services.managers.ClientManager;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.services.managers.ResourceAdminManager;
import org.keycloak.services.resources.KeycloakApplication;
+import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
+import org.keycloak.services.resources.admin.permissions.AdminPermissionManagement;
+import org.keycloak.services.resources.admin.permissions.AdminPermissions;
import org.keycloak.services.validation.ClientValidator;
import org.keycloak.services.validation.PairwiseClientValidator;
import org.keycloak.services.validation.ValidationMessages;
@@ -89,7 +96,7 @@ import static java.lang.Boolean.TRUE;
public class ClientResource {
protected static final Logger logger = Logger.getLogger(ClientResource.class);
protected RealmModel realm;
- private RealmAuth auth;
+ private AdminPermissionEvaluator auth;
private AdminEventBuilder adminEvent;
protected ClientModel client;
protected KeycloakSession session;
@@ -104,19 +111,19 @@ public class ClientResource {
return keycloak;
}
- public ClientResource(RealmModel realm, RealmAuth auth, ClientModel clientModel, KeycloakSession session, AdminEventBuilder adminEvent) {
+ public ClientResource(RealmModel realm, AdminPermissionEvaluator auth, ClientModel clientModel, KeycloakSession session, AdminEventBuilder adminEvent) {
this.realm = realm;
this.auth = auth;
this.client = clientModel;
this.session = session;
this.adminEvent = adminEvent.resource(ResourceType.CLIENT);
-
- auth.init(RealmAuth.Resource.CLIENT);
}
@Path("protocol-mappers")
public ProtocolMappersResource getProtocolMappers() {
- ProtocolMappersResource mappers = new ProtocolMappersResource(realm, client, auth, adminEvent);
+ AdminPermissionEvaluator.RequirePermissionCheck manageCheck = () -> auth.clients().requireManage(client);
+ AdminPermissionEvaluator.RequirePermissionCheck viewCheck = () -> auth.clients().requireView(client);
+ ProtocolMappersResource mappers = new ProtocolMappersResource(realm, client, auth, adminEvent, manageCheck, viewCheck);
ResteasyProviderFactory.getInstance().injectProperties(mappers);
return mappers;
}
@@ -129,15 +136,11 @@ public class ClientResource {
@PUT
@Consumes(MediaType.APPLICATION_JSON)
public Response update(final ClientRepresentation rep) {
- auth.requireManage();
-
- if (client == null) {
- throw new NotFoundException("Could not find client");
- }
+ auth.clients().requireConfigure(client);
ValidationMessages validationMessages = new ValidationMessages();
if (!ClientValidator.validate(rep, validationMessages) || !PairwiseClientValidator.validate(session, rep, validationMessages)) {
- Properties messages = AdminRoot.getMessages(session, realm, auth.getAuth().getToken().getLocale());
+ Properties messages = AdminRoot.getMessages(session, realm, auth.adminAuth().getToken().getLocale());
throw new ErrorResponseException(
validationMessages.getStringMessages(),
validationMessages.getStringMessages(messages),
@@ -164,17 +167,14 @@ public class ClientResource {
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public ClientRepresentation getClient() {
- auth.requireView();
-
- if (client == null) {
- throw new NotFoundException("Could not find client");
- }
+ auth.clients().requireView(client);
ClientRepresentation representation = ModelToRepresentation.toRepresentation(client);
if (Profile.isFeatureEnabled(Profile.Feature.AUTHORIZATION)) {
representation.setAuthorizationServicesEnabled(authorization().isEnabled());
}
+ representation.setAccess(auth.clients().getAccess(client));
return representation;
}
@@ -194,11 +194,7 @@ public class ClientResource {
@NoCache
@Path("installation/providers/{providerId}")
public Response getInstallationProvider(@PathParam("providerId") String providerId) {
- auth.requireView();
-
- if (client == null) {
- throw new NotFoundException("Could not find client");
- }
+ auth.clients().requireView(client);
ClientInstallationProvider provider = session.getProvider(ClientInstallationProvider.class, providerId);
if (provider == null) throw new NotFoundException("Unknown Provider");
@@ -212,7 +208,7 @@ public class ClientResource {
@DELETE
@NoCache
public void deleteClient() {
- auth.requireManage();
+ auth.clients().requireManage(client);
if (client == null) {
throw new NotFoundException("Could not find client");
@@ -233,11 +229,7 @@ public class ClientResource {
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public CredentialRepresentation regenerateSecret() {
- auth.requireManage();
-
- if (client == null) {
- throw new NotFoundException("Could not find client");
- }
+ auth.clients().requireConfigure(client);
logger.debug("regenerateSecret");
UserCredentialModel cred = KeycloakModelUtils.generateSecret(client);
@@ -256,11 +248,7 @@ public class ClientResource {
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public ClientRepresentation regenerateRegistrationAccessToken() {
- auth.requireManage();
-
- if (client == null) {
- throw new NotFoundException("Could not find client");
- }
+ auth.clients().requireManage(client);
String token = ClientRegistrationTokenUtils.updateRegistrationAccessToken(session, realm, uriInfo, client, RegistrationAuth.AUTHENTICATED);
@@ -281,11 +269,7 @@ public class ClientResource {
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public CredentialRepresentation getClientSecret() {
- auth.requireView();
-
- if (client == null) {
- throw new NotFoundException("Could not find client");
- }
+ auth.clients().requireView(client);
logger.debug("getClientSecret");
UserCredentialModel model = UserCredentialModel.secret(client.getSecret());
@@ -300,12 +284,14 @@ public class ClientResource {
*/
@Path("scope-mappings")
public ScopeMappedResource getScopeMappedResource() {
- return new ScopeMappedResource(realm, auth, client, session, adminEvent);
+ AdminPermissionEvaluator.RequirePermissionCheck manageCheck = () -> auth.clients().requireManage(client);
+ AdminPermissionEvaluator.RequirePermissionCheck viewCheck = () -> auth.clients().requireView(client);
+ return new ScopeMappedResource(realm, auth, client, session, adminEvent, manageCheck, viewCheck);
}
@Path("roles")
public RoleContainerResource getRoleContainerResource() {
- return new RoleContainerResource(uriInfo, realm, auth, client, adminEvent);
+ return new RoleContainerResource(session, uriInfo, realm, auth, client, adminEvent);
}
/**
@@ -318,11 +304,7 @@ public class ClientResource {
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public UserRepresentation getServiceAccountUser() {
- auth.requireView();
-
- if (client == null) {
- throw new NotFoundException("Could not find client");
- }
+ auth.clients().requireView(client);
UserModel user = session.users().getServiceAccount(client);
if (user == null) {
@@ -346,11 +328,7 @@ public class ClientResource {
@POST
@Produces(MediaType.APPLICATION_JSON)
public GlobalRequestResult pushRevocation() {
- auth.requireManage();
-
- if (client == null) {
- throw new NotFoundException("Could not find client");
- }
+ auth.clients().requireConfigure(client);
adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo).resource(ResourceType.CLIENT).success();
return new ResourceAdminManager(session).pushClientRevocationPolicy(uriInfo.getRequestUri(), realm, client);
@@ -373,11 +351,7 @@ public class ClientResource {
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public Map<String, Long> getApplicationSessionCount() {
- auth.requireView();
-
- if (client == null) {
- throw new NotFoundException("Could not find client");
- }
+ auth.clients().requireView(client);
Map<String, Long> map = new HashMap<>();
map.put("count", session.sessions().getActiveUserSessions(client.getRealm(), client));
@@ -398,11 +372,7 @@ public class ClientResource {
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public List<UserSessionRepresentation> getUserSessions(@QueryParam("first") Integer firstResult, @QueryParam("max") Integer maxResults) {
- auth.requireView();
-
- if (client == null) {
- throw new NotFoundException("Could not find client");
- }
+ auth.clients().requireView(client);
firstResult = firstResult != null ? firstResult : -1;
maxResults = maxResults != null ? maxResults : Constants.DEFAULT_MAX_RESULTS;
@@ -430,11 +400,7 @@ public class ClientResource {
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public Map<String, Long> getOfflineSessionCount() {
- auth.requireView();
-
- if (client == null) {
- throw new NotFoundException("Could not find client");
- }
+ auth.clients().requireView(client);
Map<String, Long> map = new HashMap<>();
map.put("count", session.sessions().getOfflineSessionsCount(client.getRealm(), client));
@@ -455,11 +421,7 @@ public class ClientResource {
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public List<UserSessionRepresentation> getOfflineUserSessions(@QueryParam("first") Integer firstResult, @QueryParam("max") Integer maxResults) {
- auth.requireView();
-
- if (client == null) {
- throw new NotFoundException("Could not find client");
- }
+ auth.clients().requireView(client);
firstResult = firstResult != null ? firstResult : -1;
maxResults = maxResults != null ? maxResults : Constants.DEFAULT_MAX_RESULTS;
@@ -496,11 +458,7 @@ public class ClientResource {
@POST
@Consumes(MediaType.APPLICATION_JSON)
public void registerNode(Map<String, String> formParams) {
- auth.requireManage();
-
- if (client == null) {
- throw new NotFoundException("Could not find client");
- }
+ auth.clients().requireConfigure(client);
String node = formParams.get("node");
if (node == null) {
@@ -520,11 +478,7 @@ public class ClientResource {
@DELETE
@NoCache
public void unregisterNode(final @PathParam("node") String node) {
- auth.requireManage();
-
- if (client == null) {
- throw new NotFoundException("Could not find client");
- }
+ auth.clients().requireConfigure(client);
if (logger.isDebugEnabled()) logger.debug("Unregister node: " + node);
@@ -548,11 +502,7 @@ public class ClientResource {
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public GlobalRequestResult testNodesAvailable() {
- auth.requireManage();
-
- if (client == null) {
- throw new NotFoundException("Could not find client");
- }
+ auth.clients().requireConfigure(client);
logger.debug("Test availability of cluster nodes");
GlobalRequestResult result = new ResourceAdminManager(session).testNodesAvailability(uriInfo.getRequestUri(), realm, client);
@@ -571,6 +521,57 @@ public class ClientResource {
return resource;
}
+ /**
+ * Return object stating whether client Authorization permissions have been initialized or not and a reference
+ *
+ * @return
+ */
+ @Path("management/permissions")
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ @NoCache
+ public ManagementPermissionReference getManagementPermissions() {
+ auth.roles().requireView(client);
+
+ AdminPermissionManagement permissions = AdminPermissions.management(session, realm);
+ if (!permissions.clients().isPermissionsEnabled(client)) {
+ return new ManagementPermissionReference();
+ }
+ return toMgmtRef(client, permissions);
+ }
+
+ public static ManagementPermissionReference toMgmtRef(ClientModel client, AdminPermissionManagement permissions) {
+ ManagementPermissionReference ref = new ManagementPermissionReference();
+ ref.setEnabled(true);
+ ref.setResource(permissions.clients().resource(client).getId());
+ ref.setScopePermissions(permissions.clients().getPermissions(client));
+ return ref;
+ }
+
+
+ /**
+ * Return object stating whether client Authorization permissions have been initialized or not and a reference
+ *
+ *
+ * @return initialized manage permissions reference
+ */
+ @Path("management/permissions")
+ @PUT
+ @Produces(MediaType.APPLICATION_JSON)
+ @Consumes(MediaType.APPLICATION_JSON)
+ @NoCache
+ public ManagementPermissionReference setManagementPermissionsEnabled(ManagementPermissionReference ref) {
+ auth.clients().requireManage(client);
+ if (ref.isEnabled()) {
+ AdminPermissionManagement permissions = AdminPermissions.management(session, realm);
+ permissions.clients().setPermissionsEnabled(client, ref.isEnabled());
+ return toMgmtRef(client, permissions);
+ } else {
+ return new ManagementPermissionReference();
+ }
+ }
+
+
private void updateClientFromRep(ClientRepresentation rep, ClientModel client, KeycloakSession session) throws ModelDuplicateException {
if (TRUE.equals(rep.isServiceAccountsEnabled())) {
UserModel serviceAccount = this.session.users().getServiceAccount(client);
@@ -584,6 +585,30 @@ public class ClientResource {
new ClientManager(new RealmManager(session)).clientIdChanged(client, rep.getClientId());
}
+ if (rep.isFullScopeAllowed() != null && rep.isFullScopeAllowed().booleanValue() != client.isFullScopeAllowed()) {
+ auth.clients().requireManage(client);
+ }
+
+ if (rep.getClientTemplate() != null) {
+ ClientTemplateModel currTemplate = client.getClientTemplate();
+ if (currTemplate == null) {
+ if (!rep.getClientTemplate().equals(ClientTemplateRepresentation.NONE)) {
+ auth.clients().requireManage(client);
+ }
+ } else if (!rep.getClientTemplate().equals(currTemplate.getName())){
+ auth.clients().requireManage(client);
+ }
+ if ((rep.isUseTemplateConfig() != null && rep.isUseTemplateConfig().booleanValue() != client.useTemplateConfig())
+ || (rep.isUseTemplateScope() != null && rep.isUseTemplateScope().booleanValue() != client.useTemplateScope())
+ || (rep.isUseTemplateMappers() != null && rep.isUseTemplateMappers().booleanValue() != client.useTemplateMappers())
+
+ ) {
+ auth.clients().requireManage(client);
+ }
+ }
+
+
+
RepresentationToModel.updateClient(rep, client);
}
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ClientRoleMappingsResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ClientRoleMappingsResource.java
index 3d7d4cd..ae7d519 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/ClientRoleMappingsResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ClientRoleMappingsResource.java
@@ -19,6 +19,7 @@ package org.keycloak.services.resources.admin;
import org.jboss.logging.Logger;
import org.jboss.resteasy.annotations.cache.NoCache;
import org.jboss.resteasy.spi.NotFoundException;
+import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
import org.keycloak.events.admin.OperationType;
import org.keycloak.events.admin.ResourceType;
import org.keycloak.models.ClientModel;
@@ -30,6 +31,7 @@ import org.keycloak.models.RoleModel;
import org.keycloak.models.utils.ModelToRepresentation;
import org.keycloak.representations.idm.RoleRepresentation;
import org.keycloak.services.ErrorResponseException;
+import org.keycloak.services.ForbiddenException;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
@@ -47,6 +49,7 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Properties;
import java.util.Set;
+import java.util.stream.Collectors;
/**
* @resource Client Role Mappings
@@ -58,19 +61,26 @@ public class ClientRoleMappingsResource {
protected KeycloakSession session;
protected RealmModel realm;
- protected RealmAuth auth;
+ protected AdminPermissionEvaluator auth;
protected RoleMapperModel user;
protected ClientModel client;
protected AdminEventBuilder adminEvent;
private UriInfo uriInfo;
+ protected AdminPermissionEvaluator.RequirePermissionCheck managePermission;
+ protected AdminPermissionEvaluator.RequirePermissionCheck viewPermission;
- public ClientRoleMappingsResource(UriInfo uriInfo, KeycloakSession session, RealmModel realm, RealmAuth auth, RoleMapperModel user, ClientModel client, AdminEventBuilder adminEvent) {
+
+ public ClientRoleMappingsResource(UriInfo uriInfo, KeycloakSession session, RealmModel realm, AdminPermissionEvaluator auth,
+ RoleMapperModel user, ClientModel client, AdminEventBuilder adminEvent,
+ AdminPermissionEvaluator.RequirePermissionCheck manageCheck, AdminPermissionEvaluator.RequirePermissionCheck viewCheck ) {
this.uriInfo = uriInfo;
this.session = session;
this.realm = realm;
this.auth = auth;
this.user = user;
this.client = client;
+ this.managePermission = manageCheck;
+ this.viewPermission = viewCheck;
this.adminEvent = adminEvent.resource(ResourceType.CLIENT_ROLE_MAPPING);
}
@@ -83,11 +93,7 @@ public class ClientRoleMappingsResource {
@Produces(MediaType.APPLICATION_JSON)
@NoCache
public List<RoleRepresentation> getClientRoleMappings() {
- auth.requireView();
-
- if (user == null || client == null) {
- throw new NotFoundException("Not found");
- }
+ viewPermission.require();
Set<RoleModel> mappings = user.getClientRoleMappings(client);
List<RoleRepresentation> mapRep = new ArrayList<RoleRepresentation>();
@@ -109,11 +115,8 @@ public class ClientRoleMappingsResource {
@Produces(MediaType.APPLICATION_JSON)
@NoCache
public List<RoleRepresentation> getCompositeClientRoleMappings() {
- auth.requireView();
+ viewPermission.require();
- if (user == null || client == null) {
- throw new NotFoundException("Not found");
- }
Set<RoleModel> roles = client.getRoles();
List<RoleRepresentation> mapRep = new ArrayList<RoleRepresentation>();
@@ -133,13 +136,12 @@ public class ClientRoleMappingsResource {
@Produces(MediaType.APPLICATION_JSON)
@NoCache
public List<RoleRepresentation> getAvailableClientRoleMappings() {
- auth.requireView();
-
- if (user == null || client == null) {
- throw new NotFoundException("Not found");
- }
+ viewPermission.require();
Set<RoleModel> available = client.getRoles();
+ available = available.stream().filter(r ->
+ auth.roles().canMapRole(r)
+ ).collect(Collectors.toSet());
return getAvailableRoles(user, available);
}
@@ -165,17 +167,14 @@ public class ClientRoleMappingsResource {
@POST
@Consumes(MediaType.APPLICATION_JSON)
public void addClientRoleMapping(List<RoleRepresentation> roles) {
- auth.requireManage();
-
- if (user == null || client == null) {
- throw new NotFoundException("Not found");
- }
+ managePermission.require();
for (RoleRepresentation role : roles) {
RoleModel roleModel = client.getRole(role.getName());
if (roleModel == null || !roleModel.getId().equals(role.getId())) {
throw new NotFoundException("Role not found");
}
+ auth.roles().requireMapRole(roleModel);
user.grantRole(roleModel);
}
adminEvent.operation(OperationType.CREATE).resourcePath(uriInfo).representation(roles).success();
@@ -183,18 +182,14 @@ public class ClientRoleMappingsResource {
}
/**
- * Delete client-level roles from user role mapping
- *
- * @param roles
- */
+ * Delete client-level roles from user role mapping
+ *
+ * @param roles
+ */
@DELETE
@Consumes(MediaType.APPLICATION_JSON)
public void deleteClientRoleMapping(List<RoleRepresentation> roles) {
- auth.requireManage();
-
- if (user == null || client == null) {
- throw new NotFoundException("Not found");
- }
+ managePermission.require();
if (roles == null) {
Set<RoleModel> roleModels = user.getClientRoleMappings(client);
@@ -205,6 +200,7 @@ public class ClientRoleMappingsResource {
ClientModel client = (ClientModel) roleModel.getContainer();
if (!client.getId().equals(this.client.getId())) continue;
}
+ auth.roles().requireMapRole(roleModel);
user.deleteRoleMapping(roleModel);
roles.add(ModelToRepresentation.toRepresentation(roleModel));
}
@@ -216,10 +212,11 @@ public class ClientRoleMappingsResource {
throw new NotFoundException("Role not found");
}
+ auth.roles().requireMapRole(roleModel);
try {
user.deleteRoleMapping(roleModel);
} catch (ModelException me) {
- Properties messages = AdminRoot.getMessages(session, realm, auth.getAuth().getToken().getLocale());
+ Properties messages = AdminRoot.getMessages(session, realm, auth.adminAuth().getToken().getLocale());
throw new ErrorResponseException(me.getMessage(), MessageFormat.format(messages.getProperty(me.getMessage(), me.getMessage()), me.getParameters()),
Response.Status.BAD_REQUEST);
}
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ClientsResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ClientsResource.java
index a488ba3..decb4da 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/ClientsResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ClientsResource.java
@@ -26,6 +26,7 @@ import org.keycloak.common.Profile;
import org.keycloak.events.admin.OperationType;
import org.keycloak.events.admin.ResourceType;
import org.keycloak.models.ClientModel;
+import org.keycloak.models.Constants;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ModelDuplicateException;
import org.keycloak.models.RealmModel;
@@ -34,14 +35,18 @@ import org.keycloak.models.utils.ModelToRepresentation;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.services.ErrorResponse;
import org.keycloak.services.ErrorResponseException;
+import org.keycloak.services.ForbiddenException;
import org.keycloak.services.managers.ClientManager;
import org.keycloak.services.managers.RealmManager;
+import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
import org.keycloak.services.validation.ClientValidator;
import org.keycloak.services.validation.PairwiseClientValidator;
import org.keycloak.services.validation.ValidationMessages;
import javax.ws.rs.Consumes;
+import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
+import javax.ws.rs.NotFoundException;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
@@ -65,18 +70,17 @@ import java.util.Properties;
public class ClientsResource {
protected static final Logger logger = Logger.getLogger(ClientsResource.class);
protected RealmModel realm;
- private RealmAuth auth;
+ private AdminPermissionEvaluator auth;
private AdminEventBuilder adminEvent;
@Context
protected KeycloakSession session;
- public ClientsResource(RealmModel realm, RealmAuth auth, AdminEventBuilder adminEvent) {
+ public ClientsResource(RealmModel realm, AdminPermissionEvaluator auth, AdminEventBuilder adminEvent) {
this.realm = realm;
this.auth = auth;
this.adminEvent = adminEvent.resource(ResourceType.CLIENT);
- auth.init(RealmAuth.Resource.CLIENT);
}
/**
@@ -85,21 +89,20 @@ public class ClientsResource {
* Returns a list of clients belonging to the realm
*
* @param clientId filter by clientId
+ * @param viewableOnly filter clients that cannot be viewed in full by admin
*/
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
- public List<ClientRepresentation> getClients(@QueryParam("clientId") String clientId) {
- auth.requireAny();
-
+ public List<ClientRepresentation> getClients(@QueryParam("clientId") String clientId, @QueryParam("viewableOnly") @DefaultValue("false") boolean viewableOnly) {
List<ClientRepresentation> rep = new ArrayList<>();
if (clientId == null) {
List<ClientModel> clientModels = realm.getClients();
-
- boolean view = auth.hasView();
+ auth.clients().requireList();
+ boolean view = auth.clients().canView();
for (ClientModel clientModel : clientModels) {
- if (view) {
+ if (view || auth.clients().canView(clientModel)) {
ClientRepresentation representation = ModelToRepresentation.toRepresentation(clientModel);
if (Profile.isFeatureEnabled(Profile.Feature.AUTHORIZATION)) {
@@ -111,7 +114,8 @@ public class ClientsResource {
}
rep.add(representation);
- } else {
+ representation.setAccess(auth.clients().getAccess(clientModel));
+ } else if (!viewableOnly) {
ClientRepresentation client = new ClientRepresentation();
client.setId(clientModel.getId());
client.setClientId(clientModel.getClientId());
@@ -120,9 +124,22 @@ public class ClientsResource {
}
}
} else {
- ClientModel client = realm.getClientByClientId(clientId);
- if (client != null) {
- rep.add(ModelToRepresentation.toRepresentation(client));
+ ClientModel clientModel = realm.getClientByClientId(clientId);
+ if (clientModel != null) {
+ if (auth.clients().canView(clientModel)) {
+ ClientRepresentation representation = ModelToRepresentation.toRepresentation(clientModel);
+ representation.setAccess(auth.clients().getAccess(clientModel));
+ rep.add(representation);
+ } else if (!viewableOnly && auth.clients().canList()){
+ ClientRepresentation client = new ClientRepresentation();
+ client.setId(clientModel.getId());
+ client.setClientId(clientModel.getClientId());
+ client.setDescription(clientModel.getDescription());
+ rep.add(client);
+
+ } else {
+ throw new ForbiddenException();
+ }
}
}
return rep;
@@ -144,11 +161,11 @@ public class ClientsResource {
@POST
@Consumes(MediaType.APPLICATION_JSON)
public Response createClient(final @Context UriInfo uriInfo, final ClientRepresentation rep) {
- auth.requireManage();
+ auth.clients().requireManage();
ValidationMessages validationMessages = new ValidationMessages();
if (!ClientValidator.validate(rep, validationMessages) || !PairwiseClientValidator.validate(session, rep, validationMessages)) {
- Properties messages = AdminRoot.getMessages(session, realm, auth.getAuth().getToken().getLocale());
+ Properties messages = AdminRoot.getMessages(session, realm, auth.adminAuth().getToken().getLocale());
throw new ErrorResponseException(
validationMessages.getStringMessages(),
validationMessages.getStringMessages(messages),
@@ -189,7 +206,13 @@ public class ClientsResource {
*/
@Path("{id}")
public ClientResource getClient(final @PathParam("id") String id) {
+
ClientModel clientModel = realm.getClientById(id);
+ if (clientModel == null) {
+ // we do this to make sure somebody can't phish ids
+ if (auth.clients().canList()) throw new NotFoundException("Could not find client");
+ else throw new ForbiddenException();
+ }
session.getContext().setClient(clientModel);
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ClientTemplateResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ClientTemplateResource.java
index f760a41..f076850 100644
--- a/services/src/main/java/org/keycloak/services/resources/admin/ClientTemplateResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ClientTemplateResource.java
@@ -31,6 +31,7 @@ import org.keycloak.models.utils.ModelToRepresentation;
import org.keycloak.models.utils.RepresentationToModel;
import org.keycloak.representations.idm.ClientTemplateRepresentation;
import org.keycloak.services.ErrorResponse;
+import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
@@ -54,7 +55,7 @@ import javax.ws.rs.core.UriInfo;
public class ClientTemplateResource {
protected static final Logger logger = Logger.getLogger(ClientTemplateResource.class);
protected RealmModel realm;
- private RealmAuth auth;
+ private AdminPermissionEvaluator auth;
private AdminEventBuilder adminEvent;
protected ClientTemplateModel template;
protected KeycloakSession session;
@@ -62,19 +63,20 @@ public class ClientTemplateResource {
@Context
protected UriInfo uriInfo;
- public ClientTemplateResource(RealmModel realm, RealmAuth auth, ClientTemplateModel template, KeycloakSession session, AdminEventBuilder adminEvent) {
+ public ClientTemplateResource(RealmModel realm, AdminPermissionEvaluator auth, ClientTemplateModel template, KeycloakSession session, AdminEventBuilder adminEvent) {
this.realm = realm;
this.auth = auth;
this.template = template;
this.session = session;
this.adminEvent = adminEvent.resource(ResourceType.CLIENT_TEMPLATE);
- auth.init(RealmAuth.Resource.CLIENT);
}
@Path("protocol-mappers")
public ProtocolMappersResource getProtocolMappers() {
- ProtocolMappersResource mappers = new ProtocolMappersResource(realm, template, auth, adminEvent);
+ AdminPermissionEvaluator.RequirePermissionCheck manageCheck = () -> auth.clients().requireManage(template);
+ AdminPermissionEvaluator.RequirePermissionCheck viewCheck = () -> auth.clients().requireView(template);
+ ProtocolMappersResource mappers = new ProtocolMappersResource(realm, template, auth, adminEvent, manageCheck, viewCheck);
ResteasyProviderFactory.getInstance().injectProperties(mappers);
return mappers;
}
@@ -86,7 +88,9 @@ public class ClientTemplateResource {
*/
@Path("scope-mappings")
public ScopeMappedResource getScopeMappedResource() {
- return new ScopeMappedResource(realm, auth, template, session, adminEvent);
+ AdminPermissionEvaluator.RequirePermissionCheck manageCheck = () -> auth.clients().requireManage(template);
+ AdminPermissionEvaluator.RequirePermissionCheck viewCheck = () -> auth.clients().requireView(template);
+ return new ScopeMappedResource(realm, auth, template, session, adminEvent, manageCheck, viewCheck);
}
/**
@@ -97,11 +101,7 @@ public class ClientTemplateResource {
@PUT
@Consumes(MediaType.APPLICATION_JSON)
public Response update(final ClientTemplateRepresentation rep) {
- auth.requireManage();
-
- if (template == null) {
- throw new NotFoundException("Could not find client template");
- }
+ auth.clients().requireManageTemplates();
try {
RepresentationToModel.updateClientTemplate(rep, template);
@@ -125,11 +125,8 @@ public class ClientTemplateResource {
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public ClientTemplateRepresentation getClient() {
- auth.requireView();
+ auth.clients().requireView(template);
- if (template == null) {
- throw new NotFoundException("Could not find client template");
- }
return ModelToRepresentation.toRepresentation(template);
}
@@ -141,11 +138,7 @@ public class ClientTemplateResource {
@DELETE
@NoCache
public Response deleteClientTemplate() {
- auth.requireManage();
-
- if (template == null) {
- throw new NotFoundException("Could not find client template");
- }
+ auth.clients().requireManage(template);
try {
realm.removeClientTemplate(template.getId());
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ClientTemplatesResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ClientTemplatesResource.java
index 5e27712..1e8105d 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/ClientTemplatesResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ClientTemplatesResource.java
@@ -18,6 +18,7 @@ package org.keycloak.services.resources.admin;
import org.jboss.logging.Logger;
import org.jboss.resteasy.annotations.cache.NoCache;
+import org.jboss.resteasy.spi.NotFoundException;
import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.keycloak.events.admin.OperationType;
import org.keycloak.events.admin.ResourceType;
@@ -29,6 +30,7 @@ import org.keycloak.models.utils.ModelToRepresentation;
import org.keycloak.models.utils.RepresentationToModel;
import org.keycloak.representations.idm.ClientTemplateRepresentation;
import org.keycloak.services.ErrorResponse;
+import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
@@ -53,18 +55,16 @@ import java.util.List;
public class ClientTemplatesResource {
protected static final Logger logger = Logger.getLogger(ClientTemplatesResource.class);
protected RealmModel realm;
- private RealmAuth auth;
+ private AdminPermissionEvaluator auth;
private AdminEventBuilder adminEvent;
@Context
protected KeycloakSession session;
- public ClientTemplatesResource(RealmModel realm, RealmAuth auth, AdminEventBuilder adminEvent) {
+ public ClientTemplatesResource(RealmModel realm, AdminPermissionEvaluator auth, AdminEventBuilder adminEvent) {
this.realm = realm;
this.auth = auth;
this.adminEvent = adminEvent.resource(ResourceType.CLIENT_TEMPLATE);
-
- auth.init(RealmAuth.Resource.CLIENT);
}
/**
@@ -76,22 +76,19 @@ public class ClientTemplatesResource {
@Produces(MediaType.APPLICATION_JSON)
@NoCache
public List<ClientTemplateRepresentation> getClientTemplates() {
- auth.requireView();
+ auth.clients().requireListTemplates();
List<ClientTemplateRepresentation> rep = new ArrayList<>();
List<ClientTemplateModel> clientModels = realm.getClientTemplates();
- boolean view = auth.hasView();
+ boolean viewable = auth.clients().canViewTemplates();
for (ClientTemplateModel clientModel : clientModels) {
- if (view) {
- rep.add(ModelToRepresentation.toRepresentation(clientModel));
- } else {
- ClientTemplateRepresentation client = new ClientTemplateRepresentation();
- client.setId(clientModel.getId());
- client.setName(clientModel.getName());
- client.setDescription(clientModel.getDescription());
- client.setProtocol(clientModel.getProtocol());
- rep.add(client);
+ if (viewable) rep.add(ModelToRepresentation.toRepresentation(clientModel));
+ else {
+ ClientTemplateRepresentation tempRep = new ClientTemplateRepresentation();
+ tempRep.setName(clientModel.getName());
+ tempRep.setId(clientModel.getId());
+ tempRep.setProtocol(clientModel.getProtocol());
}
}
return rep;
@@ -109,7 +106,7 @@ public class ClientTemplatesResource {
@POST
@Consumes(MediaType.APPLICATION_JSON)
public Response createClientTemplate(final @Context UriInfo uriInfo, final ClientTemplateRepresentation rep) {
- auth.requireManage();
+ auth.clients().requireManageTemplates();
try {
ClientTemplateModel clientModel = RepresentationToModel.createClientTemplate(session, realm, rep);
@@ -130,7 +127,11 @@ public class ClientTemplatesResource {
*/
@Path("{id}")
public ClientTemplateResource getClient(final @PathParam("id") String id) {
+ auth.clients().requireListTemplates();
ClientTemplateModel clientModel = realm.getClientTemplateById(id);
+ if (clientModel == null) {
+ throw new NotFoundException("Could not find client template");
+ }
ClientTemplateResource clientResource = new ClientTemplateResource(realm, auth, clientModel, session, adminEvent);
ResteasyProviderFactory.getInstance().injectProperties(clientResource);
return clientResource;
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ComponentResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ComponentResource.java
index b39b773..c532245 100644
--- a/services/src/main/java/org/keycloak/services/resources/admin/ComponentResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ComponentResource.java
@@ -38,7 +38,7 @@ import org.keycloak.representations.idm.ComponentRepresentation;
import org.keycloak.representations.idm.ComponentTypeRepresentation;
import org.keycloak.representations.idm.ConfigPropertyRepresentation;
import org.keycloak.services.ErrorResponse;
-import org.keycloak.services.ErrorResponseException;
+import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
import javax.ws.rs.BadRequestException;
import javax.ws.rs.Consumes;
@@ -63,7 +63,6 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
-import java.util.stream.Collectors;
/**
* @resource Component
@@ -75,7 +74,7 @@ public class ComponentResource {
protected RealmModel realm;
- private RealmAuth auth;
+ private AdminPermissionEvaluator auth;
private AdminEventBuilder adminEvent;
@@ -91,12 +90,10 @@ public class ComponentResource {
@Context
protected HttpHeaders headers;
- public ComponentResource(RealmModel realm, RealmAuth auth, AdminEventBuilder adminEvent) {
+ public ComponentResource(RealmModel realm, AdminPermissionEvaluator auth, AdminEventBuilder adminEvent) {
this.auth = auth;
this.realm = realm;
this.adminEvent = adminEvent.resource(ResourceType.COMPONENT);
-
- auth.init(RealmAuth.Resource.REALM);
}
@GET
@@ -105,7 +102,7 @@ public class ComponentResource {
public List<ComponentRepresentation> getComponents(@QueryParam("parent") String parent,
@QueryParam("type") String type,
@QueryParam("name") String name) {
- auth.requireView();
+ auth.realm().requireViewRealm();
List<ComponentModel> components = Collections.EMPTY_LIST;
if (parent == null && type == null) {
components = realm.getComponents();
@@ -135,7 +132,7 @@ public class ComponentResource {
@POST
@Consumes(MediaType.APPLICATION_JSON)
public Response create(ComponentRepresentation rep) {
- auth.requireManage();
+ auth.realm().requireManageRealm();
try {
ComponentModel model = RepresentationToModel.toModel(session, rep);
if (model.getParentId() == null) model.setParentId(realm.getId());
@@ -156,7 +153,7 @@ public class ComponentResource {
@Produces(MediaType.APPLICATION_JSON)
@NoCache
public ComponentRepresentation getComponent(@PathParam("id") String id) {
- auth.requireView();
+ auth.realm().requireViewRealm();
ComponentModel model = realm.getComponent(id);
if (model == null) {
throw new NotFoundException("Could not find component");
@@ -169,7 +166,7 @@ public class ComponentResource {
@Path("{id}")
@Consumes(MediaType.APPLICATION_JSON)
public Response updateComponent(@PathParam("id") String id, ComponentRepresentation rep) {
- auth.requireManage();
+ auth.realm().requireManageRealm();
try {
ComponentModel model = realm.getComponent(id);
if (model == null) {
@@ -188,7 +185,7 @@ public class ComponentResource {
@DELETE
@Path("{id}")
public void removeComponent(@PathParam("id") String id) {
- auth.requireManage();
+ auth.realm().requireManageRealm();
ComponentModel model = realm.getComponent(id);
if (model == null) {
throw new NotFoundException("Could not find component");
@@ -199,7 +196,7 @@ public class ComponentResource {
}
private Response localizedErrorResponse(ComponentValidationException cve) {
- Properties messages = AdminRoot.getMessages(session, realm, auth.getAuth().getToken().getLocale(), "admin-messages", "messages");
+ Properties messages = AdminRoot.getMessages(session, realm, auth.adminAuth().getToken().getLocale(), "admin-messages", "messages");
Object[] localizedParameters = cve.getParameters()==null ? null : Arrays.asList(cve.getParameters()).stream().map((Object parameter) -> {
@@ -228,7 +225,7 @@ public class ComponentResource {
@Produces(MediaType.APPLICATION_JSON)
@NoCache
public List<ComponentTypeRepresentation> getSubcomponentConfig(@PathParam("id") String parentId, @QueryParam("type") String subtype) {
- auth.requireView();
+ auth.realm().requireViewRealm();
ComponentModel parent = realm.getComponent(parentId);
if (parent == null) {
throw new NotFoundException("Could not find parent component");
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/GroupResource.java b/services/src/main/java/org/keycloak/services/resources/admin/GroupResource.java
index c9fe194..3de46b0 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/GroupResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/GroupResource.java
@@ -21,6 +21,7 @@ import org.jboss.resteasy.spi.NotFoundException;
import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.keycloak.events.admin.OperationType;
import org.keycloak.events.admin.ResourceType;
+import org.keycloak.models.ClientModel;
import org.keycloak.models.Constants;
import org.keycloak.models.GroupModel;
import org.keycloak.models.KeycloakSession;
@@ -28,6 +29,7 @@ import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.utils.ModelToRepresentation;
import org.keycloak.representations.idm.GroupRepresentation;
+import org.keycloak.representations.idm.ManagementPermissionReference;
import org.keycloak.representations.idm.UserRepresentation;
import javax.ws.rs.Consumes;
@@ -49,6 +51,9 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import org.keycloak.services.ErrorResponse;
+import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
+import org.keycloak.services.resources.admin.permissions.AdminPermissionManagement;
+import org.keycloak.services.resources.admin.permissions.AdminPermissions;
/**
* @resource Groups
@@ -58,11 +63,11 @@ public class GroupResource {
private final RealmModel realm;
private final KeycloakSession session;
- private final RealmAuth auth;
+ private final AdminPermissionEvaluator auth;
private final AdminEventBuilder adminEvent;
private final GroupModel group;
- public GroupResource(RealmModel realm, GroupModel group, KeycloakSession session, RealmAuth auth, AdminEventBuilder adminEvent) {
+ public GroupResource(RealmModel realm, GroupModel group, KeycloakSession session, AdminPermissionEvaluator auth, AdminEventBuilder adminEvent) {
this.realm = realm;
this.session = session;
this.auth = auth;
@@ -72,7 +77,7 @@ public class GroupResource {
@Context private UriInfo uriInfo;
- /**
+ /**
*
*
* @return
@@ -81,13 +86,13 @@ public class GroupResource {
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public GroupRepresentation getGroup() {
- this.auth.requireView();
+ this.auth.groups().requireView(group);
- if (group == null) {
- throw new NotFoundException("Could not find group by id");
- }
+ GroupRepresentation rep = ModelToRepresentation.toGroupHierarchy(group, true);
+
+ rep.setAccess(auth.groups().getAccess(group));
- return ModelToRepresentation.toGroupHierarchy(group, true);
+ return rep;
}
/**
@@ -98,11 +103,7 @@ public class GroupResource {
@PUT
@Consumes(MediaType.APPLICATION_JSON)
public void updateGroup(GroupRepresentation rep) {
- this.auth.requireManage();
-
- if (group == null) {
- throw new NotFoundException("Could not find group by id");
- }
+ this.auth.groups().requireManage(group);
updateGroup(rep, group);
adminEvent.operation(OperationType.UPDATE).resourcePath(uriInfo).representation(rep).success();
@@ -112,11 +113,7 @@ public class GroupResource {
@DELETE
public void deleteGroup() {
- this.auth.requireManage();
-
- if (group == null) {
- throw new NotFoundException("Could not find group by id");
- }
+ this.auth.groups().requireManage(group);
realm.removeGroup(group);
adminEvent.operation(OperationType.DELETE).resourcePath(uriInfo).success();
@@ -135,12 +132,8 @@ public class GroupResource {
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public Response addChild(GroupRepresentation rep) {
- this.auth.requireManage();
+ this.auth.groups().requireManage(group);
- if (group == null) {
- throw new NotFoundException("Could not find group by id");
- }
-
for (GroupModel group : group.getSubGroups()) {
if (group.getName().equals(rep.getName())) {
return ErrorResponse.exists("Parent already contains subgroup named '" + rep.getName() + "'");
@@ -191,9 +184,9 @@ public class GroupResource {
@Path("role-mappings")
public RoleMapperResource getRoleMappings() {
- auth.init(RealmAuth.Resource.USER);
-
- RoleMapperResource resource = new RoleMapperResource(realm, auth, group, adminEvent);
+ AdminPermissionEvaluator.RequirePermissionCheck manageCheck = () -> auth.groups().requireManage(group);
+ AdminPermissionEvaluator.RequirePermissionCheck viewCheck = () -> auth.groups().requireView(group);
+ RoleMapperResource resource = new RoleMapperResource(realm, auth, group, adminEvent, manageCheck, viewCheck);
ResteasyProviderFactory.getInstance().injectProperties(resource);
return resource;
@@ -214,11 +207,8 @@ public class GroupResource {
@Produces(MediaType.APPLICATION_JSON)
public List<UserRepresentation> getMembers(@QueryParam("first") Integer firstResult,
@QueryParam("max") Integer maxResults) {
- auth.requireView();
+ this.auth.groups().requireViewMembers(group);
- if (group == null) {
- throw new NotFoundException("Could not find group by id");
- }
firstResult = firstResult != null ? firstResult : 0;
maxResults = maxResults != null ? maxResults : Constants.DEFAULT_MAX_RESULTS;
@@ -232,4 +222,55 @@ public class GroupResource {
return results;
}
+ /**
+ * Return object stating whether client Authorization permissions have been initialized or not and a reference
+ *
+ * @return
+ */
+ @Path("management/permissions")
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ @NoCache
+ public ManagementPermissionReference getManagementPermissions() {
+ auth.groups().requireView(group);
+
+ AdminPermissionManagement permissions = AdminPermissions.management(session, realm);
+ if (!permissions.groups().isPermissionsEnabled(group)) {
+ return new ManagementPermissionReference();
+ }
+ return toMgmtRef(group, permissions);
+ }
+
+ public static ManagementPermissionReference toMgmtRef(GroupModel group, AdminPermissionManagement permissions) {
+ ManagementPermissionReference ref = new ManagementPermissionReference();
+ ref.setEnabled(true);
+ ref.setResource(permissions.groups().resource(group).getId());
+ ref.setScopePermissions(permissions.groups().getPermissions(group));
+ return ref;
+ }
+
+
+ /**
+ * Return object stating whether client Authorization permissions have been initialized or not and a reference
+ *
+ *
+ * @return initialized manage permissions reference
+ */
+ @Path("management/permissions")
+ @PUT
+ @Produces(MediaType.APPLICATION_JSON)
+ @Consumes(MediaType.APPLICATION_JSON)
+ @NoCache
+ public ManagementPermissionReference setManagementPermissionsEnabled(ManagementPermissionReference ref) {
+ auth.groups().requireManage(group);
+ if (ref.isEnabled()) {
+ AdminPermissionManagement permissions = AdminPermissions.management(session, realm);
+ permissions.groups().setPermissionsEnabled(group, ref.isEnabled());
+ return toMgmtRef(group, permissions);
+ } else {
+ return new ManagementPermissionReference();
+ }
+ }
+
}
+
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/GroupsResource.java b/services/src/main/java/org/keycloak/services/resources/admin/GroupsResource.java
index 5be1c0d..2a1909b 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/GroupsResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/GroupsResource.java
@@ -40,6 +40,7 @@ import javax.ws.rs.core.UriInfo;
import java.net.URI;
import java.util.List;
import org.keycloak.services.ErrorResponse;
+import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
/**
* @resource Groups
@@ -49,15 +50,14 @@ public class GroupsResource {
private final RealmModel realm;
private final KeycloakSession session;
- private final RealmAuth auth;
+ private final AdminPermissionEvaluator auth;
private final AdminEventBuilder adminEvent;
- public GroupsResource(RealmModel realm, KeycloakSession session, RealmAuth auth, AdminEventBuilder adminEvent) {
+ public GroupsResource(RealmModel realm, KeycloakSession session, AdminPermissionEvaluator auth, AdminEventBuilder adminEvent) {
this.realm = realm;
this.session = session;
this.auth = auth;
this.adminEvent = adminEvent.resource(ResourceType.GROUP);
- auth.init(RealmAuth.Resource.USER);
}
@@ -72,7 +72,7 @@ public class GroupsResource {
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public List<GroupRepresentation> getGroups() {
- auth.requireView();
+ auth.groups().requireList();
return ModelToRepresentation.toGroupHierarchy(realm, false);
}
@@ -85,9 +85,10 @@ public class GroupsResource {
*/
@Path("{id}")
public GroupResource getGroupById(@PathParam("id") String id) {
- auth.requireView();
-
GroupModel group = realm.getGroupById(id);
+ if (group == null) {
+ throw new NotFoundException("Could not find group by id");
+ }
GroupResource resource = new GroupResource(realm, group, session, this.auth, adminEvent);
ResteasyProviderFactory.getInstance().injectProperties(resource);
return resource;
@@ -102,7 +103,7 @@ public class GroupsResource {
@POST
@Consumes(MediaType.APPLICATION_JSON)
public Response addTopLevelGroup(GroupRepresentation rep) {
- auth.requireManage();
+ auth.groups().requireManage();
for (GroupModel group : realm.getGroups()) {
if (group.getName().equals(rep.getName())) {
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/IdentityProviderResource.java b/services/src/main/java/org/keycloak/services/resources/admin/IdentityProviderResource.java
index aa4a054..4c08c20 100644
--- a/services/src/main/java/org/keycloak/services/resources/admin/IdentityProviderResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/IdentityProviderResource.java
@@ -44,6 +44,7 @@ import org.keycloak.representations.idm.IdentityProviderMapperRepresentation;
import org.keycloak.representations.idm.IdentityProviderMapperTypeRepresentation;
import org.keycloak.representations.idm.IdentityProviderRepresentation;
import org.keycloak.services.ErrorResponse;
+import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
@@ -72,7 +73,7 @@ public class IdentityProviderResource {
protected static final Logger logger = Logger.getLogger(IdentityProviderResource.class);
- private final RealmAuth auth;
+ private final AdminPermissionEvaluator auth;
private final RealmModel realm;
private final KeycloakSession session;
private final IdentityProviderModel identityProviderModel;
@@ -80,7 +81,7 @@ public class IdentityProviderResource {
@Context private UriInfo uriInfo;
- public IdentityProviderResource(RealmAuth auth, RealmModel realm, KeycloakSession session, IdentityProviderModel identityProviderModel, AdminEventBuilder adminEvent) {
+ public IdentityProviderResource(AdminPermissionEvaluator auth, RealmModel realm, KeycloakSession session, IdentityProviderModel identityProviderModel, AdminEventBuilder adminEvent) {
this.realm = realm;
this.session = session;
this.identityProviderModel = identityProviderModel;
@@ -97,7 +98,7 @@ public class IdentityProviderResource {
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public IdentityProviderRepresentation getIdentityProvider() {
- this.auth.requireView();
+ this.auth.realm().requireViewIdentityProviders();
if (identityProviderModel == null) {
throw new javax.ws.rs.NotFoundException();
@@ -115,7 +116,7 @@ public class IdentityProviderResource {
@DELETE
@NoCache
public Response delete() {
- this.auth.requireManage();
+ this.auth.realm().requireManageIdentityProviders();
if (identityProviderModel == null) {
throw new javax.ws.rs.NotFoundException();
@@ -138,7 +139,7 @@ public class IdentityProviderResource {
@Consumes(MediaType.APPLICATION_JSON)
@NoCache
public Response update(IdentityProviderRepresentation providerRep) {
- this.auth.requireManage();
+ this.auth.realm().requireManageIdentityProviders();
if (identityProviderModel == null) {
throw new javax.ws.rs.NotFoundException();
@@ -229,7 +230,7 @@ public class IdentityProviderResource {
@Path("export")
@NoCache
public Response export(@Context UriInfo uriInfo, @QueryParam("format") String format) {
- this.auth.requireView();
+ this.auth.realm().requireViewIdentityProviders();
if (identityProviderModel == null) {
throw new javax.ws.rs.NotFoundException();
@@ -250,7 +251,7 @@ public class IdentityProviderResource {
@Path("mapper-types")
@NoCache
public Map<String, IdentityProviderMapperTypeRepresentation> getMapperTypes() {
- this.auth.requireView();
+ this.auth.realm().requireViewIdentityProviders();
if (identityProviderModel == null) {
throw new javax.ws.rs.NotFoundException();
@@ -289,7 +290,7 @@ public class IdentityProviderResource {
@Produces(MediaType.APPLICATION_JSON)
@NoCache
public List<IdentityProviderMapperRepresentation> getMappers() {
- this.auth.requireView();
+ this.auth.realm().requireViewIdentityProviders();
if (identityProviderModel == null) {
throw new javax.ws.rs.NotFoundException();
@@ -312,7 +313,7 @@ public class IdentityProviderResource {
@Path("mappers")
@Consumes(MediaType.APPLICATION_JSON)
public Response addMapper(IdentityProviderMapperRepresentation mapper) {
- auth.requireManage();
+ this.auth.realm().requireManageIdentityProviders();
if (identityProviderModel == null) {
throw new javax.ws.rs.NotFoundException();
@@ -343,7 +344,7 @@ public class IdentityProviderResource {
@Path("mappers/{id}")
@Produces(MediaType.APPLICATION_JSON)
public IdentityProviderMapperRepresentation getMapperById(@PathParam("id") String id) {
- auth.requireView();
+ this.auth.realm().requireViewIdentityProviders();
if (identityProviderModel == null) {
throw new javax.ws.rs.NotFoundException();
@@ -365,7 +366,7 @@ public class IdentityProviderResource {
@Path("mappers/{id}")
@Consumes(MediaType.APPLICATION_JSON)
public void update(@PathParam("id") String id, IdentityProviderMapperRepresentation rep) {
- auth.requireManage();
+ this.auth.realm().requireManageIdentityProviders();
if (identityProviderModel == null) {
throw new javax.ws.rs.NotFoundException();
@@ -388,7 +389,7 @@ public class IdentityProviderResource {
@NoCache
@Path("mappers/{id}")
public void delete(@PathParam("id") String id) {
- auth.requireManage();
+ this.auth.realm().requireManageIdentityProviders();
if (identityProviderModel == null) {
throw new javax.ws.rs.NotFoundException();
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/IdentityProvidersResource.java b/services/src/main/java/org/keycloak/services/resources/admin/IdentityProvidersResource.java
index c5250ac..646b463 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/IdentityProvidersResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/IdentityProvidersResource.java
@@ -37,6 +37,7 @@ import org.keycloak.models.utils.StripSecretsUtils;
import org.keycloak.provider.ProviderFactory;
import org.keycloak.representations.idm.IdentityProviderRepresentation;
import org.keycloak.services.ErrorResponse;
+import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
import javax.ws.rs.BadRequestException;
import javax.ws.rs.Consumes;
@@ -65,14 +66,13 @@ public class IdentityProvidersResource {
private final RealmModel realm;
private final KeycloakSession session;
- private RealmAuth auth;
+ private AdminPermissionEvaluator auth;
private AdminEventBuilder adminEvent;
- public IdentityProvidersResource(RealmModel realm, KeycloakSession session, RealmAuth auth, AdminEventBuilder adminEvent) {
+ public IdentityProvidersResource(RealmModel realm, KeycloakSession session, AdminPermissionEvaluator auth, AdminEventBuilder adminEvent) {
this.realm = realm;
this.session = session;
this.auth = auth;
- this.auth.init(RealmAuth.Resource.IDENTITY_PROVIDER);
this.adminEvent = adminEvent.resource(ResourceType.IDENTITY_PROVIDER);
}
@@ -87,7 +87,7 @@ public class IdentityProvidersResource {
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public Response getIdentityProviders(@PathParam("provider_id") String providerId) {
- this.auth.requireView();
+ this.auth.realm().requireViewIdentityProviders();
IdentityProviderFactory providerFactory = getProviderFactorytById(providerId);
if (providerFactory != null) {
return Response.ok(providerFactory).build();
@@ -108,7 +108,7 @@ public class IdentityProvidersResource {
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Produces(MediaType.APPLICATION_JSON)
public Map<String, String> importFrom(@Context UriInfo uriInfo, MultipartFormDataInput input) throws IOException {
- this.auth.requireManage();
+ this.auth.realm().requireManageIdentityProviders();
Map<String, List<InputPart>> formDataMap = input.getFormDataMap();
if (!(formDataMap.containsKey("providerId") && formDataMap.containsKey("file"))) {
throw new BadRequestException();
@@ -134,7 +134,7 @@ public class IdentityProvidersResource {
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Map<String, String> importFrom(@Context UriInfo uriInfo, Map<String, Object> data) throws IOException {
- this.auth.requireManage();
+ this.auth.realm().requireManageIdentityProviders();
if (!(data.containsKey("providerId") && data.containsKey("fromUrl"))) {
throw new BadRequestException();
}
@@ -164,7 +164,7 @@ public class IdentityProvidersResource {
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public List<IdentityProviderRepresentation> getIdentityProviders() {
- this.auth.requireView();
+ this.auth.realm().requireViewIdentityProviders();
List<IdentityProviderRepresentation> representations = new ArrayList<IdentityProviderRepresentation>();
@@ -185,7 +185,7 @@ public class IdentityProvidersResource {
@Path("instances")
@Consumes(MediaType.APPLICATION_JSON)
public Response create(@Context UriInfo uriInfo, IdentityProviderRepresentation representation) {
- this.auth.requireManage();
+ this.auth.realm().requireManageIdentityProviders();
try {
IdentityProviderModel identityProvider = RepresentationToModel.toModel(realm, representation);
@@ -203,7 +203,7 @@ public class IdentityProvidersResource {
@Path("instances/{alias}")
public IdentityProviderResource getIdentityProvider(@PathParam("alias") String alias) {
- this.auth.requireView();
+ this.auth.realm().requireViewIdentityProviders();
IdentityProviderModel identityProviderModel = null;
for (IdentityProviderModel storedIdentityProvider : this.realm.getIdentityProviders()) {
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/KeyResource.java b/services/src/main/java/org/keycloak/services/resources/admin/KeyResource.java
index f2c9401..d990fd1 100644
--- a/services/src/main/java/org/keycloak/services/resources/admin/KeyResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/KeyResource.java
@@ -26,6 +26,7 @@ import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeyManager;
import org.keycloak.models.RealmModel;
import org.keycloak.representations.idm.KeysMetadataRepresentation;
+import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
import javax.ws.rs.GET;
import javax.ws.rs.Produces;
@@ -43,9 +44,9 @@ public class KeyResource {
private RealmModel realm;
private KeycloakSession session;
- private RealmAuth auth;
+ private AdminPermissionEvaluator auth;
- public KeyResource(RealmModel realm, KeycloakSession session, RealmAuth auth) {
+ public KeyResource(RealmModel realm, KeycloakSession session, AdminPermissionEvaluator auth) {
this.realm = realm;
this.session = session;
this.auth = auth;
@@ -55,7 +56,7 @@ public class KeyResource {
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public KeysMetadataRepresentation getKeyMetadata() {
- auth.requireView();
+ auth.realm().requireViewRealm();
KeyManager keystore = session.keys();
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/permissions/AdminPermissionEvaluator.java b/services/src/main/java/org/keycloak/services/resources/admin/permissions/AdminPermissionEvaluator.java
new file mode 100644
index 0000000..19abaf4
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/resources/admin/permissions/AdminPermissionEvaluator.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.services.resources.admin.permissions;
+
+import org.keycloak.services.resources.admin.AdminAuth;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public interface AdminPermissionEvaluator {
+ RealmPermissionEvaluator realm();
+
+ void requireAnyAdminRole();
+
+ AdminAuth adminAuth();
+
+ RolePermissionEvaluator roles();
+ UserPermissionEvaluator users();
+ ClientPermissionEvaluator clients();
+ GroupPermissionEvaluator groups();
+
+ /**
+ * Useful as a function pointer, i.e. RoleMapperResource is reused bewteen GroupResource and UserResource to manage role mappings.
+ * We don't know what type of resource we're managing here (user or group), so we don't know how to query the policy engine to determine
+ * if an action is allowed.
+ *
+ */
+ interface PermissionCheck {
+ boolean evaluate();
+ }
+ /**
+ * Useful as a function pointer, i.e. RoleMapperResource is reused bewteen GroupResource and UserResource to manage role mappings.
+ * We don't know what type of resource we're managing here (user or group), so we don't know how to query the policy engine to determine
+ * if an action is allowed.
+ *
+ * throws appropriate exception if permission is deny
+ *
+ */
+ interface RequirePermissionCheck {
+ void require();
+ }
+}
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/permissions/AdminPermissionManagement.java b/services/src/main/java/org/keycloak/services/resources/admin/permissions/AdminPermissionManagement.java
new file mode 100644
index 0000000..2a94132
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/resources/admin/permissions/AdminPermissionManagement.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.services.resources.admin.permissions;
+
+import org.keycloak.authorization.AuthorizationProvider;
+import org.keycloak.authorization.model.ResourceServer;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public interface AdminPermissionManagement {
+ public static final String MANAGE_SCOPE = "manage";
+ public static final String VIEW_SCOPE = "view";
+
+ AuthorizationProvider authz();
+
+ RolePermissionManagement roles();
+ UserPermissionManagement users();
+ GroupPermissionManagement groups();
+ ClientPermissionManagement clients();
+
+ ResourceServer realmResourceServer();
+}
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/permissions/AdminPermissions.java b/services/src/main/java/org/keycloak/services/resources/admin/permissions/AdminPermissions.java
new file mode 100644
index 0000000..f809e1d
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/resources/admin/permissions/AdminPermissions.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.services.resources.admin.permissions;
+
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.GroupModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleContainerModel;
+import org.keycloak.models.RoleModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.provider.ProviderEvent;
+import org.keycloak.provider.ProviderEventListener;
+import org.keycloak.provider.ProviderEventManager;
+import org.keycloak.services.resources.admin.AdminAuth;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class AdminPermissions {
+
+
+ public static AdminPermissionEvaluator evaluator(KeycloakSession session, RealmModel realm, AdminAuth auth) {
+ return new MgmtPermissions(session, realm, auth);
+ }
+ public static AdminPermissionEvaluator evaluator(KeycloakSession session, RealmModel realm, RealmModel adminsRealm, UserModel admin) {
+ return new MgmtPermissions(session, realm, adminsRealm, admin);
+ }
+
+ public static RealmsPermissionEvaluator realms(KeycloakSession session, AdminAuth auth) {
+ return new MgmtPermissions(session, auth);
+ }
+
+ public static AdminPermissionManagement management(KeycloakSession session, RealmModel realm) {
+ return new MgmtPermissions(session, realm);
+ }
+
+ public static void registerListener(ProviderEventManager manager) {
+ manager.register(new ProviderEventListener() {
+ @Override
+ public void onEvent(ProviderEvent event) {
+ if (event instanceof RoleContainerModel.RoleRemovedEvent) {
+ RoleContainerModel.RoleRemovedEvent cast = (RoleContainerModel.RoleRemovedEvent)event;
+ RoleModel role = cast.getRole();
+ RealmModel realm;
+ if (role.getContainer() instanceof ClientModel) {
+ realm = ((ClientModel)role.getContainer()).getRealm();
+
+ } else {
+ realm = (RealmModel)role.getContainer();
+ }
+ management(cast.getKeycloakSession(), realm).roles().setPermissionsEnabled(role, false);
+ } else if (event instanceof RealmModel.ClientRemovedEvent) {
+ RealmModel.ClientRemovedEvent cast = (RealmModel.ClientRemovedEvent)event;
+ management(cast.getKeycloakSession(), cast.getClient().getRealm()).clients().setPermissionsEnabled(cast.getClient(), false);
+ } else if (event instanceof GroupModel.GroupRemovedEvent) {
+ GroupModel.GroupRemovedEvent cast = (GroupModel.GroupRemovedEvent)event;
+ management(cast.getKeycloakSession(), cast.getRealm()).groups().setPermissionsEnabled(cast.getGroup(), false);
+ }
+ }
+ });
+ }
+
+
+}
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/permissions/ClientPermissionEvaluator.java b/services/src/main/java/org/keycloak/services/resources/admin/permissions/ClientPermissionEvaluator.java
new file mode 100644
index 0000000..3b64a27
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/resources/admin/permissions/ClientPermissionEvaluator.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.services.resources.admin.permissions;
+
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.ClientTemplateModel;
+
+import java.util.Map;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public interface ClientPermissionEvaluator {
+ boolean isPermissionsEnabled(ClientModel client);
+
+ void setPermissionsEnabled(ClientModel client, boolean enable);
+
+ void requireListTemplates();
+
+ boolean canManage();
+
+ void requireManage();
+
+ boolean canManageTemplates();
+
+ void requireManageTemplates();
+
+ boolean canView();
+
+ boolean canList();
+
+ boolean canViewTemplates();
+
+ void requireList();
+
+ boolean canListTemplates();
+
+ void requireView();
+
+ void requireViewTemplates();
+
+ boolean canManage(ClientModel client);
+
+ boolean canConfigure(ClientModel client);
+
+ void requireConfigure(ClientModel client);
+
+ void requireManage(ClientModel client);
+
+ boolean canView(ClientModel client);
+
+ void requireView(ClientModel client);
+
+ boolean canManage(ClientTemplateModel template);
+
+ void requireManage(ClientTemplateModel template);
+
+ boolean canView(ClientTemplateModel template);
+
+ void requireView(ClientTemplateModel template);
+
+ boolean canMapRoles(ClientModel client);
+
+ boolean canMapCompositeRoles(ClientModel client);
+
+ boolean canMapClientScopeRoles(ClientModel client);
+
+ Map<String, Boolean> getAccess(ClientModel client);
+}
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/permissions/ClientPermissionManagement.java b/services/src/main/java/org/keycloak/services/resources/admin/permissions/ClientPermissionManagement.java
new file mode 100644
index 0000000..8a6b76d
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/resources/admin/permissions/ClientPermissionManagement.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.services.resources.admin.permissions;
+
+import org.keycloak.authorization.model.Policy;
+import org.keycloak.authorization.model.Resource;
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.models.ClientModel;
+
+import java.util.Map;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public interface ClientPermissionManagement {
+ public static final String MAP_ROLES_SCOPE = "map-roles";
+ public static final String MAP_ROLES_CLIENT_SCOPE = "map-roles-client-scope";
+ public static final String MAP_ROLES_COMPOSITE_SCOPE = "map-roles-composite";
+ public static final String CONFIGURE_SCOPE = "configure";
+
+ boolean isPermissionsEnabled(ClientModel client);
+
+ void setPermissionsEnabled(ClientModel client, boolean enable);
+
+ Resource resource(ClientModel client);
+
+ Map<String, String> getPermissions(ClientModel client);
+
+ Policy mapRolesPermission(ClientModel client);
+
+ Policy mapRolesClientScopePermission(ClientModel client);
+
+ Policy mapRolesCompositePermission(ClientModel client);
+
+ Policy managePermission(ClientModel client);
+
+ Policy configurePermission(ClientModel client);
+
+ Policy viewPermission(ClientModel client);
+
+ ResourceServer resourceServer(ClientModel client);
+}
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/permissions/ClientPermissions.java b/services/src/main/java/org/keycloak/services/resources/admin/permissions/ClientPermissions.java
new file mode 100644
index 0000000..2b1e234
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/resources/admin/permissions/ClientPermissions.java
@@ -0,0 +1,568 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.services.resources.admin.permissions;
+
+import org.jboss.logging.Logger;
+import org.keycloak.authorization.AuthorizationProvider;
+import org.keycloak.authorization.model.Policy;
+import org.keycloak.authorization.model.Resource;
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.model.Scope;
+import org.keycloak.models.AdminRoles;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.ClientTemplateModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleModel;
+import org.keycloak.services.ForbiddenException;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Manages default policies for all users.
+ *
+ *
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+class ClientPermissions implements ClientPermissionEvaluator, ClientPermissionManagement {
+ private static final Logger logger = Logger.getLogger(ClientPermissions.class);
+ protected final KeycloakSession session;
+ protected final RealmModel realm;
+ protected final AuthorizationProvider authz;
+ protected final MgmtPermissions root;
+
+ public ClientPermissions(KeycloakSession session, RealmModel realm, AuthorizationProvider authz, MgmtPermissions root) {
+ this.session = session;
+ this.realm = realm;
+ this.authz = authz;
+ this.root = root;
+ }
+
+ private String getResourceName(ClientModel client) {
+ return "client.resource." + client.getId();
+ }
+
+ private String getManagePermissionName(ClientModel client) {
+ return "manage.permission.client." + client.getId();
+ }
+ private String getConfigurePermissionName(ClientModel client) {
+ return "configure.permission.client." + client.getId();
+ }
+ private String getViewPermissionName(ClientModel client) {
+ return "view.permission.client." + client.getId();
+ }
+ private String getMapRolesPermissionName(ClientModel client) {
+ return MAP_ROLES_SCOPE + ".permission.client." + client.getId();
+ }
+ private String getMapRolesClientScopePermissionName(ClientModel client) {
+ return MAP_ROLES_CLIENT_SCOPE + ".permission.client." + client.getId();
+ }
+ private String getMapRolesCompositePermissionName(ClientModel client) {
+ return MAP_ROLES_COMPOSITE_SCOPE + ".permission.client." + client.getId();
+ }
+
+ private void initialize(ClientModel client) {
+ ResourceServer server = root.findOrCreateResourceServer(client);
+ Scope manageScope = manageScope(server);
+ if (manageScope == null) {
+ manageScope = authz.getStoreFactory().getScopeStore().create(AdminPermissionManagement.MANAGE_SCOPE, server);
+ }
+ Scope viewScope = viewScope(server);
+ if (viewScope == null) {
+ viewScope = authz.getStoreFactory().getScopeStore().create(AdminPermissionManagement.VIEW_SCOPE, server);
+ }
+ Scope mapRoleScope = mapRolesScope(server);
+ if (mapRoleScope == null) {
+ mapRoleScope = authz.getStoreFactory().getScopeStore().create(MAP_ROLES_SCOPE, server);
+ }
+ Scope mapRoleClientScope = authz.getStoreFactory().getScopeStore().findByName(MAP_ROLES_CLIENT_SCOPE, server.getId());
+ if (mapRoleClientScope == null) {
+ mapRoleClientScope = authz.getStoreFactory().getScopeStore().create(MAP_ROLES_CLIENT_SCOPE, server);
+ }
+ Scope mapRoleCompositeScope = authz.getStoreFactory().getScopeStore().findByName(MAP_ROLES_COMPOSITE_SCOPE, server.getId());
+ if (mapRoleCompositeScope == null) {
+ mapRoleCompositeScope = authz.getStoreFactory().getScopeStore().create(MAP_ROLES_COMPOSITE_SCOPE, server);
+ }
+ Scope configureScope = authz.getStoreFactory().getScopeStore().findByName(CONFIGURE_SCOPE, server.getId());
+ if (configureScope == null) {
+ configureScope = authz.getStoreFactory().getScopeStore().create(CONFIGURE_SCOPE, server);
+ }
+
+ String resourceName = getResourceName(client);
+ Resource resource = authz.getStoreFactory().getResourceStore().findByName(resourceName, server.getId());
+ if (resource == null) {
+ resource = authz.getStoreFactory().getResourceStore().create(resourceName, server, server.getClientId());
+ resource.setType("Client");
+ Set<Scope> scopeset = new HashSet<>();
+ scopeset.add(configureScope);
+ scopeset.add(manageScope);
+ scopeset.add(viewScope);
+ scopeset.add(mapRoleScope);
+ scopeset.add(mapRoleClientScope);
+ scopeset.add(mapRoleCompositeScope);
+ resource.updateScopes(scopeset);
+ }
+ String managePermissionName = getManagePermissionName(client);
+ Policy managePermission = authz.getStoreFactory().getPolicyStore().findByName(managePermissionName, server.getId());
+ if (managePermission == null) {
+ Helper.addEmptyScopePermission(authz, server, managePermissionName, resource, manageScope);
+ }
+ String configurePermissionName = getConfigurePermissionName(client);
+ Policy configurePermission = authz.getStoreFactory().getPolicyStore().findByName(configurePermissionName, server.getId());
+ if (configurePermission == null) {
+ Helper.addEmptyScopePermission(authz, server, configurePermissionName, resource, configureScope);
+ }
+ String viewPermissionName = getViewPermissionName(client);
+ Policy viewPermission = authz.getStoreFactory().getPolicyStore().findByName(viewPermissionName, server.getId());
+ if (viewPermission == null) {
+ Helper.addEmptyScopePermission(authz, server, viewPermissionName, resource, viewScope);
+ }
+ String mapRolePermissionName = getMapRolesPermissionName(client);
+ Policy mapRolePermission = authz.getStoreFactory().getPolicyStore().findByName(mapRolePermissionName, server.getId());
+ if (mapRolePermission == null) {
+ Helper.addEmptyScopePermission(authz, server, mapRolePermissionName, resource, mapRoleScope);
+ }
+ String mapRoleClientScopePermissionName = getMapRolesClientScopePermissionName(client);
+ Policy mapRoleClientScopePermission = authz.getStoreFactory().getPolicyStore().findByName(mapRoleClientScopePermissionName, server.getId());
+ if (mapRoleClientScopePermission == null) {
+ Helper.addEmptyScopePermission(authz, server, mapRoleClientScopePermissionName, resource, mapRoleClientScope);
+ }
+ String mapRoleCompositePermissionName = getMapRolesCompositePermissionName(client);
+ Policy mapRoleCompositePermission = authz.getStoreFactory().getPolicyStore().findByName(mapRoleCompositePermissionName, server.getId());
+ if (mapRoleCompositePermission == null) {
+ Helper.addEmptyScopePermission(authz, server, mapRoleCompositePermissionName, resource, mapRoleCompositeScope);
+ }
+ }
+
+ private void deletePolicy(String name, ResourceServer server) {
+ Policy policy = authz.getStoreFactory().getPolicyStore().findByName(name, server.getId());
+ if (policy != null) {
+ authz.getStoreFactory().getPolicyStore().delete(policy.getId());
+ }
+
+ }
+
+ private void deletePermissions(ClientModel client) {
+ ResourceServer server = resourceServer(client);
+ if (server == null) return;
+ deletePolicy(getManagePermissionName(client), server);
+ deletePolicy(getViewPermissionName(client), server);
+ deletePolicy(getMapRolesPermissionName(client), server);
+ deletePolicy(getMapRolesClientScopePermissionName(client), server);
+ deletePolicy(getMapRolesCompositePermissionName(client), server);
+ deletePolicy(getConfigurePermissionName(client), server);
+ Resource resource = authz.getStoreFactory().getResourceStore().findByName(getResourceName(client), server.getId());;
+ if (resource != null) authz.getStoreFactory().getResourceStore().delete(resource.getId());
+ }
+
+ @Override
+ public boolean isPermissionsEnabled(ClientModel client) {
+ ResourceServer server = resourceServer(client);
+ if (server == null) return false;
+
+ return authz.getStoreFactory().getResourceStore().findByName(getResourceName(client), server.getId()) != null;
+ }
+
+ @Override
+ public void setPermissionsEnabled(ClientModel client, boolean enable) {
+ if (enable) {
+ initialize(client);
+ } else {
+ deletePermissions(client);
+ }
+ }
+
+
+
+ private Scope manageScope(ResourceServer server) {
+ return authz.getStoreFactory().getScopeStore().findByName(AdminPermissionManagement.MANAGE_SCOPE, server.getId());
+ }
+
+ private Scope configureScope(ResourceServer server) {
+ return authz.getStoreFactory().getScopeStore().findByName(CONFIGURE_SCOPE, server.getId());
+ }
+
+ private Scope viewScope(ResourceServer server) {
+ return authz.getStoreFactory().getScopeStore().findByName(AdminPermissionManagement.VIEW_SCOPE, server.getId());
+ }
+ private Scope mapRolesScope(ResourceServer server) {
+ return authz.getStoreFactory().getScopeStore().findByName(MAP_ROLES_SCOPE, server.getId());
+ }
+
+ @Override
+ public boolean canList() {
+ return root.hasAnyAdminRole();
+ }
+
+ @Override
+ public void requireList() {
+ if (!canList()) {
+ throw new ForbiddenException();
+ }
+ }
+
+ @Override
+ public boolean canListTemplates() {
+ return root.hasAnyAdminRole();
+ }
+
+ @Override
+ public void requireListTemplates() {
+ if (!canListTemplates()) {
+ throw new ForbiddenException();
+ }
+ }
+ public boolean canManageClientsDefault() {
+ return root.hasOneAdminRole(AdminRoles.MANAGE_CLIENTS);
+ }
+ public boolean canViewClientDefault() {
+ return root.hasOneAdminRole(AdminRoles.MANAGE_CLIENTS, AdminRoles.VIEW_CLIENTS);
+ }
+
+ @Override
+ public boolean canManage() {
+ return canManageClientsDefault();
+ }
+
+ @Override
+ public void requireManage() {
+ if (!canManage()) {
+ throw new ForbiddenException();
+ }
+ }
+ @Override
+ public boolean canView() {
+ return canManageClientsDefault() || canViewClientDefault();
+ }
+
+ @Override
+ public void requireView() {
+ if (!canView()) {
+ throw new ForbiddenException();
+ }
+ }
+
+ @Override
+ public Resource resource(ClientModel client) {
+ ResourceServer server = resourceServer(client);
+ if (server == null) return null;
+ Resource resource = authz.getStoreFactory().getResourceStore().findByName(getResourceName(client), server.getId());
+ if (resource == null) return null;
+ return resource;
+ }
+
+ @Override
+ public Map<String, String> getPermissions(ClientModel client) {
+ Map<String, String> scopes = new HashMap<>();
+ scopes.put(MAP_ROLES_SCOPE, mapRolesPermission(client).getId());
+ scopes.put(MAP_ROLES_CLIENT_SCOPE, mapRolesClientScopePermission(client).getId());
+ scopes.put(MAP_ROLES_COMPOSITE_SCOPE, mapRolesCompositePermission(client).getId());
+ scopes.put(AdminPermissionManagement.VIEW_SCOPE, viewPermission(client).getId());
+ scopes.put(AdminPermissionManagement.MANAGE_SCOPE, managePermission(client).getId());
+ scopes.put(CONFIGURE_SCOPE, configurePermission(client).getId());
+ return scopes;
+ }
+
+
+ @Override
+ public boolean canManage(ClientModel client) {
+ if (canManageClientsDefault()) return true;
+ if (!root.isAdminSameRealm()) {
+ return false;
+ }
+
+ ResourceServer server = resourceServer(client);
+ if (server == null) return false;
+
+ Resource resource = authz.getStoreFactory().getResourceStore().findByName(getResourceName(client), server.getId());
+ if (resource == null) return false;
+
+ Policy policy = authz.getStoreFactory().getPolicyStore().findByName(getManagePermissionName(client), server.getId());
+ if (policy == null) {
+ return false;
+ }
+
+ Set<Policy> associatedPolicies = policy.getAssociatedPolicies();
+ // if no policies attached to permission then just do default behavior
+ if (associatedPolicies == null || associatedPolicies.isEmpty()) {
+ return false;
+ }
+
+ Scope scope = manageScope(server);
+ return root.evaluatePermission(resource, scope, server);
+ }
+
+ @Override
+ public boolean canConfigure(ClientModel client) {
+ if (canManage(client)) return true;
+ if (!root.isAdminSameRealm()) {
+ return false;
+ }
+
+ ResourceServer server = resourceServer(client);
+ if (server == null) return false;
+
+ Resource resource = authz.getStoreFactory().getResourceStore().findByName(getResourceName(client), server.getId());
+ if (resource == null) return false;
+
+ Policy policy = authz.getStoreFactory().getPolicyStore().findByName(getConfigurePermissionName(client), server.getId());
+ if (policy == null) {
+ return false;
+ }
+
+ Set<Policy> associatedPolicies = policy.getAssociatedPolicies();
+ // if no policies attached to permission then just do default behavior
+ if (associatedPolicies == null || associatedPolicies.isEmpty()) {
+ return false;
+ }
+
+ Scope scope = configureScope(server);
+ return root.evaluatePermission(resource, scope, server);
+ }
+ @Override
+ public void requireConfigure(ClientModel client) {
+ if (!canConfigure(client)) {
+ throw new ForbiddenException();
+ }
+ }
+
+
+ @Override
+ public void requireManage(ClientModel client) {
+ if (!canManage(client)) {
+ throw new ForbiddenException();
+ }
+ }
+
+ @Override
+ public boolean canView(ClientModel client) {
+ return hasView(client) || canConfigure(client);
+ }
+
+ private boolean hasView(ClientModel client) {
+ if (canView()) return true;
+ if (!root.isAdminSameRealm()) {
+ return false;
+ }
+
+ ResourceServer server = resourceServer(client);
+ if (server == null) return false;
+
+ Resource resource = authz.getStoreFactory().getResourceStore().findByName(getResourceName(client), server.getId());
+ if (resource == null) return false;
+
+ Policy policy = authz.getStoreFactory().getPolicyStore().findByName(getViewPermissionName(client), server.getId());
+ if (policy == null) {
+ return false;
+ }
+
+ Set<Policy> associatedPolicies = policy.getAssociatedPolicies();
+ // if no policies attached to permission then just do default behavior
+ if (associatedPolicies == null || associatedPolicies.isEmpty()) {
+ return false;
+ }
+
+ Scope scope = viewScope(server);
+ return root.evaluatePermission(resource, scope, server);
+ }
+
+ @Override
+ public void requireView(ClientModel client) {
+ if (!canView(client)) {
+ throw new ForbiddenException();
+ }
+ }
+
+ // templates
+
+ @Override
+ public boolean canViewTemplates() {
+ return canView();
+ }
+
+ @Override
+ public boolean canManageTemplates() {
+ return canManageClientsDefault();
+ }
+
+ @Override
+ public void requireManageTemplates() {
+ if (!canManageTemplates()) {
+ throw new ForbiddenException();
+ }
+ }
+ @Override
+ public void requireViewTemplates() {
+ if (!canViewTemplates()) {
+ throw new ForbiddenException();
+ }
+ }
+
+ @Override
+ public boolean canManage(ClientTemplateModel template) {
+ return canManageClientsDefault();
+ }
+
+ @Override
+ public void requireManage(ClientTemplateModel template) {
+ if (!canManage(template)) {
+ throw new ForbiddenException();
+ }
+ }
+
+ @Override
+ public boolean canView(ClientTemplateModel template) {
+ return canViewClientDefault();
+ }
+
+ @Override
+ public void requireView(ClientTemplateModel template) {
+ if (!canView(template)) {
+ throw new ForbiddenException();
+ }
+ }
+
+ @Override
+ public boolean canMapRoles(ClientModel client) {
+ ResourceServer server = resourceServer(client);
+ if (server == null) return false;
+
+ Resource resource = authz.getStoreFactory().getResourceStore().findByName(getResourceName(client), server.getId());
+ if (resource == null) return false;
+
+ Policy policy = authz.getStoreFactory().getPolicyStore().findByName(getMapRolesPermissionName(client), server.getId());
+ if (policy == null) {
+ return false;
+ }
+
+ Set<Policy> associatedPolicies = policy.getAssociatedPolicies();
+ // if no policies attached to permission then just do default behavior
+ if (associatedPolicies == null || associatedPolicies.isEmpty()) {
+ return false;
+ }
+
+ Scope scope = mapRolesScope(server);
+ return root.evaluatePermission(resource, scope, server);
+ }
+
+ @Override
+ public Policy mapRolesPermission(ClientModel client) {
+ ResourceServer server = resourceServer(client);
+ if (server == null) return null;
+ return authz.getStoreFactory().getPolicyStore().findByName(getMapRolesPermissionName(client), server.getId());
+ }
+
+ @Override
+ public Policy mapRolesClientScopePermission(ClientModel client) {
+ ResourceServer server = resourceServer(client);
+ if (server == null) return null;
+ return authz.getStoreFactory().getPolicyStore().findByName(getMapRolesClientScopePermissionName(client), server.getId());
+ }
+
+ @Override
+ public Policy mapRolesCompositePermission(ClientModel client) {
+ ResourceServer server = resourceServer(client);
+ if (server == null) return null;
+ return authz.getStoreFactory().getPolicyStore().findByName(getMapRolesCompositePermissionName(client), server.getId());
+ }
+
+ @Override
+ public Policy managePermission(ClientModel client) {
+ ResourceServer server = resourceServer(client);
+ if (server == null) return null;
+ return authz.getStoreFactory().getPolicyStore().findByName(getManagePermissionName(client), server.getId());
+ }
+
+ @Override
+ public Policy configurePermission(ClientModel client) {
+ ResourceServer server = resourceServer(client);
+ if (server == null) return null;
+ return authz.getStoreFactory().getPolicyStore().findByName(getConfigurePermissionName(client), server.getId());
+ }
+
+ @Override
+ public Policy viewPermission(ClientModel client) {
+ ResourceServer server = resourceServer(client);
+ if (server == null) return null;
+ return authz.getStoreFactory().getPolicyStore().findByName(getViewPermissionName(client), server.getId());
+ }
+
+ @Override
+ public ResourceServer resourceServer(ClientModel client) {
+ return root.resourceServer(client);
+ }
+
+ @Override
+ public boolean canMapCompositeRoles(ClientModel client) {
+ ResourceServer server = resourceServer(client);
+ if (server == null) return false;
+
+ Resource resource = authz.getStoreFactory().getResourceStore().findByName(getResourceName(client), server.getId());
+ if (resource == null) return false;
+
+ Policy policy = authz.getStoreFactory().getPolicyStore().findByName(getMapRolesCompositePermissionName(client), server.getId());
+ if (policy == null) {
+ return false;
+ }
+
+ Set<Policy> associatedPolicies = policy.getAssociatedPolicies();
+ // if no policies attached to permission then just do default behavior
+ if (associatedPolicies == null || associatedPolicies.isEmpty()) {
+ return false;
+ }
+
+ Scope scope = authz.getStoreFactory().getScopeStore().findByName(MAP_ROLES_COMPOSITE_SCOPE, server.getId());
+ return root.evaluatePermission(resource, scope, server);
+ }
+ @Override
+ public boolean canMapClientScopeRoles(ClientModel client) {
+ ResourceServer server = resourceServer(client);
+ if (server == null) return false;
+
+ Resource resource = authz.getStoreFactory().getResourceStore().findByName(getResourceName(client), server.getId());
+ if (resource == null) return false;
+
+ Policy policy = authz.getStoreFactory().getPolicyStore().findByName(getMapRolesClientScopePermissionName(client), server.getId());
+ if (policy == null) {
+ return false;
+ }
+
+ Set<Policy> associatedPolicies = policy.getAssociatedPolicies();
+ // if no policies attached to permission then just do default behavior
+ if (associatedPolicies == null || associatedPolicies.isEmpty()) {
+ return false;
+ }
+
+ Scope scope = authz.getStoreFactory().getScopeStore().findByName(MAP_ROLES_CLIENT_SCOPE, server.getId());
+ return root.evaluatePermission(resource, scope, server);
+ }
+
+ @Override
+ public Map<String, Boolean> getAccess(ClientModel client) {
+ Map<String, Boolean> map = new HashMap<>();
+ map.put("view", canView(client));
+ map.put("manage", canManage(client));
+ map.put("configure", canConfigure(client));
+ return map;
+ }
+
+
+}
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/permissions/GroupPermissionEvaluator.java b/services/src/main/java/org/keycloak/services/resources/admin/permissions/GroupPermissionEvaluator.java
new file mode 100644
index 0000000..d3a5213
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/resources/admin/permissions/GroupPermissionEvaluator.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.services.resources.admin.permissions;
+
+import org.keycloak.models.GroupModel;
+
+import java.util.Map;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public interface GroupPermissionEvaluator {
+ boolean canList();
+
+ void requireList();
+
+ boolean canManage(GroupModel group);
+
+ void requireManage(GroupModel group);
+
+ boolean canView(GroupModel group);
+
+ void requireView(GroupModel group);
+
+ boolean canManage();
+
+ void requireManage();
+
+ boolean canView();
+
+ void requireView();
+
+ boolean canViewMembers(GroupModel group);
+
+ void requireViewMembers(GroupModel group);
+
+ boolean canManageMembers(GroupModel group);
+
+ void requireManageMembers(GroupModel group);
+
+ boolean canManageMembership(GroupModel group);
+
+ void requireManageMembership(GroupModel group);
+
+ Map<String, Boolean> getAccess(GroupModel group);
+}
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/permissions/GroupPermissionManagement.java b/services/src/main/java/org/keycloak/services/resources/admin/permissions/GroupPermissionManagement.java
new file mode 100644
index 0000000..2ec602c
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/resources/admin/permissions/GroupPermissionManagement.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.services.resources.admin.permissions;
+
+import org.keycloak.authorization.model.Policy;
+import org.keycloak.authorization.model.Resource;
+import org.keycloak.models.GroupModel;
+import org.keycloak.models.RoleModel;
+
+import java.util.Map;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public interface GroupPermissionManagement {
+ boolean isPermissionsEnabled(GroupModel group);
+ void setPermissionsEnabled(GroupModel group, boolean enable);
+
+ Policy viewMembersPermission(GroupModel group);
+ Policy manageMembersPermission(GroupModel group);
+
+ Policy manageMembershipPermission(GroupModel group);
+
+ Policy viewPermission(GroupModel group);
+ Policy managePermission(GroupModel group);
+
+ Resource resource(GroupModel group);
+
+ Map<String, String> getPermissions(GroupModel group);
+
+}
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/permissions/GroupPermissions.java b/services/src/main/java/org/keycloak/services/resources/admin/permissions/GroupPermissions.java
new file mode 100644
index 0000000..a7e9f37
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/resources/admin/permissions/GroupPermissions.java
@@ -0,0 +1,483 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.services.resources.admin.permissions;
+
+import org.jboss.logging.Logger;
+import org.keycloak.authorization.AuthorizationProvider;
+import org.keycloak.authorization.model.Policy;
+import org.keycloak.authorization.model.Resource;
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.model.Scope;
+import org.keycloak.models.AdminRoles;
+import org.keycloak.models.GroupModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.utils.ModelToRepresentation;
+import org.keycloak.services.ForbiddenException;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+class GroupPermissions implements GroupPermissionEvaluator, GroupPermissionManagement {
+ private static final Logger logger = Logger.getLogger(GroupPermissions.class);
+ public static final String MAP_ROLE_SCOPE = "map-role";
+ public static final String MANAGE_MEMBERSHIP_SCOPE = "manage-membership";
+ public static final String MANAGE_MEMBERS_SCOPE = "manage-members";
+ public static final String VIEW_MEMBERS_SCOPE = "view-members";
+ protected final KeycloakSession session;
+ protected final RealmModel realm;
+ protected final AuthorizationProvider authz;
+ protected final MgmtPermissions root;
+
+ public GroupPermissions(KeycloakSession session, RealmModel realm, AuthorizationProvider authz, MgmtPermissions root) {
+ this.session = session;
+ this.realm = realm;
+ this.authz = authz;
+ this.root = root;
+ }
+
+ private static String getGroupResourceName(GroupModel group) {
+ return "group.resource." + group.getId();
+ }
+
+
+ public static String getManagePermissionGroup(GroupModel group) {
+ return "manage.permission.group." + group.getId();
+ }
+
+ public static String getManageMembersPermissionGroup(GroupModel group) {
+ return "manage.members.permission.group." + group.getId();
+ }
+
+ public static String getManageMembershipPermissionGroup(GroupModel group) {
+ return "manage.membership.permission.group." + group.getId();
+ }
+
+ public static String getViewPermissionGroup(GroupModel group) {
+ return "view.permission.group." + group.getId();
+ }
+
+ public static String getViewMembersPermissionGroup(GroupModel group) {
+ return "view.members.permission.group." + group.getId();
+ }
+
+ private void initialize(GroupModel group) {
+ root.initializeRealmResourceServer();
+ root.initializeRealmDefaultScopes();
+ ResourceServer server = root.realmResourceServer();
+ Scope manageScope = root.realmManageScope();
+ Scope viewScope = root.realmViewScope();
+ Scope manageMembersScope = root.initializeRealmScope(MANAGE_MEMBERS_SCOPE);
+ Scope viewMembersScope = root.initializeRealmScope(VIEW_MEMBERS_SCOPE);
+ Scope manageMembershipScope = root.initializeRealmScope(MANAGE_MEMBERSHIP_SCOPE);
+
+ String groupResourceName = getGroupResourceName(group);
+ Resource groupResource = authz.getStoreFactory().getResourceStore().findByName(groupResourceName, server.getId());
+ if (groupResource == null) {
+ groupResource = authz.getStoreFactory().getResourceStore().create(groupResourceName, server, server.getClientId());
+ Set<Scope> scopeset = new HashSet<>();
+ scopeset.add(manageScope);
+ scopeset.add(viewScope);
+ scopeset.add(manageMembershipScope);
+ scopeset.add(manageMembersScope);
+ groupResource.updateScopes(scopeset);
+ }
+ String managePermissionName = getManagePermissionGroup(group);
+ Policy managePermission = authz.getStoreFactory().getPolicyStore().findByName(managePermissionName, server.getId());
+ if (managePermission == null) {
+ Helper.addEmptyScopePermission(authz, server, managePermissionName, groupResource, manageScope);
+ }
+ String viewPermissionName = getViewPermissionGroup(group);
+ Policy viewPermission = authz.getStoreFactory().getPolicyStore().findByName(viewPermissionName, server.getId());
+ if (viewPermission == null) {
+ Helper.addEmptyScopePermission(authz, server, viewPermissionName, groupResource, viewScope);
+ }
+ String manageMembersPermissionName = getManageMembersPermissionGroup(group);
+ Policy manageMembersPermission = authz.getStoreFactory().getPolicyStore().findByName(manageMembersPermissionName, server.getId());
+ if (manageMembersPermission == null) {
+ Helper.addEmptyScopePermission(authz, server, manageMembersPermissionName, groupResource, manageMembersScope);
+ }
+ String viewMembersPermissionName = getViewMembersPermissionGroup(group);
+ Policy viewMembersPermission = authz.getStoreFactory().getPolicyStore().findByName(viewMembersPermissionName, server.getId());
+ if (viewMembersPermission == null) {
+ Helper.addEmptyScopePermission(authz, server, viewMembersPermissionName, groupResource, viewMembersScope);
+ }
+ String manageMembershipPermissionName = getManageMembershipPermissionGroup(group);
+ Policy manageMembershipPermission = authz.getStoreFactory().getPolicyStore().findByName(manageMembershipPermissionName, server.getId());
+ if (manageMembershipPermission == null) {
+ Helper.addEmptyScopePermission(authz, server, manageMembershipPermissionName, groupResource, manageMembershipScope);
+ }
+
+ }
+
+ @Override
+ public boolean canList() {
+ return root.hasOneAdminRole(AdminRoles.VIEW_USERS, AdminRoles.MANAGE_USERS, AdminRoles.QUERY_GROUPS);
+ }
+
+ @Override
+ public void requireList() {
+ if (!canList()) {
+ throw new ForbiddenException();
+ }
+ }
+
+
+
+ @Override
+ public boolean isPermissionsEnabled(GroupModel group) {
+ ResourceServer server = root.realmResourceServer();
+ if (server == null) return false;
+
+ return authz.getStoreFactory().getResourceStore().findByName(getGroupResourceName(group), server.getId()) != null;
+ }
+
+ private Resource groupResource(GroupModel group) {
+ ResourceServer server = root.realmResourceServer();
+ if (server == null) return null;
+ String groupResourceName = getGroupResourceName(group);
+ return authz.getStoreFactory().getResourceStore().findByName(groupResourceName, server.getId());
+ }
+
+ @Override
+ public void setPermissionsEnabled(GroupModel group, boolean enable) {
+ if (enable) {
+ initialize(group);
+ } else {
+ deletePermissions(group);
+ }
+ }
+
+ private void deletePermissions(GroupModel group) {
+ ResourceServer server = root.realmResourceServer();
+ if (server == null) return;
+ Policy managePermission = managePermission(group);
+ if (managePermission != null) {
+ authz.getStoreFactory().getPolicyStore().delete(managePermission.getId());
+ }
+ Policy viewPermission = viewPermission(group);
+ if (viewPermission != null) {
+ authz.getStoreFactory().getPolicyStore().delete(viewPermission.getId());
+ }
+ Policy manageMembersPermission = manageMembersPermission(group);
+ if (manageMembersPermission != null) {
+ authz.getStoreFactory().getPolicyStore().delete(manageMembersPermission.getId());
+ }
+ Policy viewMembersPermission = viewMembersPermission(group);
+ if (manageMembersPermission == null) {
+ authz.getStoreFactory().getPolicyStore().delete(viewMembersPermission.getId());
+ }
+ Resource resource = groupResource(group);
+ if (resource != null) authz.getStoreFactory().getResourceStore().delete(resource.getId());
+ }
+
+ @Override
+ public Policy viewMembersPermission(GroupModel group) {
+ ResourceServer server = root.realmResourceServer();
+ if (server == null) return null;
+ String viewMembersPermissionName = getViewMembersPermissionGroup(group);
+ return authz.getStoreFactory().getPolicyStore().findByName(viewMembersPermissionName, server.getId());
+ }
+
+ @Override
+ public Policy manageMembersPermission(GroupModel group) {
+ ResourceServer server = root.realmResourceServer();
+ if (server == null) return null;
+ String manageMembersPermissionName = getManageMembersPermissionGroup(group);
+ return authz.getStoreFactory().getPolicyStore().findByName(manageMembersPermissionName, server.getId());
+ }
+
+ @Override
+ public Policy manageMembershipPermission(GroupModel group) {
+ ResourceServer server = root.realmResourceServer();
+ if (server == null) return null;
+ String manageMembershipPermissionName = getManageMembershipPermissionGroup(group);
+ return authz.getStoreFactory().getPolicyStore().findByName(manageMembershipPermissionName, server.getId());
+ }
+
+ @Override
+ public Policy viewPermission(GroupModel group) {
+ ResourceServer server = root.realmResourceServer();
+ if (server == null) return null;
+ String viewPermissionName = getViewPermissionGroup(group);
+ return authz.getStoreFactory().getPolicyStore().findByName(viewPermissionName, server.getId());
+ }
+
+ @Override
+ public Policy managePermission(GroupModel group) {
+ ResourceServer server = root.realmResourceServer();
+ if (server == null) return null;
+ String managePermissionName = getManagePermissionGroup(group);
+ return authz.getStoreFactory().getPolicyStore().findByName(managePermissionName, server.getId());
+ }
+
+ @Override
+ public Resource resource(GroupModel group) {
+ ResourceServer server = root.realmResourceServer();
+ if (server == null) return null;
+ Resource resource = authz.getStoreFactory().getResourceStore().findByName(getGroupResourceName(group), server.getId());
+ if (resource == null) return null;
+ return resource;
+ }
+
+ @Override
+ public Map<String, String> getPermissions(GroupModel group) {
+ Map<String, String> scopes = new HashMap<>();
+ scopes.put(AdminPermissionManagement.VIEW_SCOPE, viewPermission(group).getId());
+ scopes.put(AdminPermissionManagement.MANAGE_SCOPE, managePermission(group).getId());
+ scopes.put(MANAGE_MEMBERS_SCOPE, manageMembersPermission(group).getId());
+ scopes.put(VIEW_MEMBERS_SCOPE, viewMembersPermission(group).getId());
+ scopes.put(MANAGE_MEMBERSHIP_SCOPE, manageMembershipPermission(group).getId());
+ return scopes;
+ }
+
+
+
+
+ @Override
+ public boolean canManage(GroupModel group) {
+ if (canManage()) return true;
+ if (!root.isAdminSameRealm()) {
+ return false;
+ }
+
+ ResourceServer server = root.realmResourceServer();
+ if (server == null) return false;
+
+ Resource resource = authz.getStoreFactory().getResourceStore().findByName(getGroupResourceName(group), server.getId());
+ if (resource == null) return false;
+
+ Policy policy = managePermission(group);
+ if (policy == null) {
+ return false;
+ }
+
+ Set<Policy> associatedPolicies = policy.getAssociatedPolicies();
+ // if no policies attached to permission then just do default behavior
+ if (associatedPolicies == null || associatedPolicies.isEmpty()) {
+ return false;
+ }
+
+ Scope scope = root.realmManageScope();
+ return root.evaluatePermission(resource, scope, server);
+ }
+
+ @Override
+ public void requireManage(GroupModel group) {
+ if (!canManage(group)) {
+ throw new ForbiddenException();
+ }
+ }
+ @Override
+ public boolean canView(GroupModel group) {
+ return hasView(group) || canManage(group);
+ }
+
+ private boolean hasView(GroupModel group) {
+ if (canView()) return true;
+
+ if (!root.isAdminSameRealm()) {
+ return false;
+ }
+
+ ResourceServer server = root.realmResourceServer();
+ if (server == null) return false;
+
+ Resource resource = authz.getStoreFactory().getResourceStore().findByName(getGroupResourceName(group), server.getId());
+ if (resource == null) return false;
+
+ Policy policy = viewPermission(group);
+ if (policy == null) {
+ return false;
+ }
+
+ Set<Policy> associatedPolicies = policy.getAssociatedPolicies();
+ // if no policies attached to permission then abort
+ if (associatedPolicies == null || associatedPolicies.isEmpty()) {
+ return false;
+ }
+
+ Scope scope = root.realmViewScope();
+ return root.evaluatePermission(resource, scope, server);
+ }
+
+ @Override
+ public void requireView(GroupModel group) {
+ if (!canView(group)) {
+ throw new ForbiddenException();
+ }
+ }
+
+ @Override
+ public boolean canManage() {
+ return root.users().canManageDefault();
+ }
+
+ @Override
+ public void requireManage() {
+ if (!canManage()) {
+ throw new ForbiddenException();
+ }
+ }
+ @Override
+ public boolean canView() {
+ return root.users().canViewDefault();
+ }
+
+ @Override
+ public void requireView() {
+ if (!canView()) {
+ throw new ForbiddenException();
+ }
+ }
+
+
+
+ @Override
+ public boolean canViewMembers(GroupModel group) {
+ return canViewMembersEvaluation(group) || canManageMembers(group);
+ }
+
+ private boolean canViewMembersEvaluation(GroupModel group) {
+ if (root.users().canView()) return true;
+
+ if (!root.isAdminSameRealm()) {
+ return false;
+ }
+
+ ResourceServer server = root.realmResourceServer();
+ if (server == null) return false;
+
+ Resource resource = authz.getStoreFactory().getResourceStore().findByName(getGroupResourceName(group), server.getId());
+ if (resource == null) return false;
+
+ Policy policy = viewMembersPermission(group);
+ if (policy == null) {
+ return false;
+ }
+
+ Set<Policy> associatedPolicies = policy.getAssociatedPolicies();
+ // if no policies attached to permission then just do default behavior
+ if (associatedPolicies == null || associatedPolicies.isEmpty()) {
+ return false;
+ }
+
+ Scope scope = authz.getStoreFactory().getScopeStore().findByName(VIEW_MEMBERS_SCOPE, server.getId());
+
+ return root.evaluatePermission(resource, scope, server);
+ }
+
+
+ @Override
+ public void requireViewMembers(GroupModel group) {
+ if (!canViewMembers(group)) {
+ throw new ForbiddenException();
+ }
+ }
+
+
+ @Override
+ public boolean canManageMembers(GroupModel group) {
+ if (root.users().canManage()) return true;
+
+ if (!root.isAdminSameRealm()) {
+ return false;
+ }
+
+ ResourceServer server = root.realmResourceServer();
+ if (server == null) return false;
+
+ Resource resource = authz.getStoreFactory().getResourceStore().findByName(getGroupResourceName(group), server.getId());
+ if (resource == null) return false;
+
+ Policy policy = manageMembersPermission(group);
+ if (policy == null) {
+ return false;
+ }
+
+ Set<Policy> associatedPolicies = policy.getAssociatedPolicies();
+ // if no policies attached to permission then just do default behavior
+ if (associatedPolicies == null || associatedPolicies.isEmpty()) {
+ return false;
+ }
+
+ Scope scope = authz.getStoreFactory().getScopeStore().findByName(MANAGE_MEMBERS_SCOPE, server.getId());
+ return root.evaluatePermission(resource, scope, server);
+ }
+
+ @Override
+ public void requireManageMembers(GroupModel group) {
+ if (!canManageMembers(group)) {
+ throw new ForbiddenException();
+ }
+ }
+
+ @Override
+ public boolean canManageMembership(GroupModel group) {
+ if (canManage(group)) return true;
+
+ if (!root.isAdminSameRealm()) {
+ return false;
+ }
+
+ ResourceServer server = root.realmResourceServer();
+ if (server == null) return false;
+
+ Resource resource = authz.getStoreFactory().getResourceStore().findByName(getGroupResourceName(group), server.getId());
+ if (resource == null) return false;
+
+ Policy policy = manageMembershipPermission(group);
+ if (policy == null) {
+ return false;
+ }
+
+ Set<Policy> associatedPolicies = policy.getAssociatedPolicies();
+ // if no policies attached to permission then just do default behavior
+ if (associatedPolicies == null || associatedPolicies.isEmpty()) {
+ return false;
+ }
+
+ Scope scope = authz.getStoreFactory().getScopeStore().findByName(MANAGE_MEMBERSHIP_SCOPE, server.getId());
+ return root.evaluatePermission(resource, scope, server);
+ }
+
+ @Override
+ public void requireManageMembership(GroupModel group) {
+ if (!canManageMembership(group)) {
+ throw new ForbiddenException();
+ }
+ }
+
+ @Override
+ public Map<String, Boolean> getAccess(GroupModel group) {
+ Map<String, Boolean> map = new HashMap<>();
+ map.put("view", canView(group));
+ map.put("manage", canManage(group));
+ map.put("manageMembership", canManageMembership(group));
+ return map;
+ }
+
+
+
+
+}
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/permissions/Helper.java b/services/src/main/java/org/keycloak/services/resources/admin/permissions/Helper.java
new file mode 100644
index 0000000..2e7942d
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/resources/admin/permissions/Helper.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.services.resources.admin.permissions;
+
+import org.keycloak.authorization.AuthorizationProvider;
+import org.keycloak.authorization.model.Policy;
+import org.keycloak.authorization.model.Resource;
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.model.Scope;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.RoleModel;
+import org.keycloak.representations.idm.authorization.DecisionStrategy;
+import org.keycloak.representations.idm.authorization.Logic;
+import org.keycloak.representations.idm.authorization.PolicyRepresentation;
+import org.keycloak.representations.idm.authorization.ScopePermissionRepresentation;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+class Helper {
+ public static Policy addScopePermission(AuthorizationProvider authz, ResourceServer resourceServer, String name, Resource resource, Scope scope, Policy policy) {
+ ScopePermissionRepresentation representation = new ScopePermissionRepresentation();
+
+ representation.setName(name);
+ representation.setDecisionStrategy(DecisionStrategy.UNANIMOUS);
+ representation.setLogic(Logic.POSITIVE);
+ representation.addResource(resource.getName());
+ representation.addScope(scope.getName());
+ representation.addPolicy(policy.getName());
+
+ return authz.getStoreFactory().getPolicyStore().create(representation, resourceServer);
+ }
+
+ public static Policy addEmptyScopePermission(AuthorizationProvider authz, ResourceServer resourceServer, String name, Resource resource, Scope scope) {
+ ScopePermissionRepresentation representation = new ScopePermissionRepresentation();
+
+ representation.setName(name);
+ representation.setDecisionStrategy(DecisionStrategy.UNANIMOUS);
+ representation.setLogic(Logic.POSITIVE);
+ representation.addResource(resource.getName());
+ representation.addScope(scope.getName());
+
+ return authz.getStoreFactory().getPolicyStore().create(representation, resourceServer);
+ }
+
+ public static Policy createRolePolicy(AuthorizationProvider authz, ResourceServer resourceServer, RoleModel role) {
+ String roleName = getRolePolicyName(role);
+ return createRolePolicy(authz, resourceServer, role, roleName);
+ }
+
+ public static Policy createRolePolicy(AuthorizationProvider authz, ResourceServer resourceServer, RoleModel role, String policyName) {
+ PolicyRepresentation representation = new PolicyRepresentation();
+
+ representation.setName(policyName);
+ representation.setType("role");
+ representation.setDecisionStrategy(DecisionStrategy.UNANIMOUS);
+ representation.setLogic(Logic.POSITIVE);
+ String roleValues = "[{\"id\":\"" + role.getId() + "\",\"required\": true}]";
+ Map<String, String> config = new HashMap<>();
+ config.put("roles", roleValues);
+ representation.setConfig(config);
+
+ return authz.getStoreFactory().getPolicyStore().create(representation, resourceServer);
+ }
+
+ public static String getRolePolicyName(RoleModel role) {
+ String roleName = "";
+ if (role.getContainer() instanceof ClientModel) {
+ ClientModel client = (ClientModel) role.getContainer();
+ roleName = client.getClientId() + "." + role.getName();
+ } else {
+ roleName = role.getName();
+ }
+ roleName = "role.policy." + roleName;
+ return roleName;
+ }
+}
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/permissions/MgmtPermissions.java b/services/src/main/java/org/keycloak/services/resources/admin/permissions/MgmtPermissions.java
new file mode 100644
index 0000000..2df4953
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/resources/admin/permissions/MgmtPermissions.java
@@ -0,0 +1,364 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.services.resources.admin.permissions;
+
+import org.jboss.logging.Logger;
+import org.keycloak.Config;
+import org.keycloak.authorization.AuthorizationProvider;
+import org.keycloak.authorization.AuthorizationProviderFactory;
+import org.keycloak.authorization.Decision;
+import org.keycloak.authorization.common.DefaultEvaluationContext;
+import org.keycloak.authorization.common.KeycloakIdentity;
+import org.keycloak.authorization.common.UserModelIdentity;
+import org.keycloak.authorization.identity.Identity;
+import org.keycloak.authorization.model.Resource;
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.model.Scope;
+import org.keycloak.authorization.permission.ResourcePermission;
+import org.keycloak.authorization.permission.evaluator.PermissionEvaluator;
+import org.keycloak.authorization.policy.evaluation.DecisionResult;
+import org.keycloak.authorization.policy.evaluation.EvaluationContext;
+import org.keycloak.authorization.store.ResourceServerStore;
+import org.keycloak.authorization.util.Permissions;
+import org.keycloak.models.AdminRoles;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.Constants;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.services.ForbiddenException;
+import org.keycloak.services.managers.RealmManager;
+import org.keycloak.services.resources.admin.AdminAuth;
+
+import java.util.List;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+class MgmtPermissions implements AdminPermissionEvaluator, AdminPermissionManagement, RealmsPermissionEvaluator {
+ private static final Logger logger = Logger.getLogger(MgmtPermissions.class);
+
+ protected RealmModel realm;
+ protected KeycloakSession session;
+ protected AuthorizationProvider authz;
+ protected AdminAuth auth;
+ protected Identity identity;
+ protected UserModel admin;
+ protected RealmModel adminsRealm;
+ protected ResourceServer realmResourceServer;
+ protected UserPermissions users;
+ protected GroupPermissions groups;
+ protected RealmPermissions realmPermissions;
+ protected ClientPermissions clientPermissions;
+
+
+ MgmtPermissions(KeycloakSession session, RealmModel realm) {
+ this.session = session;
+ this.realm = realm;
+ KeycloakSessionFactory keycloakSessionFactory = session.getKeycloakSessionFactory();
+ AuthorizationProviderFactory factory = (AuthorizationProviderFactory) keycloakSessionFactory.getProviderFactory(AuthorizationProvider.class);
+ this.authz = factory.create(session, realm);
+ }
+
+ MgmtPermissions(KeycloakSession session, RealmModel realm, AdminAuth auth) {
+ this(session, realm);
+ this.auth = auth;
+ this.admin = auth.getUser();
+ this.adminsRealm = auth.getRealm();
+ if (!auth.getRealm().equals(realm)
+ && !auth.getRealm().equals(new RealmManager(session).getKeycloakAdminstrationRealm())) {
+ throw new ForbiddenException();
+ }
+ if (auth.getClient().getClientId().equals(Constants.ADMIN_CLI_CLIENT_ID)
+ || auth.getClient().getClientId().equals(Constants.ADMIN_CONSOLE_CLIENT_ID)) {
+ this.identity = new UserModelIdentity(auth.getRealm(), auth.getUser());
+
+ } else {
+ this.identity = new KeycloakIdentity(auth.getToken(), session);
+ }
+ }
+ MgmtPermissions(KeycloakSession session, AdminAuth auth) {
+ this.session = session;
+ this.auth = auth;
+ this.admin = auth.getUser();
+ this.adminsRealm = auth.getRealm();
+ if (auth.getClient().getClientId().equals(Constants.ADMIN_CLI_CLIENT_ID)
+ || auth.getClient().getClientId().equals(Constants.ADMIN_CONSOLE_CLIENT_ID)) {
+ this.identity = new UserModelIdentity(auth.getRealm(), auth.getUser());
+
+ } else {
+ this.identity = new KeycloakIdentity(auth.getToken(), session);
+ }
+ }
+ MgmtPermissions(KeycloakSession session, RealmModel realm, RealmModel adminsRealm, UserModel admin) {
+ this(session, realm);
+ this.admin = admin;
+ this.adminsRealm = adminsRealm;
+ this.identity = new UserModelIdentity(realm, admin);
+ }
+
+ public ClientModel getRealmManagementClient() {
+ ClientModel client = null;
+ if (realm.getName().equals(Config.getAdminRealm())) {
+ client = realm.getClientByClientId(Config.getAdminRealm() + "-realm");
+ } else {
+ client = realm.getClientByClientId(Constants.REALM_MANAGEMENT_CLIENT_ID);
+
+ }
+ return client;
+ }
+
+ @Override
+ public AuthorizationProvider authz() {
+ return authz;
+ }
+
+
+
+ @Override
+ public void requireAnyAdminRole() {
+ if (!hasAnyAdminRole()) {
+ throw new ForbiddenException();
+ }
+ }
+
+ public boolean hasAnyAdminRole() {
+ return hasOneAdminRole(AdminRoles.ALL_REALM_ROLES);
+ }
+
+ public boolean hasAnyAdminRole(RealmModel realm) {
+ return hasOneAdminRole(realm, AdminRoles.ALL_REALM_ROLES);
+ }
+
+ public boolean hasOneAdminRole(String... adminRoles) {
+ String clientId;
+ RealmModel realm = this.realm;
+ return hasOneAdminRole(realm, adminRoles);
+ }
+
+ public boolean hasOneAdminRole(RealmModel realm, String... adminRoles) {
+ String clientId;
+ RealmManager realmManager = new RealmManager(session);
+ if (adminsRealm.equals(realmManager.getKeycloakAdminstrationRealm())) {
+ clientId = realm.getMasterAdminClient().getClientId();
+ } else if (adminsRealm.equals(realm)) {
+ clientId = realm.getClientByClientId(realmManager.getRealmAdminClientId(realm)).getClientId();
+ } else {
+ return false;
+ }
+ for (String adminRole : adminRoles) {
+ if (identity.hasClientRole(clientId, adminRole)) return true;
+ }
+ return false;
+ }
+
+
+ public boolean isAdminSameRealm() {
+ return auth == null || realm.getId().equals(auth.getRealm().getId());
+ }
+
+ @Override
+ public AdminAuth adminAuth() {
+ return auth;
+ }
+
+ public Identity identity() {
+ return identity;
+ }
+
+ public UserModel admin() {
+ return admin;
+ }
+
+
+ @Override
+ public RolePermissions roles() {
+ return new RolePermissions(session, realm, authz, this);
+ }
+
+ @Override
+ public UserPermissions users() {
+ if (users != null) return users;
+ users = new UserPermissions(session, realm, authz, this);
+ return users;
+ }
+
+ @Override
+ public RealmPermissions realm() {
+ if (realmPermissions != null) return realmPermissions;
+ realmPermissions = new RealmPermissions(session, realm, authz, this);
+ return realmPermissions;
+ }
+
+ @Override
+ public ClientPermissions clients() {
+ if (clientPermissions != null) return clientPermissions;
+ clientPermissions = new ClientPermissions(session, realm, authz, this);
+ return clientPermissions;
+ }
+
+ @Override
+ public GroupPermissions groups() {
+ if (groups != null) return groups;
+ groups = new GroupPermissions(session, realm, authz, this);
+ return groups;
+ }
+
+ public ResourceServer findOrCreateResourceServer(ClientModel client) {
+ return initializeRealmResourceServer();
+ }
+
+ public ResourceServer resourceServer(ClientModel client) {
+ return realmResourceServer();
+ }
+
+ @Override
+ public ResourceServer realmResourceServer() {
+ if (realmResourceServer != null) return realmResourceServer;
+ ResourceServerStore resourceServerStore = authz.getStoreFactory().getResourceServerStore();
+ ClientModel client = getRealmManagementClient();
+ if (client == null) return null;
+ realmResourceServer = authz.getStoreFactory().getResourceServerStore().findByClient(client.getId());
+ return realmResourceServer;
+
+ }
+
+ public ResourceServer initializeRealmResourceServer() {
+ if (realmResourceServer != null) return realmResourceServer;
+ ClientModel client = getRealmManagementClient();
+ realmResourceServer = authz.getStoreFactory().getResourceServerStore().findByClient(client.getId());
+ if (realmResourceServer == null) {
+ realmResourceServer = authz.getStoreFactory().getResourceServerStore().create(client.getId());
+ }
+ return realmResourceServer;
+ }
+
+ protected Scope manageScope;
+ protected Scope viewScope;
+
+ public void initializeRealmDefaultScopes() {
+ ResourceServer server = initializeRealmResourceServer();
+ manageScope = initializeRealmScope(MgmtPermissions.MANAGE_SCOPE);
+ viewScope = initializeRealmScope(MgmtPermissions.VIEW_SCOPE);
+ }
+
+ public Scope initializeRealmScope(String name) {
+ ResourceServer server = initializeRealmResourceServer();
+ Scope scope = authz.getStoreFactory().getScopeStore().findByName(name, server.getId());
+ if (scope == null) {
+ scope = authz.getStoreFactory().getScopeStore().create(name, server);
+ }
+ return scope;
+ }
+
+
+
+ public Scope realmManageScope() {
+ if (manageScope != null) return manageScope;
+ manageScope = realmScope(MgmtPermissions.MANAGE_SCOPE);
+ return manageScope;
+ }
+
+
+ public Scope realmViewScope() {
+ if (viewScope != null) return viewScope;
+ viewScope = realmScope(MgmtPermissions.VIEW_SCOPE);
+ return viewScope;
+ }
+
+ public Scope realmScope(String scope) {
+ ResourceServer server = realmResourceServer();
+ if (server == null) return null;
+ return authz.getStoreFactory().getScopeStore().findByName(scope, server.getId());
+ }
+
+ public boolean evaluatePermission(Resource resource, Scope scope, ResourceServer resourceServer) {
+ Identity identity = identity();
+ if (identity == null) {
+ throw new RuntimeException("Identity of admin is not set for permission query");
+ }
+ return evaluatePermission(resource, scope, resourceServer, identity);
+ }
+
+ public boolean evaluatePermission(Resource resource, Scope scope, ResourceServer resourceServer, Identity identity) {
+ RealmModel oldRealm = session.getContext().getRealm();
+ try {
+ session.getContext().setRealm(realm);
+ EvaluationContext context = new DefaultEvaluationContext(identity, session);
+ DecisionResult decisionCollector = new DecisionResult();
+ List<ResourcePermission> permissions = Permissions.permission(resourceServer, resource, scope);
+ PermissionEvaluator from = authz.evaluators().from(permissions, context);
+ from.evaluate(decisionCollector);
+ if (!decisionCollector.completed()) {
+ logger.error("Failed to run permission check", decisionCollector.getError());
+ return false;
+ }
+ return decisionCollector.getResults().get(0).getEffect() == Decision.Effect.PERMIT;
+ } finally {
+ session.getContext().setRealm(oldRealm);
+ }
+ }
+
+ @Override
+ public boolean canView(RealmModel realm) {
+ return hasOneAdminRole(realm, AdminRoles.VIEW_REALM, AdminRoles.MANAGE_REALM);
+ }
+
+ @Override
+ public boolean isAdmin(RealmModel realm) {
+ return hasAnyAdminRole(realm);
+ }
+
+ @Override
+ public boolean isAdmin() {
+ RealmManager realmManager = new RealmManager(session);
+ if (adminsRealm.equals(realmManager.getKeycloakAdminstrationRealm())) {
+ if (identity.hasRealmRole(AdminRoles.ADMIN) || identity.hasRealmRole(AdminRoles.CREATE_REALM)) {
+ return true;
+ }
+ for (RealmModel realm : session.realms().getRealms()) {
+ if (isAdmin(realm)) return true;
+ }
+ return false;
+ } else {
+ return isAdmin(adminsRealm);
+ }
+ }
+
+ @Override
+ public boolean canCreateRealm() {
+ RealmManager realmManager = new RealmManager(session);
+ if (!auth.getRealm().equals(realmManager.getKeycloakAdminstrationRealm())) {
+ return false;
+ }
+ return identity.hasRealmRole(AdminRoles.CREATE_REALM);
+ }
+
+ @Override
+ public void requireCreateRealm() {
+ if (!canCreateRealm()) {
+ throw new ForbiddenException();
+ }
+ }
+
+
+
+
+}
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/permissions/RealmPermissionEvaluator.java b/services/src/main/java/org/keycloak/services/resources/admin/permissions/RealmPermissionEvaluator.java
new file mode 100644
index 0000000..7020667
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/resources/admin/permissions/RealmPermissionEvaluator.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.services.resources.admin.permissions;
+
+import org.keycloak.models.RealmModel;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public interface RealmPermissionEvaluator {
+ boolean canListRealms();
+
+ void requireViewRealmNameList();
+
+ boolean canManageRealm();
+
+ void requireManageRealm();
+
+ boolean canViewRealm();
+
+ void requireViewRealm();
+
+ boolean canManageIdentityProviders();
+
+ boolean canViewIdentityProviders();
+
+ void requireViewIdentityProviders();
+
+ void requireManageIdentityProviders();
+
+ boolean canManageAuthorization();
+
+ boolean canViewAuthorization();
+
+ void requireManageAuthorization();
+
+ void requireViewAuthorization();
+
+ boolean canManageEvents();
+
+ void requireManageEvents();
+
+ boolean canViewEvents();
+
+ void requireViewEvents();
+}
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/permissions/RealmPermissions.java b/services/src/main/java/org/keycloak/services/resources/admin/permissions/RealmPermissions.java
new file mode 100644
index 0000000..3fc752f
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/resources/admin/permissions/RealmPermissions.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.services.resources.admin.permissions;
+
+import org.jboss.logging.Logger;
+import org.keycloak.authorization.AuthorizationProvider;
+import org.keycloak.models.AdminRoles;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.services.ForbiddenException;
+
+/**
+ * Manages default policies for all users.
+ *
+ *
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+class RealmPermissions implements RealmPermissionEvaluator {
+ private static final Logger logger = Logger.getLogger(RealmPermissions.class);
+ protected final KeycloakSession session;
+ protected final RealmModel realm;
+ protected final AuthorizationProvider authz;
+ protected final MgmtPermissions root;
+
+ public RealmPermissions(KeycloakSession session, RealmModel realm, AuthorizationProvider authz, MgmtPermissions root) {
+ this.session = session;
+ this.realm = realm;
+ this.authz = authz;
+ this.root = root;
+ }
+
+
+ public boolean canManageRealmDefault() {
+ return root.hasOneAdminRole(AdminRoles.MANAGE_REALM);
+
+ }
+ public boolean canViewRealmDefault() {
+ return root.hasOneAdminRole(AdminRoles.MANAGE_REALM, AdminRoles.VIEW_REALM);
+ }
+
+ public boolean canManageIdentityProvidersDefault() {
+ return root.hasOneAdminRole(AdminRoles.MANAGE_IDENTITY_PROVIDERS);
+
+ }
+ public boolean canViewIdentityProvidersDefault() {
+ return root.hasOneAdminRole(AdminRoles.MANAGE_IDENTITY_PROVIDERS, AdminRoles.VIEW_IDENTITY_PROVIDERS);
+ }
+
+ public boolean canManageAuthorizationDefault() {
+ return root.hasOneAdminRole(AdminRoles.MANAGE_AUTHORIZATION);
+
+ }
+ public boolean canViewAuthorizationDefault() {
+ return root.hasOneAdminRole(AdminRoles.MANAGE_AUTHORIZATION, AdminRoles.VIEW_AUTHORIZATION);
+ }
+ public boolean canManageEventsDefault() {
+ return root.hasOneAdminRole(AdminRoles.MANAGE_EVENTS);
+ }
+ public boolean canViewEventsDefault() {
+ return root.hasOneAdminRole(AdminRoles.MANAGE_EVENTS, AdminRoles.VIEW_EVENTS);
+ }
+
+ @Override
+ public boolean canListRealms() {
+ return root.hasAnyAdminRole();
+ }
+
+ @Override
+ public void requireViewRealmNameList() {
+ if (!canListRealms()) {
+ throw new ForbiddenException();
+ }
+ }
+
+
+ @Override
+ public boolean canManageRealm() {
+ return canManageRealmDefault();
+ }
+
+ @Override
+ public void requireManageRealm() {
+ if (!canManageRealm()) {
+ throw new ForbiddenException();
+ }
+ }
+ @Override
+ public boolean canViewRealm() {
+ return canViewRealmDefault();
+ }
+
+ @Override
+ public void requireViewRealm() {
+ if (!canViewRealm()) {
+ throw new ForbiddenException();
+ }
+ }
+
+ @Override
+ public boolean canManageIdentityProviders() {
+ return canManageIdentityProvidersDefault();
+ }
+
+ @Override
+ public boolean canViewIdentityProviders() {
+ return canViewIdentityProvidersDefault();
+ }
+
+ @Override
+ public void requireViewIdentityProviders() {
+ if (!canViewIdentityProviders()) {
+ throw new ForbiddenException();
+ }
+ }
+
+
+ @Override
+ public void requireManageIdentityProviders() {
+ if (!canManageIdentityProviders()) {
+ throw new ForbiddenException();
+ }
+ }
+
+
+ @Override
+ public boolean canManageAuthorization() {
+ return canManageAuthorizationDefault();
+ }
+
+ @Override
+ public boolean canViewAuthorization() {
+ return canViewAuthorizationDefault();
+ }
+
+ @Override
+ public void requireManageAuthorization() {
+ if (!canManageAuthorization()) {
+ throw new ForbiddenException();
+ }
+ }
+ @Override
+ public void requireViewAuthorization() {
+ if (!canViewAuthorization()) {
+ throw new ForbiddenException();
+ }
+ }
+
+ @Override
+ public boolean canManageEvents() {
+ return canManageEventsDefault();
+ }
+
+ @Override
+ public void requireManageEvents() {
+ if (!canManageEvents()) {
+ throw new ForbiddenException();
+ }
+ }
+ @Override
+ public boolean canViewEvents() {
+ return canViewEventsDefault();
+ }
+
+ @Override
+ public void requireViewEvents() {
+ if (!canViewEvents()) {
+ throw new ForbiddenException();
+ }
+ }
+
+
+
+
+}
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/permissions/RealmsPermissionEvaluator.java b/services/src/main/java/org/keycloak/services/resources/admin/permissions/RealmsPermissionEvaluator.java
new file mode 100644
index 0000000..5286d10
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/resources/admin/permissions/RealmsPermissionEvaluator.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.services.resources.admin.permissions;
+
+import org.keycloak.models.RealmModel;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public interface RealmsPermissionEvaluator {
+ boolean canView(RealmModel realm);
+
+ boolean isAdmin(RealmModel realm);
+
+ boolean isAdmin();
+
+ boolean canCreateRealm();
+
+ void requireCreateRealm();
+}
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/permissions/RolePermissionEvaluator.java b/services/src/main/java/org/keycloak/services/resources/admin/permissions/RolePermissionEvaluator.java
new file mode 100644
index 0000000..a4a8b71
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/resources/admin/permissions/RolePermissionEvaluator.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.services.resources.admin.permissions;
+
+import org.keycloak.models.RoleContainerModel;
+import org.keycloak.models.RoleModel;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public interface RolePermissionEvaluator {
+ boolean canList(RoleContainerModel container);
+
+ void requireList(RoleContainerModel container);
+
+ boolean canMapRole(RoleModel role);
+ void requireMapRole(RoleModel role);
+
+ boolean canManage(RoleModel role);
+
+ void requireManage(RoleModel role);
+
+ boolean canView(RoleModel role);
+
+ void requireView(RoleModel role);
+
+ boolean canMapClientScope(RoleModel role);
+ void requireMapClientScope(RoleModel role);
+
+ boolean canMapComposite(RoleModel role);
+ void requireMapComposite(RoleModel role);
+
+ boolean canManage(RoleContainerModel container);
+
+ void requireManage(RoleContainerModel container);
+
+ boolean canView(RoleContainerModel container);
+
+ void requireView(RoleContainerModel container);
+
+}
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/permissions/RolePermissionManagement.java b/services/src/main/java/org/keycloak/services/resources/admin/permissions/RolePermissionManagement.java
new file mode 100644
index 0000000..977e109
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/resources/admin/permissions/RolePermissionManagement.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.services.resources.admin.permissions;
+
+import org.keycloak.authorization.model.Policy;
+import org.keycloak.authorization.model.Resource;
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.models.RoleContainerModel;
+import org.keycloak.models.RoleModel;
+
+import java.util.Map;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public interface RolePermissionManagement {
+ public static final String MAP_ROLE_SCOPE = "map-role";
+ public static final String MAP_ROLE_CLIENT_SCOPE_SCOPE = "map-role-client-scope";
+ public static final String MAP_ROLE_COMPOSITE_SCOPE = "map-role-composite";
+
+ boolean isPermissionsEnabled(RoleModel role);
+ void setPermissionsEnabled(RoleModel role, boolean enable);
+
+ Map<String, String> getPermissions(RoleModel role);
+
+ Policy mapRolePermission(RoleModel role);
+
+ Policy mapCompositePermission(RoleModel role);
+
+ Policy mapClientScopePermission(RoleModel role);
+
+ Resource resource(RoleModel role);
+
+ ResourceServer resourceServer(RoleModel role);
+
+ Policy manageUsersPolicy(ResourceServer server);
+
+ Policy viewUsersPolicy(ResourceServer server);
+
+ Policy rolePolicy(ResourceServer server, RoleModel role);
+}
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/permissions/RolePermissions.java b/services/src/main/java/org/keycloak/services/resources/admin/permissions/RolePermissions.java
new file mode 100644
index 0000000..091d7a5
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/resources/admin/permissions/RolePermissions.java
@@ -0,0 +1,585 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.services.resources.admin.permissions;
+
+import org.jboss.logging.Logger;
+import org.keycloak.Config;
+import org.keycloak.authorization.AuthorizationProvider;
+import org.keycloak.authorization.model.Policy;
+import org.keycloak.authorization.model.Resource;
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.model.Scope;
+import org.keycloak.authorization.store.ResourceStore;
+import org.keycloak.models.AdminRoles;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.ImpersonationConstants;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleContainerModel;
+import org.keycloak.models.RoleModel;
+import org.keycloak.representations.idm.authorization.DecisionStrategy;
+import org.keycloak.services.ForbiddenException;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+class RolePermissions implements RolePermissionEvaluator, RolePermissionManagement {
+ private static final Logger logger = Logger.getLogger(RolePermissions.class);
+ protected final KeycloakSession session;
+ protected final RealmModel realm;
+ protected final AuthorizationProvider authz;
+ protected final MgmtPermissions root;
+
+ public RolePermissions(KeycloakSession session, RealmModel realm, AuthorizationProvider authz, MgmtPermissions root) {
+ this.session = session;
+ this.realm = realm;
+ this.authz = authz;
+ this.root = root;
+ }
+
+ @Override
+ public boolean isPermissionsEnabled(RoleModel role) {
+ return mapRolePermission(role) != null;
+ }
+
+ @Override
+ public void setPermissionsEnabled(RoleModel role, boolean enable) {
+ if (enable) {
+ initialize(role);
+ } else {
+ disablePermissions(role);
+ }
+ }
+
+ private void disablePermissions(RoleModel role) {
+ ResourceServer server = resourceServer(role);
+ if (server == null) return;
+ Policy policy = mapRolePermission(role);
+ if (policy != null) authz.getStoreFactory().getPolicyStore().delete(policy.getId());
+ policy = mapClientScopePermission(role);
+ if (policy != null) authz.getStoreFactory().getPolicyStore().delete(policy.getId());
+ policy = mapCompositePermission(role);
+ if (policy != null) authz.getStoreFactory().getPolicyStore().delete(policy.getId());
+
+ Resource resource = authz.getStoreFactory().getResourceStore().findByName(getRoleResourceName(role), server.getId());
+ if (resource != null) authz.getStoreFactory().getResourceStore().delete(resource.getId());
+ }
+
+ @Override
+ public Map<String, String> getPermissions(RoleModel role) {
+ Map<String, String> scopes = new HashMap<>();
+ scopes.put(RolePermissionManagement.MAP_ROLE_SCOPE, mapRolePermission(role).getId());
+ scopes.put(RolePermissionManagement.MAP_ROLE_CLIENT_SCOPE_SCOPE, mapClientScopePermission(role).getId());
+ scopes.put(RolePermissionManagement.MAP_ROLE_COMPOSITE_SCOPE, mapCompositePermission(role).getId());
+ return scopes;
+ }
+
+ @Override
+ public Policy mapRolePermission(RoleModel role) {
+ ResourceServer server = resourceServer(role);
+ if (server == null) return null;
+ return authz.getStoreFactory().getPolicyStore().findByName(getMapRolePermissionName(role), server.getId());
+ }
+
+ @Override
+ public Policy mapCompositePermission(RoleModel role) {
+ ResourceServer server = resourceServer(role);
+ if (server == null) return null;
+
+ return authz.getStoreFactory().getPolicyStore().findByName(getMapCompositePermissionName(role), server.getId());
+ }
+
+ @Override
+ public Policy mapClientScopePermission(RoleModel role) {
+ ResourceServer server = resourceServer(role);
+ if (server == null) return null;
+
+ return authz.getStoreFactory().getPolicyStore().findByName(getMapClientScopePermissionName(role), server.getId());
+ }
+
+ @Override
+ public Resource resource(RoleModel role) {
+ ResourceStore resourceStore = authz.getStoreFactory().getResourceStore();
+ ResourceServer server = resourceServer(role);
+ if (server == null) return null;
+ return resourceStore.findByName(getRoleResourceName(role), server.getId());
+ }
+
+ @Override
+ public ResourceServer resourceServer(RoleModel role) {
+ ClientModel client = getRoleClient(role);
+ return root.resourceServer(client);
+ }
+
+ private boolean checkAdminRoles(RoleModel role) {
+ if (AdminRoles.ALL_ROLES.contains(role.getName())) {
+ if (root.admin().hasRole(role)) return true;
+
+ ClientModel adminClient = root.getRealmManagementClient();
+ if (adminClient.equals(role.getContainer())) {
+ // if this is realm admin role, then check to see if admin has similar permissions
+ // we do this so that the authz service is invoked
+ if (role.getName().equals(AdminRoles.MANAGE_CLIENTS)) {
+ if (!root.clients().canManage()) {
+ return adminConflictMessage(role);
+ } else {
+ return true;
+ }
+ } else if (role.getName().equals(AdminRoles.VIEW_CLIENTS)) {
+ if (!root.clients().canView()) {
+ return adminConflictMessage(role);
+ } else {
+ return true;
+ }
+ } else if (role.getName().equals(AdminRoles.QUERY_CLIENTS)) {
+ return true;
+ } else if (role.getName().equals(AdminRoles.QUERY_USERS)) {
+ return true;
+ } else if (role.getName().equals(AdminRoles.QUERY_GROUPS)) {
+ return true;
+ } else if (role.getName().equals(AdminRoles.MANAGE_AUTHORIZATION)) {
+ if (!root.realm().canManageAuthorization()) {
+ return adminConflictMessage(role);
+ } else {
+ return true;
+ }
+ } else if (role.getName().equals(AdminRoles.VIEW_AUTHORIZATION)) {
+ if (!root.realm().canViewAuthorization()) {
+ return adminConflictMessage(role);
+ } else {
+ return true;
+ }
+ } else if (role.getName().equals(AdminRoles.MANAGE_EVENTS)) {
+ if (!root.realm().canManageEvents()) {
+ return adminConflictMessage(role);
+ } else {
+ return true;
+ }
+ } else if (role.getName().equals(AdminRoles.VIEW_EVENTS)) {
+ if (!root.realm().canViewEvents()) {
+ return adminConflictMessage(role);
+ } else {
+ return true;
+ }
+ } else if (role.getName().equals(AdminRoles.MANAGE_USERS)) {
+ if (!root.users().canManage()) {
+ return adminConflictMessage(role);
+ } else {
+ return true;
+ }
+ } else if (role.getName().equals(AdminRoles.VIEW_USERS)) {
+ if (!root.users().canView()) {
+ return adminConflictMessage(role);
+ } else {
+ return true;
+ }
+ } else if (role.getName().equals(AdminRoles.MANAGE_IDENTITY_PROVIDERS)) {
+ if (!root.realm().canManageIdentityProviders()) {
+ return adminConflictMessage(role);
+ } else {
+ return true;
+ }
+ } else if (role.getName().equals(AdminRoles.VIEW_IDENTITY_PROVIDERS)) {
+ if (!root.realm().canViewIdentityProviders()) {
+ return adminConflictMessage(role);
+ } else {
+ return true;
+ }
+ } else if (role.getName().equals(AdminRoles.MANAGE_REALM)) {
+ if (!root.realm().canManageRealm()) {
+ return adminConflictMessage(role);
+ } else {
+ return true;
+ }
+ } else if (role.getName().equals(AdminRoles.VIEW_REALM)) {
+ if (!root.realm().canViewRealm()) {
+ return adminConflictMessage(role);
+ } else {
+ return true;
+ }
+ } else if (role.getName().equals(ImpersonationConstants.IMPERSONATION_ROLE)) {
+ if (!root.users().canImpersonate()) {
+ return adminConflictMessage(role);
+ } else {
+ return true;
+ }
+ } else {
+ return adminConflictMessage(role);
+ }
+
+ } else {
+ // now we need to check to see if this is a master admin role
+ if (role.getContainer() instanceof RealmModel) {
+ RealmModel realm = (RealmModel)role.getContainer();
+ // If realm role is master admin role then abort
+ if (realm.getName().equals(Config.getAdminRealm())) {
+ return adminConflictMessage(role);
+ }
+ } else {
+ ClientModel container = (ClientModel)role.getContainer();
+ // abort if this is an role in master realm and role is an admin role of any realm
+ if (container.getRealm().getName().equals(Config.getAdminRealm())
+ && container.getClientId().endsWith("-realm")) {
+ return adminConflictMessage(role);
+ }
+ }
+ return true;
+ }
+
+ }
+ return true;
+
+ }
+
+ private boolean adminConflictMessage(RoleModel role) {
+ logger.debug("Trying to assign admin privileges of role: " + role.getName() + " but admin doesn't have same privilege");
+ return false;
+ }
+
+ /**
+ * Is admin allowed to map this role?
+ *
+ * @param role
+ * @return
+ */
+ @Override
+ public boolean canMapRole(RoleModel role) {
+ if (root.users().canManageDefault()) return checkAdminRoles(role);
+ if (!root.isAdminSameRealm()) {
+ return false;
+ }
+
+ if (role.getContainer() instanceof ClientModel) {
+ if (root.clients().canMapRoles((ClientModel)role.getContainer())) return true;
+ }
+ if (!isPermissionsEnabled(role)){
+ return false;
+ }
+
+ ResourceServer resourceServer = resourceServer(role);
+ if (resourceServer == null) return false;
+
+ Policy policy = authz.getStoreFactory().getPolicyStore().findByName(getMapRolePermissionName(role), resourceServer.getId());
+ if (policy == null || policy.getAssociatedPolicies().isEmpty()) {
+ return false;
+ }
+
+ Resource roleResource = resource(role);
+ Scope mapRoleScope = mapRoleScope(resourceServer);
+ if (root.evaluatePermission(roleResource, mapRoleScope, resourceServer)) {
+ return checkAdminRoles(role);
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public void requireMapRole(RoleModel role) {
+ if (!canMapRole(role)) {
+ throw new ForbiddenException();
+ }
+
+ }
+
+ @Override
+ public boolean canList(RoleContainerModel container) {
+ return root.hasAnyAdminRole();
+ }
+
+ @Override
+ public void requireList(RoleContainerModel container) {
+ if (!canList(container)) {
+ throw new ForbiddenException();
+ }
+
+ }
+
+ @Override
+ public boolean canManage(RoleContainerModel container) {
+ if (container instanceof RealmModel) {
+ return root.realm().canManageRealm();
+ } else {
+ return root.clients().canConfigure((ClientModel)container);
+ }
+ }
+
+ @Override
+ public void requireManage(RoleContainerModel container) {
+ if (!canManage(container)) {
+ throw new ForbiddenException();
+ }
+ }
+
+ @Override
+ public boolean canView(RoleContainerModel container) {
+ if (container instanceof RealmModel) {
+ return root.realm().canViewRealm();
+ } else {
+ return root.clients().canView((ClientModel)container);
+ }
+ }
+
+ @Override
+ public void requireView(RoleContainerModel container) {
+ if (!canView(container)) {
+ throw new ForbiddenException();
+ }
+ }
+
+ @Override
+ public boolean canMapComposite(RoleModel role) {
+ if (canManageDefault(role)) return checkAdminRoles(role);
+
+ if (!root.isAdminSameRealm()) {
+ return false;
+ }
+ if (role.getContainer() instanceof ClientModel) {
+ if (root.clients().canMapCompositeRoles((ClientModel)role.getContainer())) return true;
+ }
+ if (!isPermissionsEnabled(role)){
+ return false;
+ }
+
+ ResourceServer resourceServer = resourceServer(role);
+ if (resourceServer == null) return false;
+
+ Policy policy = authz.getStoreFactory().getPolicyStore().findByName(getMapCompositePermissionName(role), resourceServer.getId());
+ if (policy == null || policy.getAssociatedPolicies().isEmpty()) {
+ return false;
+ }
+
+ Resource roleResource = resource(role);
+ Scope scope = mapCompositeScope(resourceServer);
+ if (root.evaluatePermission(roleResource, scope, resourceServer)) {
+ return checkAdminRoles(role);
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public void requireMapComposite(RoleModel role) {
+ if (!canMapComposite(role)) {
+ throw new ForbiddenException();
+ }
+
+ }
+
+
+ @Override
+ public boolean canMapClientScope(RoleModel role) {
+ if (root.clients().canManageClientsDefault()) return true;
+ if (!root.isAdminSameRealm()) {
+ return false;
+ }
+ if (role.getContainer() instanceof ClientModel) {
+ if (root.clients().canMapClientScopeRoles((ClientModel)role.getContainer())) return true;
+ }
+ if (!isPermissionsEnabled(role)){
+ return false;
+ }
+
+ ResourceServer resourceServer = resourceServer(role);
+ if (resourceServer == null) return false;
+
+ Policy policy = authz.getStoreFactory().getPolicyStore().findByName(getMapClientScopePermissionName(role), resourceServer.getId());
+ if (policy == null || policy.getAssociatedPolicies().isEmpty()) {
+ return false;
+ }
+
+ Resource roleResource = resource(role);
+ Scope scope = mapClientScope(resourceServer);
+ return root.evaluatePermission(roleResource, scope, resourceServer);
+ }
+
+ @Override
+ public void requireMapClientScope(RoleModel role) {
+ if (!canMapClientScope(role)) {
+ throw new ForbiddenException();
+ }
+ }
+
+
+ @Override
+ public boolean canManage(RoleModel role) {
+ if (role.getContainer() instanceof RealmModel) {
+ return root.realm().canManageRealm();
+ } else if (role.getContainer() instanceof ClientModel) {
+ ClientModel client = (ClientModel)role.getContainer();
+ return root.clients().canManage(client);
+ }
+ return false;
+ }
+
+ public boolean canManageDefault(RoleModel role) {
+ if (role.getContainer() instanceof RealmModel) {
+ return root.realm().canManageRealmDefault();
+ } else if (role.getContainer() instanceof ClientModel) {
+ ClientModel client = (ClientModel)role.getContainer();
+ return root.clients().canManageClientsDefault();
+ }
+ return false;
+ }
+
+ @Override
+ public void requireManage(RoleModel role) {
+ if (!canManage(role)) {
+ throw new ForbiddenException();
+ }
+
+ }
+
+ @Override
+ public boolean canView(RoleModel role) {
+ if (role.getContainer() instanceof RealmModel) {
+ return root.realm().canViewRealm();
+ } else if (role.getContainer() instanceof ClientModel) {
+ ClientModel client = (ClientModel)role.getContainer();
+ return root.clients().canView(client);
+ }
+ return false;
+ }
+
+ @Override
+ public void requireView(RoleModel role) {
+ if (!canView(role)) {
+ throw new ForbiddenException();
+ }
+
+ }
+
+ private ClientModel getRoleClient(RoleModel role) {
+ ClientModel client = null;
+ if (role.getContainer() instanceof ClientModel) {
+ client = (ClientModel)role.getContainer();
+ } else {
+ client = root.getRealmManagementClient();
+ }
+ return client;
+ }
+
+ @Override
+ public Policy manageUsersPolicy(ResourceServer server) {
+ RoleModel role = root.getRealmManagementClient().getRole(AdminRoles.MANAGE_USERS);
+ return rolePolicy(server, role);
+ }
+
+ @Override
+ public Policy viewUsersPolicy(ResourceServer server) {
+ RoleModel role = root.getRealmManagementClient().getRole(AdminRoles.VIEW_USERS);
+ return rolePolicy(server, role);
+ }
+
+ @Override
+ public Policy rolePolicy(ResourceServer server, RoleModel role) {
+ String policyName = Helper.getRolePolicyName(role);
+ Policy policy = authz.getStoreFactory().getPolicyStore().findByName(policyName, server.getId());
+ if (policy != null) return policy;
+ return Helper.createRolePolicy(authz, server, role, policyName);
+ }
+
+ private Scope mapRoleScope(ResourceServer server) {
+ return authz.getStoreFactory().getScopeStore().findByName(MAP_ROLE_SCOPE, server.getId());
+ }
+
+ private Scope mapClientScope(ResourceServer server) {
+ return authz.getStoreFactory().getScopeStore().findByName(MAP_ROLE_CLIENT_SCOPE_SCOPE, server.getId());
+ }
+
+ private Scope mapCompositeScope(ResourceServer server) {
+ return authz.getStoreFactory().getScopeStore().findByName(MAP_ROLE_COMPOSITE_SCOPE, server.getId());
+ }
+
+
+ private void initialize(RoleModel role) {
+ ResourceServer server = resourceServer(role);
+ if (server == null) {
+ ClientModel client = getRoleClient(role);
+ server = root.findOrCreateResourceServer(client);
+ }
+ Scope mapRoleScope = mapRoleScope(server);
+ if (mapRoleScope == null) {
+ mapRoleScope = authz.getStoreFactory().getScopeStore().create(MAP_ROLE_SCOPE, server);
+ }
+ Scope mapClientScope = mapClientScope(server);
+ if (mapClientScope == null) {
+ mapClientScope = authz.getStoreFactory().getScopeStore().create(MAP_ROLE_CLIENT_SCOPE_SCOPE, server);
+ }
+ Scope mapCompositeScope = mapCompositeScope(server);
+ if (mapCompositeScope == null) {
+ mapCompositeScope = authz.getStoreFactory().getScopeStore().create(MAP_ROLE_COMPOSITE_SCOPE, server);
+ }
+
+ String roleResourceName = getRoleResourceName(role);
+ Resource resource = authz.getStoreFactory().getResourceStore().findByName(roleResourceName, server.getId());
+ if (resource == null) {
+ resource = authz.getStoreFactory().getResourceStore().create(roleResourceName, server, server.getClientId());
+ Set<Scope> scopeset = new HashSet<>();
+ scopeset.add(mapClientScope);
+ scopeset.add(mapCompositeScope);
+ scopeset.add(mapRoleScope);
+ resource.updateScopes(scopeset);
+ resource.setType("Role");
+ }
+ Policy mapRolePermission = mapRolePermission(role);
+ if (mapRolePermission == null) {
+ mapRolePermission = Helper.addEmptyScopePermission(authz, server, getMapRolePermissionName(role), resource, mapRoleScope);
+ mapRolePermission.setDecisionStrategy(DecisionStrategy.AFFIRMATIVE);
+ }
+
+ Policy mapClientScopePermission = mapClientScopePermission(role);
+ if (mapClientScopePermission == null) {
+ mapClientScopePermission = Helper.addEmptyScopePermission(authz, server, getMapClientScopePermissionName(role), resource, mapClientScope);
+ mapClientScopePermission.setDecisionStrategy(DecisionStrategy.AFFIRMATIVE);
+ }
+
+ Policy mapCompositePermission = mapCompositePermission(role);
+ if (mapCompositePermission == null) {
+ mapCompositePermission = Helper.addEmptyScopePermission(authz, server, getMapCompositePermissionName(role), resource, mapCompositeScope);
+ mapCompositePermission.setDecisionStrategy(DecisionStrategy.AFFIRMATIVE);
+ }
+ }
+
+ private String getMapRolePermissionName(RoleModel role) {
+ return MAP_ROLE_SCOPE + ".permission." + role.getId();
+ }
+
+ private String getMapClientScopePermissionName(RoleModel role) {
+ return MAP_ROLE_CLIENT_SCOPE_SCOPE + ".permission." + role.getId();
+ }
+
+ private String getMapCompositePermissionName(RoleModel role) {
+ return MAP_ROLE_COMPOSITE_SCOPE + ".permission." + role.getId();
+ }
+
+ private ResourceServer sdfgetResourceServer(RoleModel role) {
+ ClientModel client = getRoleClient(role);
+ return root.findOrCreateResourceServer(client);
+ }
+
+ private static String getRoleResourceName(RoleModel role) {
+ return "role.resource." + role.getId();
+ }
+
+
+}
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/permissions/UserPermissionEvaluator.java b/services/src/main/java/org/keycloak/services/resources/admin/permissions/UserPermissionEvaluator.java
new file mode 100644
index 0000000..40b1f8f
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/resources/admin/permissions/UserPermissionEvaluator.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.services.resources.admin.permissions;
+
+import org.keycloak.authorization.model.Policy;
+import org.keycloak.models.UserModel;
+
+import java.util.Map;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public interface UserPermissionEvaluator {
+ boolean canManage();
+
+ void requireManage();
+
+ boolean canManage(UserModel user);
+ void requireManage(UserModel user);
+
+ boolean canQuery();
+
+ void requireQuery();
+
+ boolean canQuery(UserModel user);
+
+ void requireQuery(UserModel user);
+
+ boolean canView();
+ boolean canView(UserModel user);
+ void requireView(UserModel user);
+
+ void requireView();
+
+ boolean canImpersonate(UserModel user);
+
+ boolean canImpersonate();
+
+ void requireImpersonate(UserModel user);
+
+ Map<String, Boolean> getAccess(UserModel user);
+
+ boolean canMapRoles(UserModel user);
+
+ void requireMapRoles(UserModel user);
+
+ boolean canManageGroupMembership(UserModel user);
+
+ void requireManageGroupMembership(UserModel user);
+}
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/permissions/UserPermissionManagement.java b/services/src/main/java/org/keycloak/services/resources/admin/permissions/UserPermissionManagement.java
new file mode 100644
index 0000000..d465378
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/resources/admin/permissions/UserPermissionManagement.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.services.resources.admin.permissions;
+
+import org.keycloak.authorization.model.Policy;
+import org.keycloak.authorization.model.Resource;
+import org.keycloak.models.RoleModel;
+
+import java.util.Map;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public interface UserPermissionManagement {
+ boolean isPermissionsEnabled();
+
+ void setPermissionsEnabled(boolean enable);
+
+ Map<String, String> getPermissions();
+
+ Resource resource();
+
+ Policy managePermission();
+
+ Policy viewPermission();
+
+ Policy manageGroupMembershipPermission();
+
+ Policy mapRolesPermission();
+
+ Policy adminImpersonatingPermission();
+
+ Policy userImpersonatedPermission();
+}
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/permissions/UserPermissions.java b/services/src/main/java/org/keycloak/services/resources/admin/permissions/UserPermissions.java
new file mode 100644
index 0000000..149e526
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/resources/admin/permissions/UserPermissions.java
@@ -0,0 +1,635 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.services.resources.admin.permissions;
+
+import org.jboss.logging.Logger;
+import org.keycloak.authorization.AuthorizationProvider;
+import org.keycloak.authorization.common.UserModelIdentity;
+import org.keycloak.authorization.identity.Identity;
+import org.keycloak.authorization.model.Policy;
+import org.keycloak.authorization.model.Resource;
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.authorization.model.Scope;
+import org.keycloak.models.AdminRoles;
+import org.keycloak.models.GroupModel;
+import org.keycloak.models.ImpersonationConstants;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.services.ForbiddenException;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Manages default policies for all users.
+ *
+ *
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+class UserPermissions implements UserPermissionEvaluator, UserPermissionManagement {
+ private static final Logger logger = Logger.getLogger(UserPermissions.class);
+ public static final String MAP_ROLES_SCOPE="map-roles";
+ public static final String IMPERSONATE_SCOPE="impersonate";
+ public static final String USER_IMPERSONATED_SCOPE="user-impersonated";
+ public static final String MANAGE_GROUP_MEMBERSHIP_SCOPE="manage-group-membership";
+ public static final String MAP_ROLES_PERMISSION_USERS = "map-roles.permission.users";
+ public static final String ADMIN_IMPERSONATING_PERMISSION = "admin-impersonating.permission.users";
+ public static final String USER_IMPERSONATED_PERMISSION = "user-impersonated.permission.users";
+ public static final String MANAGE_GROUP_MEMBERSHIP_PERMISSION_USERS = "manage-group-membership.permission.users";
+ public static final String MANAGE_PERMISSION_USERS = "manage.permission.users";
+ public static final String VIEW_PERMISSION_USERS = "view.permission.users";
+ public static final String USERS_RESOURCE = "Users";
+ protected final KeycloakSession session;
+ protected final RealmModel realm;
+ protected final AuthorizationProvider authz;
+ protected final MgmtPermissions root;
+
+ public UserPermissions(KeycloakSession session, RealmModel realm, AuthorizationProvider authz, MgmtPermissions root) {
+ this.session = session;
+ this.realm = realm;
+ this.authz = authz;
+ this.root = root;
+ }
+
+
+ private void initialize() {
+ root.initializeRealmResourceServer();
+ root.initializeRealmDefaultScopes();
+ ResourceServer server = root.realmResourceServer();
+ Scope manageScope = root.realmManageScope();
+ Scope viewScope = root.realmViewScope();
+ Scope mapRolesScope = root.initializeRealmScope(MAP_ROLES_SCOPE);
+ Scope impersonateScope = root.initializeRealmScope(IMPERSONATE_SCOPE);
+ Scope userImpersonatedScope = root.initializeRealmScope(USER_IMPERSONATED_SCOPE);
+ Scope manageGroupMembershipScope = root.initializeRealmScope(MANAGE_GROUP_MEMBERSHIP_SCOPE);
+
+ Resource usersResource = authz.getStoreFactory().getResourceStore().findByName(USERS_RESOURCE, server.getId());
+ if (usersResource == null) {
+ usersResource = authz.getStoreFactory().getResourceStore().create(USERS_RESOURCE, server, server.getClientId());
+ Set<Scope> scopeset = new HashSet<>();
+ scopeset.add(manageScope);
+ scopeset.add(viewScope);
+ scopeset.add(mapRolesScope);
+ scopeset.add(impersonateScope);
+ scopeset.add(manageGroupMembershipScope);
+ scopeset.add(userImpersonatedScope);
+ usersResource.updateScopes(scopeset);
+ }
+ Policy managePermission = authz.getStoreFactory().getPolicyStore().findByName(MANAGE_PERMISSION_USERS, server.getId());
+ if (managePermission == null) {
+ Helper.addEmptyScopePermission(authz, server, MANAGE_PERMISSION_USERS, usersResource, manageScope);
+ }
+ Policy viewPermission = authz.getStoreFactory().getPolicyStore().findByName(VIEW_PERMISSION_USERS, server.getId());
+ if (viewPermission == null) {
+ Helper.addEmptyScopePermission(authz, server, VIEW_PERMISSION_USERS, usersResource, viewScope);
+ }
+ Policy mapRolesPermission = authz.getStoreFactory().getPolicyStore().findByName(MAP_ROLES_PERMISSION_USERS, server.getId());
+ if (mapRolesPermission == null) {
+ Helper.addEmptyScopePermission(authz, server, MAP_ROLES_PERMISSION_USERS, usersResource, mapRolesScope);
+ }
+ Policy membershipPermission = authz.getStoreFactory().getPolicyStore().findByName(MANAGE_GROUP_MEMBERSHIP_PERMISSION_USERS, server.getId());
+ if (membershipPermission == null) {
+ Helper.addEmptyScopePermission(authz, server, MANAGE_GROUP_MEMBERSHIP_PERMISSION_USERS, usersResource, manageGroupMembershipScope);
+ }
+ Policy impersonatePermission = authz.getStoreFactory().getPolicyStore().findByName(ADMIN_IMPERSONATING_PERMISSION, server.getId());
+ if (impersonatePermission == null) {
+ Helper.addEmptyScopePermission(authz, server, ADMIN_IMPERSONATING_PERMISSION, usersResource, impersonateScope);
+ }
+ impersonatePermission = authz.getStoreFactory().getPolicyStore().findByName(USER_IMPERSONATED_PERMISSION, server.getId());
+ if (impersonatePermission == null) {
+ Helper.addEmptyScopePermission(authz, server, USER_IMPERSONATED_PERMISSION, usersResource, userImpersonatedScope);
+ }
+ }
+
+ @Override
+ public Map<String, String> getPermissions() {
+ Map<String, String> scopes = new HashMap<>();
+ scopes.put(AdminPermissionManagement.MANAGE_SCOPE, managePermission().getId());
+ scopes.put(AdminPermissionManagement.VIEW_SCOPE, viewPermission().getId());
+ scopes.put(MAP_ROLES_SCOPE, mapRolesPermission().getId());
+ scopes.put(MANAGE_GROUP_MEMBERSHIP_SCOPE, manageGroupMembershipPermission().getId());
+ scopes.put(IMPERSONATE_SCOPE, adminImpersonatingPermission().getId());
+ scopes.put(USER_IMPERSONATED_SCOPE, userImpersonatedPermission().getId());
+ return scopes;
+ }
+
+ @Override
+ public boolean isPermissionsEnabled() {
+ ResourceServer server = root.realmResourceServer();
+ if (server == null) return false;
+
+ Resource resource = authz.getStoreFactory().getResourceStore().findByName(USERS_RESOURCE, server.getId());
+ if (resource == null) return false;
+
+ Policy policy = managePermission();
+
+ return policy != null;
+ }
+
+ @Override
+ public void setPermissionsEnabled(boolean enable) {
+ if (enable) {
+ initialize();
+ } else {
+ deletePermissionSetup();
+ }
+ }
+
+ private void deletePermissionSetup() {
+ ResourceServer server = root.realmResourceServer();
+ if (server == null) return;
+ Policy policy = managePermission();
+ if (policy == null) {
+ authz.getStoreFactory().getPolicyStore().delete(policy.getId());
+
+ }
+ policy = viewPermission();
+ if (policy == null) {
+ authz.getStoreFactory().getPolicyStore().delete(policy.getId());
+
+ }
+ policy = mapRolesPermission();
+ if (policy == null) {
+ authz.getStoreFactory().getPolicyStore().delete(policy.getId());
+
+ }
+ policy = manageGroupMembershipPermission();
+ if (policy == null) {
+ authz.getStoreFactory().getPolicyStore().delete(policy.getId());
+
+ }
+ policy = adminImpersonatingPermission();
+ if (policy == null) {
+ authz.getStoreFactory().getPolicyStore().delete(policy.getId());
+
+ }
+ policy = userImpersonatedPermission();
+ if (policy == null) {
+ authz.getStoreFactory().getPolicyStore().delete(policy.getId());
+
+ }
+ Resource usersResource = authz.getStoreFactory().getResourceStore().findByName(USERS_RESOURCE, server.getId());
+ if (usersResource == null) {
+ authz.getStoreFactory().getResourceStore().delete(usersResource.getId());
+ }
+ }
+
+ public boolean canManageDefault() {
+ return root.hasOneAdminRole(AdminRoles.MANAGE_USERS);
+ }
+
+ @Override
+ public Resource resource() {
+ ResourceServer server = root.realmResourceServer();
+ if (server == null) return null;
+
+ return authz.getStoreFactory().getResourceStore().findByName(USERS_RESOURCE, server.getId());
+ }
+
+ @Override
+ public Policy managePermission() {
+ ResourceServer server = root.realmResourceServer();
+ return authz.getStoreFactory().getPolicyStore().findByName(MANAGE_PERMISSION_USERS, server.getId());
+ }
+
+ @Override
+ public Policy viewPermission() {
+ ResourceServer server = root.realmResourceServer();
+ return authz.getStoreFactory().getPolicyStore().findByName(VIEW_PERMISSION_USERS, server.getId());
+ }
+
+ @Override
+ public Policy manageGroupMembershipPermission() {
+ ResourceServer server = root.realmResourceServer();
+ return authz.getStoreFactory().getPolicyStore().findByName(MANAGE_GROUP_MEMBERSHIP_PERMISSION_USERS, server.getId());
+ }
+
+ @Override
+ public Policy mapRolesPermission() {
+ ResourceServer server = root.realmResourceServer();
+ return authz.getStoreFactory().getPolicyStore().findByName(MAP_ROLES_PERMISSION_USERS, server.getId());
+ }
+
+
+ @Override
+ public Policy adminImpersonatingPermission() {
+ ResourceServer server = root.realmResourceServer();
+ return authz.getStoreFactory().getPolicyStore().findByName(ADMIN_IMPERSONATING_PERMISSION, server.getId());
+ }
+
+ @Override
+ public Policy userImpersonatedPermission() {
+ ResourceServer server = root.realmResourceServer();
+ return authz.getStoreFactory().getPolicyStore().findByName(USER_IMPERSONATED_PERMISSION, server.getId());
+ }
+
+
+
+ /**
+ * Is admin allowed to manage all users? In Authz terms, does the admin have the "manage" scope for the Users Authz resource?
+ *
+ * This method will follow the old default behavior (does the admin have the manage-users role) if any of these conditions
+ * are met.:
+ * - The admin is from the master realm managing a different realm
+ * - If the Authz objects are not set up correctly for the Users resource in Authz
+ * - The "manage" permission for the Users resource has an empty associatedPolicy list.
+ *
+ * Otherwise, it will use the Authz policy engine to resolve this answer.
+ *
+ * @return
+ */
+ @Override
+ public boolean canManage() {
+ if (canManageDefault()) return true;
+ if (!root.isAdminSameRealm()) {
+ return false;
+ }
+
+ ResourceServer server = root.realmResourceServer();
+ if (server == null) return false;
+
+ Resource resource = authz.getStoreFactory().getResourceStore().findByName(USERS_RESOURCE, server.getId());
+ if (resource == null) return false;
+
+ Policy policy = authz.getStoreFactory().getPolicyStore().findByName(MANAGE_PERMISSION_USERS, server.getId());
+ if (policy == null) {
+ return false;
+ }
+
+ Set<Policy> associatedPolicies = policy.getAssociatedPolicies();
+ // if no policies attached to permission then just do default behavior
+ if (associatedPolicies == null || associatedPolicies.isEmpty()) {
+ return false;
+ }
+
+ Scope scope = root.realmManageScope();
+ return root.evaluatePermission(resource, scope, server);
+
+ }
+
+ @Override
+ public void requireManage() {
+ if (!canManage()) {
+ throw new ForbiddenException();
+ }
+ }
+
+
+ /**
+ * Does current admin have manage permissions for this particular user?
+ *
+ * @param user
+ * @return
+ */
+ @Override
+ public boolean canManage(UserModel user) {
+ return canManage() || canManageByGroup(user);
+ }
+
+ @Override
+ public void requireManage(UserModel user) {
+ if (!canManage(user)) {
+ throw new ForbiddenException();
+ }
+ }
+
+ private interface EvaluateGroup {
+ boolean evaluate(GroupModel group);
+ }
+
+ private boolean evaluateGroups(UserModel user, EvaluateGroup eval) {
+ for (GroupModel group : user.getGroups()) {
+ if (eval.evaluate(group)) return true;
+ }
+ return false;
+ }
+
+ private boolean evaluateHierarchy(UserModel user, EvaluateGroup eval) {
+ Set<GroupModel> visited = new HashSet<>();
+ for (GroupModel group : user.getGroups()) {
+ if (evaluateHierarchy(eval, group, visited)) return true;
+ }
+ return false;
+ }
+
+ private boolean evaluateHierarchy(EvaluateGroup eval, GroupModel group, Set<GroupModel> visited) {
+ if (visited.contains(group)) return false;
+ if (eval.evaluate(group)) {
+ return true;
+ }
+ visited.add(group);
+ if (group.getParent() == null) return false;
+ return evaluateHierarchy(eval, group.getParent(), visited);
+ }
+
+ private boolean canManageByGroup(UserModel user) {
+ /* no inheritance
+ return evaluateGroups(user,
+ (group) -> root.groups().canViewMembers(group)
+ );
+ */
+
+ /* inheritance
+ */
+ return evaluateHierarchy(user, (group) -> root.groups().canManageMembers(group));
+
+ }
+ private boolean canViewByGroup(UserModel user) {
+ /* no inheritance
+ return evaluateGroups(user,
+ (group) -> root.groups().canViewMembers(group)
+ );
+ */
+
+ /* inheritance
+ */
+ return evaluateHierarchy(user, (group) -> root.groups().canViewMembers(group));
+ }
+
+ public boolean canViewDefault() {
+ return root.hasOneAdminRole(AdminRoles.MANAGE_USERS, AdminRoles.VIEW_USERS);
+ }
+
+ @Override
+ public boolean canQuery() {
+ return canView() || root.hasOneAdminRole(AdminRoles.QUERY_USERS);
+ }
+
+ @Override
+ public void requireQuery() {
+ if (!canQuery()) {
+ throw new ForbiddenException();
+ }
+ }
+
+ @Override
+ public boolean canQuery(UserModel user) {
+ return canView(user);
+ }
+
+ @Override
+ public void requireQuery(UserModel user) {
+ if (!canQuery(user)) {
+ throw new ForbiddenException();
+ }
+
+ }
+
+
+
+ /**
+ * Is admin allowed to view all users? In Authz terms, does the admin have the "view" scope for the Users Authz resource?
+ *
+ * This method will follow the old default behavior (does the admin have the view-users role) if any of these conditions
+ * are met.:
+ * - The admin is from the master realm managing a different realm
+ * - If the Authz objects are not set up correctly for the Users resource in Authz
+ * - The "view" permission for the Users resource has an empty associatedPolicy list.
+ *
+ * Otherwise, it will use the Authz policy engine to resolve this answer.
+ *
+ * @return
+ */
+ @Override
+ public boolean canView() {
+ if (canViewDefault()) return true;
+ if (!root.isAdminSameRealm()) {
+ return false;
+ }
+
+ return hasViewPermission() || canManage();
+ }
+
+ private boolean hasViewPermission() {
+ ResourceServer server = root.realmResourceServer();
+ if (server == null) return canViewDefault();
+
+ Resource resource = authz.getStoreFactory().getResourceStore().findByName(USERS_RESOURCE, server.getId());
+ if (resource == null) return canViewDefault();
+
+ Policy policy = authz.getStoreFactory().getPolicyStore().findByName(VIEW_PERMISSION_USERS, server.getId());
+ if (policy == null) {
+ return canViewDefault();
+ }
+
+ Set<Policy> associatedPolicies = policy.getAssociatedPolicies();
+ // if no policies attached to permission then just do default behavior
+ if (associatedPolicies == null || associatedPolicies.isEmpty()) {
+ return canViewDefault();
+ }
+
+ Scope scope = root.realmViewScope();
+ return root.evaluatePermission(resource, scope, server);
+ }
+
+ /**
+ * Does current admin have view permissions for this particular user?
+ *
+ * Evaluates in this order. If any true, return true:
+ * - canViewUsers
+ * - canManageUsers
+ *
+ *
+ * @param user
+ * @return
+ */
+ @Override
+ public boolean canView(UserModel user) {
+ return canView() || canViewByGroup(user);
+ }
+
+ @Override
+ public void requireView(UserModel user) {
+ if (!canView(user)) {
+ throw new ForbiddenException();
+ }
+ }
+
+ @Override
+ public void requireView() {
+ if (!(canView())) {
+ throw new ForbiddenException();
+ }
+ }
+
+ @Override
+ public boolean canImpersonate(UserModel user) {
+ if (!canImpersonate()) {
+ return false;
+ }
+
+ Identity userIdentity = new UserModelIdentity(root.realm, user);
+ if (!root.isAdminSameRealm()) {
+ return true;
+ }
+
+ ResourceServer server = root.realmResourceServer();
+ if (server == null) return true;
+
+ Resource resource = authz.getStoreFactory().getResourceStore().findByName(USERS_RESOURCE, server.getId());
+ if (resource == null) return true;
+
+ Policy policy = authz.getStoreFactory().getPolicyStore().findByName(USER_IMPERSONATED_PERMISSION, server.getId());
+ if (policy == null) {
+ return true;
+ }
+
+ Set<Policy> associatedPolicies = policy.getAssociatedPolicies();
+ // if no policies attached to permission then just do default behavior
+ if (associatedPolicies == null || associatedPolicies.isEmpty()) {
+ return true;
+ }
+
+ Scope scope = root.realmScope(USER_IMPERSONATED_SCOPE);
+ return root.evaluatePermission(resource, scope, server, userIdentity);
+
+ }
+
+ @Override
+ public boolean canImpersonate() {
+ if (root.hasOneAdminRole(ImpersonationConstants.IMPERSONATION_ROLE)) return true;
+
+ if (!root.isAdminSameRealm()) {
+ return false;
+ }
+
+ ResourceServer server = root.realmResourceServer();
+ if (server == null) return false;
+
+ Resource resource = authz.getStoreFactory().getResourceStore().findByName(USERS_RESOURCE, server.getId());
+ if (resource == null) return false;
+
+ Policy policy = authz.getStoreFactory().getPolicyStore().findByName(ADMIN_IMPERSONATING_PERMISSION, server.getId());
+ if (policy == null) {
+ return false;
+ }
+
+ Set<Policy> associatedPolicies = policy.getAssociatedPolicies();
+ // if no policies attached to permission then just do default behavior
+ if (associatedPolicies == null || associatedPolicies.isEmpty()) {
+ return false;
+ }
+
+ Scope scope = root.realmScope(IMPERSONATE_SCOPE);
+ return root.evaluatePermission(resource, scope, server);
+ }
+
+ @Override
+ public void requireImpersonate(UserModel user) {
+ if (!canImpersonate(user)) {
+ throw new ForbiddenException();
+ }
+ }
+
+ @Override
+ public Map<String, Boolean> getAccess(UserModel user) {
+ Map<String, Boolean> map = new HashMap<>();
+ map.put("view", canView(user));
+ map.put("manage", canManage(user));
+ map.put("mapRoles", canMapRoles(user));
+ map.put("manageGroupMembership", canManageGroupMembership(user));
+ map.put("impersonate", canImpersonate(user));
+ return map;
+ }
+
+ @Override
+ public boolean canMapRoles(UserModel user) {
+ if (canManage(user)) return true;
+
+ if (!root.isAdminSameRealm()) {
+ return false;
+ }
+
+ ResourceServer server = root.realmResourceServer();
+ if (server == null) return false;
+
+ Resource resource = authz.getStoreFactory().getResourceStore().findByName(USERS_RESOURCE, server.getId());
+ if (resource == null) return false;
+
+ Policy policy = authz.getStoreFactory().getPolicyStore().findByName(MAP_ROLES_PERMISSION_USERS, server.getId());
+ if (policy == null) {
+ return false;
+ }
+
+ Set<Policy> associatedPolicies = policy.getAssociatedPolicies();
+ // if no policies attached to permission then just do default behavior
+ if (associatedPolicies == null || associatedPolicies.isEmpty()) {
+ return false;
+ }
+
+ Scope scope = root.realmScope(MAP_ROLES_SCOPE);
+ return root.evaluatePermission(resource, scope, server);
+
+ }
+
+ @Override
+ public void requireMapRoles(UserModel user) {
+ if (!canMapRoles(user)) {
+ throw new ForbiddenException();
+ }
+
+ }
+
+
+ @Override
+ public boolean canManageGroupMembership(UserModel user) {
+ if (canManage(user)) return true;
+
+ if (!root.isAdminSameRealm()) {
+ return false;
+ }
+
+ ResourceServer server = root.realmResourceServer();
+ if (server == null) return false;
+
+ Resource resource = authz.getStoreFactory().getResourceStore().findByName(USERS_RESOURCE, server.getId());
+ if (resource == null) return false;
+
+ Policy policy = authz.getStoreFactory().getPolicyStore().findByName(MANAGE_GROUP_MEMBERSHIP_PERMISSION_USERS, server.getId());
+ if (policy == null) {
+ return false;
+ }
+
+ Set<Policy> associatedPolicies = policy.getAssociatedPolicies();
+ // if no policies attached to permission then just do default behavior
+ if (associatedPolicies == null || associatedPolicies.isEmpty()) {
+ return false;
+ }
+
+ Scope scope = root.realmScope(MANAGE_GROUP_MEMBERSHIP_SCOPE);
+ return root.evaluatePermission(resource, scope, server);
+
+ }
+
+ @Override
+ public void requireManageGroupMembership(UserModel user) {
+ if (!canManageGroupMembership(user)) {
+ throw new ForbiddenException();
+ }
+
+ }
+
+
+
+
+
+
+}
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ProtocolMappersResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ProtocolMappersResource.java
index ad473b9..709197d 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/ProtocolMappersResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ProtocolMappersResource.java
@@ -33,7 +33,7 @@ import org.keycloak.protocol.ProtocolMapperConfigException;
import org.keycloak.representations.idm.ProtocolMapperRepresentation;
import org.keycloak.services.ErrorResponse;
import org.keycloak.services.ErrorResponseException;
-import org.keycloak.services.resources.admin.RealmAuth.Resource;
+import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
@@ -66,7 +66,9 @@ public class ProtocolMappersResource {
protected ProtocolMapperContainerModel client;
- protected RealmAuth auth;
+ protected AdminPermissionEvaluator auth;
+ protected AdminPermissionEvaluator.RequirePermissionCheck managePermission;
+ protected AdminPermissionEvaluator.RequirePermissionCheck viewPermission;
protected AdminEventBuilder adminEvent;
@@ -76,13 +78,17 @@ public class ProtocolMappersResource {
@Context
protected KeycloakSession session;
- public ProtocolMappersResource(RealmModel realm, ProtocolMapperContainerModel client, RealmAuth auth, AdminEventBuilder adminEvent) {
+ public ProtocolMappersResource(RealmModel realm, ProtocolMapperContainerModel client, AdminPermissionEvaluator auth,
+ AdminEventBuilder adminEvent,
+ AdminPermissionEvaluator.RequirePermissionCheck managePermission,
+ AdminPermissionEvaluator.RequirePermissionCheck viewPermission) {
this.realm = realm;
this.auth = auth;
this.client = client;
this.adminEvent = adminEvent.resource(ResourceType.PROTOCOL_MAPPER);
+ this.managePermission = managePermission;
+ this.viewPermission = viewPermission;
- auth.init(Resource.CLIENT);
}
/**
@@ -96,11 +102,7 @@ public class ProtocolMappersResource {
@Path("protocol/{protocol}")
@Produces(MediaType.APPLICATION_JSON)
public List<ProtocolMapperRepresentation> getMappersPerProtocol(@PathParam("protocol") String protocol) {
- auth.requireAny();
-
- if (client == null) {
- throw new NotFoundException("Could not find client");
- }
+ viewPermission.require();
List<ProtocolMapperRepresentation> mappers = new LinkedList<ProtocolMapperRepresentation>();
for (ProtocolMapperModel mapper : client.getProtocolMappers()) {
@@ -119,11 +121,7 @@ public class ProtocolMappersResource {
@NoCache
@Consumes(MediaType.APPLICATION_JSON)
public Response createMapper(ProtocolMapperRepresentation rep) {
- auth.requireManage();
-
- if (client == null) {
- throw new NotFoundException("Could not find client");
- }
+ managePermission.require();
ProtocolMapperModel model = null;
try {
@@ -147,11 +145,7 @@ public class ProtocolMappersResource {
@NoCache
@Consumes(MediaType.APPLICATION_JSON)
public void createMapper(List<ProtocolMapperRepresentation> reps) {
- auth.requireManage();
-
- if (client == null) {
- throw new NotFoundException("Could not find client");
- }
+ managePermission.require();
ProtocolMapperModel model = null;
for (ProtocolMapperRepresentation rep : reps) {
@@ -172,11 +166,7 @@ public class ProtocolMappersResource {
@Path("models")
@Produces(MediaType.APPLICATION_JSON)
public List<ProtocolMapperRepresentation> getMappers() {
- auth.requireAny();
-
- if (client == null) {
- throw new NotFoundException("Could not find client");
- }
+ viewPermission.require();
List<ProtocolMapperRepresentation> mappers = new LinkedList<ProtocolMapperRepresentation>();
for (ProtocolMapperModel mapper : client.getProtocolMappers()) {
@@ -196,11 +186,7 @@ public class ProtocolMappersResource {
@Path("models/{id}")
@Produces(MediaType.APPLICATION_JSON)
public ProtocolMapperRepresentation getMapperById(@PathParam("id") String id) {
- auth.requireAny();
-
- if (client == null) {
- throw new NotFoundException("Could not find client");
- }
+ viewPermission.require();
ProtocolMapperModel model = client.getProtocolMapperById(id);
if (model == null) throw new NotFoundException("Model not found");
@@ -218,11 +204,7 @@ public class ProtocolMappersResource {
@Path("models/{id}")
@Consumes(MediaType.APPLICATION_JSON)
public void update(@PathParam("id") String id, ProtocolMapperRepresentation rep) {
- auth.requireManage();
-
- if (client == null) {
- throw new NotFoundException("Could not find client");
- }
+ managePermission.require();
ProtocolMapperModel model = client.getProtocolMapperById(id);
if (model == null) throw new NotFoundException("Model not found");
@@ -243,11 +225,7 @@ public class ProtocolMappersResource {
@NoCache
@Path("models/{id}")
public void delete(@PathParam("id") String id) {
- auth.requireManage();
-
- if (client == null) {
- throw new NotFoundException("Could not find client");
- }
+ managePermission.require();
ProtocolMapperModel model = client.getProtocolMapperById(id);
if (model == null) throw new NotFoundException("Model not found");
@@ -261,10 +239,12 @@ public class ProtocolMappersResource {
ProtocolMapper mapper = (ProtocolMapper)session.getKeycloakSessionFactory().getProviderFactory(ProtocolMapper.class, model.getProtocolMapper());
if (mapper != null) {
mapper.validateConfig(session, realm, client, model);
+ } else {
+ throw new NotFoundException("ProtocolMapper provider not found");
}
} catch (ProtocolMapperConfigException ex) {
logger.error(ex.getMessage());
- Properties messages = AdminRoot.getMessages(session, realm, auth.getAuth().getToken().getLocale());
+ Properties messages = AdminRoot.getMessages(session, realm, auth.adminAuth().getToken().getLocale());
throw new ErrorResponseException(ex.getMessage(), MessageFormat.format(messages.getProperty(ex.getMessageKey(), ex.getMessage()), ex.getParameters()),
Response.Status.BAD_REQUEST);
}
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
index f1469d0..28392f7 100644
--- a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
@@ -23,6 +23,9 @@ import org.jboss.resteasy.spi.NotFoundException;
import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.keycloak.Config;
import org.keycloak.KeyPairVerifier;
+import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
+import org.keycloak.services.resources.admin.permissions.AdminPermissionManagement;
+import org.keycloak.services.resources.admin.permissions.AdminPermissions;
import org.keycloak.common.ClientConnection;
import org.keycloak.common.VerificationException;
import org.keycloak.common.util.PemUtils;
@@ -63,6 +66,7 @@ import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.ComponentRepresentation;
import org.keycloak.representations.idm.EventRepresentation;
import org.keycloak.representations.idm.GroupRepresentation;
+import org.keycloak.representations.idm.ManagementPermissionReference;
import org.keycloak.representations.idm.PartialImportRepresentation;
import org.keycloak.representations.idm.RealmEventsConfigRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
@@ -72,7 +76,6 @@ import org.keycloak.services.managers.LDAPConnectionTestManager;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.services.managers.ResourceAdminManager;
import org.keycloak.services.managers.UserStorageSyncManager;
-import org.keycloak.services.resources.admin.RealmAuth.Resource;
import org.keycloak.storage.UserStorageProviderModel;
import javax.ws.rs.Consumes;
@@ -112,7 +115,7 @@ import static org.keycloak.models.utils.StripSecretsUtils.stripForExport;
*/
public class RealmAdminResource {
protected static final Logger logger = Logger.getLogger(RealmAdminResource.class);
- protected RealmAuth auth;
+ protected AdminPermissionEvaluator auth;
protected RealmModel realm;
private TokenManager tokenManager;
private AdminEventBuilder adminEvent;
@@ -129,14 +132,11 @@ public class RealmAdminResource {
@Context
protected HttpHeaders headers;
- public RealmAdminResource(RealmAuth auth, RealmModel realm, TokenManager tokenManager, AdminEventBuilder adminEvent) {
+ public RealmAdminResource(AdminPermissionEvaluator auth, RealmModel realm, TokenManager tokenManager, AdminEventBuilder adminEvent) {
this.auth = auth;
this.realm = realm;
this.tokenManager = tokenManager;
this.adminEvent = adminEvent.realm(realm).resource(ResourceType.REALM);
-
- auth.init(RealmAuth.Resource.REALM);
- auth.requireAny();
}
/**
@@ -149,7 +149,7 @@ public class RealmAdminResource {
@POST
@Produces(MediaType.APPLICATION_JSON)
public ClientRepresentation convertClientDescription(String description) {
- auth.init(Resource.CLIENT).requireManage();
+ auth.clients().requireManage();
if (realm == null) {
throw new NotFoundException("Realm not found.");
@@ -238,7 +238,7 @@ public class RealmAdminResource {
*/
@Path("roles")
public RoleContainerResource getRoleContainerResource() {
- return new RoleContainerResource(uriInfo, realm, auth, realm, adminEvent);
+ return new RoleContainerResource(session, uriInfo, realm, auth, realm, adminEvent);
}
/**
@@ -252,15 +252,15 @@ public class RealmAdminResource {
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public RealmRepresentation getRealm() {
- if (auth.hasView()) {
+ if (auth.realm().canViewRealm()) {
return ModelToRepresentation.toRepresentation(realm, false);
} else {
- auth.requireAny();
+ auth.realm().requireViewRealmNameList();
RealmRepresentation rep = new RealmRepresentation();
rep.setRealm(realm.getName());
- if (auth.init(Resource.IDENTITY_PROVIDER).hasView()) {
+ if (auth.realm().canViewIdentityProviders()) {
RealmRepresentation r = ModelToRepresentation.toRepresentation(realm, false);
rep.setIdentityProviders(r.getIdentityProviders());
rep.setIdentityProviderMappers(r.getIdentityProviderMappers());
@@ -282,7 +282,7 @@ public class RealmAdminResource {
@PUT
@Consumes(MediaType.APPLICATION_JSON)
public Response updateRealm(final RealmRepresentation rep) {
- auth.requireManage();
+ auth.realm().requireManageRealm();
logger.debug("updating realm: " + realm.getName());
@@ -344,7 +344,7 @@ public class RealmAdminResource {
*/
@DELETE
public void deleteRealm() {
- auth.requireManage();
+ auth.realm().requireManageRealm();
if (!new RealmManager(session).removeRealm(realm)) {
throw new NotFoundException("Realm doesn't exist");
@@ -364,6 +364,50 @@ public class RealmAdminResource {
return users;
}
+ @NoCache
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ @Path("users-management-permissions")
+ public ManagementPermissionReference getUserMgmtPermissions() {
+ auth.realm().requireViewRealm();
+
+ AdminPermissionManagement permissions = AdminPermissions.management(session, realm);
+ if (permissions.users().isPermissionsEnabled()) {
+ return toUsersMgmtRef(permissions);
+ } else {
+ return new ManagementPermissionReference();
+ }
+
+ }
+
+ @PUT
+ @Produces(MediaType.APPLICATION_JSON)
+ @Consumes(MediaType.APPLICATION_JSON)
+ @NoCache
+ @Path("users-management-permissions")
+ public ManagementPermissionReference setUsersManagementPermissionsEnabled(ManagementPermissionReference ref) {
+ auth.realm().requireManageRealm();
+
+ AdminPermissionManagement permissions = AdminPermissions.management(session, realm);
+ permissions.users().setPermissionsEnabled(ref.isEnabled());
+ if (ref.isEnabled()) {
+ return toUsersMgmtRef(permissions);
+ } else {
+ return new ManagementPermissionReference();
+ }
+ }
+
+
+ public static ManagementPermissionReference toUsersMgmtRef(AdminPermissionManagement permissions) {
+ ManagementPermissionReference ref = new ManagementPermissionReference();
+ ref.setEnabled(true);
+ ref.setResource(permissions.users().resource().getId());
+ Map<String, String> scopes = permissions.users().getPermissions();
+ ref.setScopePermissions(scopes);
+ return ref;
+ }
+
+
@Path("user-storage")
public UserStorageProviderResource userStorage() {
UserStorageProviderResource fed = new UserStorageProviderResource(realm, auth, adminEvent);
@@ -388,7 +432,7 @@ public class RealmAdminResource {
*/
@Path("roles-by-id")
public RoleByIdResource rolesById() {
- RoleByIdResource resource = new RoleByIdResource(realm, auth, adminEvent);
+ RoleByIdResource resource = new RoleByIdResource(realm, auth, adminEvent);
ResteasyProviderFactory.getInstance().injectProperties(resource);
//resourceContext.initResource(resource);
return resource;
@@ -401,7 +445,7 @@ public class RealmAdminResource {
@Path("push-revocation")
@POST
public GlobalRequestResult pushRevocation() {
- auth.requireManage();
+ auth.realm().requireManageRealm();
GlobalRequestResult result = new ResourceAdminManager(session).pushRealmRevocationPolicy(uriInfo.getRequestUri(), realm);
adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo).representation(result).success();
@@ -416,7 +460,7 @@ public class RealmAdminResource {
@Path("logout-all")
@POST
public GlobalRequestResult logoutAll() {
- auth.init(RealmAuth.Resource.USER).requireManage();
+ auth.users().requireManage();
session.sessions().removeUserSessions(realm);
GlobalRequestResult result = new ResourceAdminManager(session).logoutAll(uriInfo.getRequestUri(), realm);
@@ -433,7 +477,7 @@ public class RealmAdminResource {
@Path("sessions/{session}")
@DELETE
public void deleteSession(@PathParam("session") String sessionId) {
- auth.init(RealmAuth.Resource.USER).requireManage();
+ auth.users().requireManage();
UserSessionModel userSession = session.sessions().getUserSession(realm, sessionId);
if (userSession == null) throw new NotFoundException("Sesssion not found");
@@ -455,7 +499,7 @@ public class RealmAdminResource {
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public List<Map<String, String>> getClientSessionStats() {
- auth.requireView();
+ auth.realm().requireViewRealm();
List<Map<String, String>> data = new LinkedList<Map<String, String>>();
for (ClientModel client : realm.getClients()) {
@@ -482,7 +526,7 @@ public class RealmAdminResource {
@Path("events/config")
@Produces(MediaType.APPLICATION_JSON)
public RealmEventsConfigRepresentation getRealmEventsConfig() {
- auth.init(RealmAuth.Resource.EVENTS).requireView();
+ auth.realm().requireViewEvents();
RealmEventsConfigRepresentation config = ModelToRepresentation.toEventsConfigReprensetation(realm);
if (config.getEnabledEventTypes() == null || config.getEnabledEventTypes().isEmpty()) {
@@ -507,7 +551,7 @@ public class RealmAdminResource {
@Path("events/config")
@Consumes(MediaType.APPLICATION_JSON)
public void updateRealmEventsConfig(final RealmEventsConfigRepresentation rep) {
- auth.init(RealmAuth.Resource.EVENTS).requireManage();
+ auth.realm().requireManageEvents();
logger.debug("updating realm events config: " + realm.getName());
new RealmManager(session).updateRealmEventsConfig(rep, realm);
@@ -536,7 +580,7 @@ public class RealmAdminResource {
@QueryParam("user") String user, @QueryParam("dateFrom") String dateFrom, @QueryParam("dateTo") String dateTo,
@QueryParam("ipAddress") String ipAddress, @QueryParam("first") Integer firstResult,
@QueryParam("max") Integer maxResults) {
- auth.init(RealmAuth.Resource.EVENTS).requireView();
+ auth.realm().requireViewEvents();
EventStoreProvider eventStore = session.getProvider(EventStoreProvider.class);
@@ -629,7 +673,7 @@ public class RealmAdminResource {
@QueryParam("dateTo") String dateTo, @QueryParam("first") Integer firstResult,
@QueryParam("max") Integer maxResults,
@QueryParam("resourceTypes") List<String> resourceTypes) {
- auth.init(RealmAuth.Resource.EVENTS).requireView();
+ auth.realm().requireViewEvents();
EventStoreProvider eventStore = session.getProvider(EventStoreProvider.class);
AdminEventQuery query = eventStore.createAdminQuery().realm(realm.getId());;
@@ -722,7 +766,7 @@ public class RealmAdminResource {
@Path("events")
@DELETE
public void clearEvents() {
- auth.init(RealmAuth.Resource.EVENTS).requireManage();
+ auth.realm().requireManageEvents();
EventStoreProvider eventStore = session.getProvider(EventStoreProvider.class);
eventStore.clear(realm.getId());
@@ -735,7 +779,7 @@ public class RealmAdminResource {
@Path("admin-events")
@DELETE
public void clearAdminEvents() {
- auth.init(RealmAuth.Resource.EVENTS).requireManage();
+ auth.realm().requireManageEvents();
EventStoreProvider eventStore = session.getProvider(EventStoreProvider.class);
eventStore.clearAdmin(realm.getId());
@@ -757,7 +801,7 @@ public class RealmAdminResource {
@QueryParam("bindDn") String bindDn, @QueryParam("bindCredential") String bindCredential,
@QueryParam("useTruststoreSpi") String useTruststoreSpi, @QueryParam("connectionTimeout") String connectionTimeout,
@QueryParam("componentId") String componentId) {
- auth.init(RealmAuth.Resource.REALM).requireManage();
+ auth.realm().requireManageRealm();
if (componentId != null && bindCredential.equals(ComponentRepresentation.SECRET_VALUE)) {
bindCredential = realm.getComponent(componentId).getConfig().getFirst(LDAPConstants.BIND_CREDENTIAL);
@@ -782,7 +826,7 @@ public class RealmAdminResource {
@Produces(MediaType.APPLICATION_JSON)
@Path("default-groups")
public List<GroupRepresentation> getDefaultGroups() {
- auth.requireView();
+ auth.realm().requireViewRealm();
List<GroupRepresentation> defaults = new LinkedList<>();
for (GroupModel group : realm.getDefaultGroups()) {
@@ -794,7 +838,7 @@ public class RealmAdminResource {
@NoCache
@Path("default-groups/{groupId}")
public void addDefaultGroup(@PathParam("groupId") String groupId) {
- auth.requireManage();
+ auth.realm().requireManageRealm();
GroupModel group = realm.getGroupById(groupId);
if (group == null) {
@@ -809,7 +853,7 @@ public class RealmAdminResource {
@NoCache
@Path("default-groups/{groupId}")
public void removeDefaultGroup(@PathParam("groupId") String groupId) {
- auth.requireManage();
+ auth.realm().requireManageRealm();
GroupModel group = realm.getGroupById(groupId);
if (group == null) {
@@ -834,13 +878,12 @@ public class RealmAdminResource {
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public GroupRepresentation getGroupByPath(@PathParam("path") String path) {
- auth.requireView();
-
GroupModel found = KeycloakModelUtils.findGroupByPath(realm, path);
if (found == null) {
throw new NotFoundException("Group path does not exist");
}
+ auth.groups().requireView(found);
return ModelToRepresentation.toGroupHierarchy(found, true);
}
@@ -854,7 +897,7 @@ public class RealmAdminResource {
@POST
@Consumes(MediaType.APPLICATION_JSON)
public Response partialImport(PartialImportRepresentation rep) {
- auth.requireManage();
+ auth.realm().requireManageRealm();
PartialImportManager partialImport = new PartialImportManager(rep, session, realm, adminEvent);
return partialImport.saveResources();
@@ -888,7 +931,7 @@ public class RealmAdminResource {
@Path("clear-realm-cache")
@POST
public void clearRealmCache() {
- auth.requireManage();
+ auth.realm().requireManageRealm();
CacheRealmProvider cache = session.getProvider(CacheRealmProvider.class);
if (cache != null) {
@@ -905,7 +948,7 @@ public class RealmAdminResource {
@Path("clear-user-cache")
@POST
public void clearUserCache() {
- auth.requireManage();
+ auth.realm().requireManageRealm();
UserCache cache = session.getProvider(UserCache.class);
if (cache != null) {
@@ -922,7 +965,7 @@ public class RealmAdminResource {
@Path("clear-keys-cache")
@POST
public void clearKeysCache() {
- auth.requireManage();
+ auth.realm().requireManageRealm();
PublicKeyStorageProvider cache = session.getProvider(PublicKeyStorageProvider.class);
if (cache != null) {
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RealmsAdminResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RealmsAdminResource.java
index 7a948d9..b00f2e4 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/RealmsAdminResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/RealmsAdminResource.java
@@ -34,6 +34,8 @@ import org.keycloak.services.ErrorResponse;
import org.keycloak.services.ForbiddenException;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.services.resources.KeycloakApplication;
+import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
+import org.keycloak.services.resources.admin.permissions.AdminPermissions;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
@@ -94,18 +96,11 @@ public class RealmsAdminResource {
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public List<RealmRepresentation> getRealms() {
- RealmManager realmManager = new RealmManager(session);
List<RealmRepresentation> reps = new ArrayList<RealmRepresentation>();
- if (auth.getRealm().equals(realmManager.getKeycloakAdminstrationRealm())) {
- List<RealmModel> realms = session.realms().getRealms();
- for (RealmModel realm : realms) {
- addRealmRep(reps, realm, realm.getMasterAdminClient());
- }
- } else {
- ClientModel adminApp = auth.getRealm().getClientByClientId(realmManager.getRealmAdminClientId(auth.getRealm()));
- addRealmRep(reps, auth.getRealm(), adminApp);
+ List<RealmModel> realms = session.realms().getRealms();
+ for (RealmModel realm : realms) {
+ addRealmRep(reps, realm);
}
-
if (reps.isEmpty()) {
throw new ForbiddenException();
}
@@ -114,10 +109,10 @@ public class RealmsAdminResource {
return reps;
}
- protected void addRealmRep(List<RealmRepresentation> reps, RealmModel realm, ClientModel realmManagementClient) {
- if (auth.hasAppRole(realmManagementClient, AdminRoles.VIEW_REALM)) {
+ protected void addRealmRep(List<RealmRepresentation> reps, RealmModel realm) {
+ if (AdminPermissions.realms(session, auth).canView(realm)) {
reps.add(ModelToRepresentation.toRepresentation(realm, false));
- } else if (auth.hasOneOfAppRole(realmManagementClient, AdminRoles.ALL_REALM_ROLES)) {
+ } else if (AdminPermissions.realms(session, auth).isAdmin(realm)) {
RealmRepresentation rep = new RealmRepresentation();
rep.setRealm(realm.getName());
reps.add(rep);
@@ -138,12 +133,7 @@ public class RealmsAdminResource {
public Response importRealm(@Context final UriInfo uriInfo, final RealmRepresentation rep) {
RealmManager realmManager = new RealmManager(session);
realmManager.setContextPath(keycloak.getContextPath());
- if (!auth.getRealm().equals(realmManager.getKeycloakAdminstrationRealm())) {
- throw new ForbiddenException();
- }
- if (!auth.hasRealmRole(AdminRoles.CREATE_REALM)) {
- throw new ForbiddenException();
- }
+ AdminPermissions.realms(session, auth).requireCreateRealm();
logger.debugv("importRealm: {0}", rep.getRealm());
@@ -191,13 +181,7 @@ public class RealmsAdminResource {
&& !auth.getRealm().equals(realm)) {
throw new ForbiddenException();
}
- RealmAuth realmAuth;
-
- if (auth.getRealm().equals(realmManager.getKeycloakAdminstrationRealm())) {
- realmAuth = new RealmAuth(auth, realm.getMasterAdminClient());
- } else {
- realmAuth = new RealmAuth(auth, realm.getClientByClientId(realmManager.getRealmAdminClientId(auth.getRealm())));
- }
+ AdminPermissionEvaluator realmAuth = AdminPermissions.evaluator(session, realm, auth);
AdminEventBuilder adminEvent = new AdminEventBuilder(realm, auth, session, clientConnection);
session.getContext().setRealm(realm);
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RoleByIdResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RoleByIdResource.java
index 1f19b64..b2ae6ad 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/RoleByIdResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/RoleByIdResource.java
@@ -19,6 +19,10 @@ package org.keycloak.services.resources.admin;
import org.jboss.logging.Logger;
import org.jboss.resteasy.annotations.cache.NoCache;
import org.jboss.resteasy.spi.NotFoundException;
+import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
+import org.keycloak.services.resources.admin.permissions.AdminPermissionManagement;
+import org.keycloak.services.resources.admin.permissions.AdminPermissions;
+import org.keycloak.services.resources.admin.permissions.RolePermissionManagement;
import org.keycloak.events.admin.OperationType;
import org.keycloak.events.admin.ResourceType;
import org.keycloak.models.ClientModel;
@@ -26,6 +30,7 @@ import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserModel;
+import org.keycloak.representations.idm.ManagementPermissionReference;
import org.keycloak.representations.idm.RoleRepresentation;
import javax.ws.rs.Consumes;
@@ -39,7 +44,9 @@ import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.UriInfo;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.Set;
/**
@@ -52,7 +59,7 @@ import java.util.Set;
public class RoleByIdResource extends RoleResource {
protected static final Logger logger = Logger.getLogger(RoleByIdResource.class);
private final RealmModel realm;
- private final RealmAuth auth;
+ private AdminPermissionEvaluator auth;
private AdminEventBuilder adminEvent;
@Context
@@ -61,7 +68,7 @@ public class RoleByIdResource extends RoleResource {
@Context
private UriInfo uriInfo;
- public RoleByIdResource(RealmModel realm, RealmAuth auth, AdminEventBuilder adminEvent) {
+ public RoleByIdResource(RealmModel realm, AdminPermissionEvaluator auth, AdminEventBuilder adminEvent) {
super(realm);
this.realm = realm;
@@ -80,9 +87,9 @@ public class RoleByIdResource extends RoleResource {
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public RoleRepresentation getRole(final @PathParam("role-id") String id) {
- auth.requireAny();
RoleModel roleModel = getRoleModel(id);
+ auth.roles().requireView(roleModel);
return getRole(roleModel);
}
@@ -91,17 +98,7 @@ public class RoleByIdResource extends RoleResource {
if (roleModel == null) {
throw new NotFoundException("Could not find role with id");
}
-
- RealmAuth.Resource r = null;
- if (roleModel.getContainer() instanceof RealmModel) {
- r = RealmAuth.Resource.REALM;
- } else if (roleModel.getContainer() instanceof ClientModel) {
- r = RealmAuth.Resource.CLIENT;
- } else if (roleModel.getContainer() instanceof UserModel) {
- r = RealmAuth.Resource.USER;
- }
- auth.init(r);
- return roleModel;
+ return roleModel;
}
/**
@@ -113,9 +110,8 @@ public class RoleByIdResource extends RoleResource {
@DELETE
@NoCache
public void deleteRole(final @PathParam("role-id") String id) {
- auth.requireManage();
-
RoleModel role = getRoleModel(id);
+ auth.roles().requireManage(role);
deleteRole(role);
if (role.isClientRole()) {
@@ -137,9 +133,8 @@ public class RoleByIdResource extends RoleResource {
@PUT
@Consumes(MediaType.APPLICATION_JSON)
public void updateRole(final @PathParam("role-id") String id, final RoleRepresentation rep) {
- auth.requireManage();
-
RoleModel role = getRoleModel(id);
+ auth.roles().requireManage(role);
updateRole(rep, role);
if (role.isClientRole()) {
@@ -161,10 +156,9 @@ public class RoleByIdResource extends RoleResource {
@POST
@Consumes(MediaType.APPLICATION_JSON)
public void addComposites(final @PathParam("role-id") String id, List<RoleRepresentation> roles) {
- auth.requireManage();
-
RoleModel role = getRoleModel(id);
- addComposites(adminEvent, uriInfo, roles, role);
+ auth.roles().requireManage(role);
+ addComposites(auth, adminEvent, uriInfo, roles, role);
}
/**
@@ -180,11 +174,10 @@ public class RoleByIdResource extends RoleResource {
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public Set<RoleRepresentation> getRoleComposites(final @PathParam("role-id") String id) {
- auth.requireAny();
if (logger.isDebugEnabled()) logger.debug("*** getRoleComposites: '" + id + "'");
RoleModel role = getRoleModel(id);
- auth.requireView();
+ auth.roles().requireView(role);
return getRoleComposites(role);
}
@@ -199,9 +192,9 @@ public class RoleByIdResource extends RoleResource {
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public Set<RoleRepresentation> getRealmRoleComposites(final @PathParam("role-id") String id) {
- auth.requireAny();
-
RoleModel role = getRoleModel(id);
+ auth.roles().requireView(role);
+ auth.roles().requireView(role);
return getRealmRoleComposites(role);
}
@@ -218,9 +211,9 @@ public class RoleByIdResource extends RoleResource {
@Produces(MediaType.APPLICATION_JSON)
public Set<RoleRepresentation> getClientRoleComposites(final @PathParam("role-id") String id,
final @PathParam("client") String client) {
- auth.requireAny();
RoleModel role = getRoleModel(id);
+ auth.roles().requireView(role);
ClientModel clientModel = realm.getClientById(client);
if (clientModel == null) {
throw new NotFoundException("Could not find client");
@@ -238,10 +231,64 @@ public class RoleByIdResource extends RoleResource {
@DELETE
@Consumes(MediaType.APPLICATION_JSON)
public void deleteComposites(final @PathParam("role-id") String id, List<RoleRepresentation> roles) {
- auth.requireManage();
-
RoleModel role = getRoleModel(id);
+ auth.roles().requireManage(role);
deleteComposites(adminEvent, uriInfo, roles, role);
}
+ /**
+ * Return object stating whether role Authoirzation permissions have been initialized or not and a reference
+ *
+ *
+ * @param id
+ * @return
+ */
+ @Path("{role-id}/management/permissions")
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ @NoCache
+ public ManagementPermissionReference getManagementPermissions(final @PathParam("role-id") String id) {
+ RoleModel role = getRoleModel(id);
+ auth.roles().requireView(role);
+
+ AdminPermissionManagement permissions = AdminPermissions.management(session, realm);
+ if (!permissions.roles().isPermissionsEnabled(role)) {
+ return new ManagementPermissionReference();
+ }
+ return toMgmtRef(role, permissions);
+ }
+
+ public static ManagementPermissionReference toMgmtRef(RoleModel role, AdminPermissionManagement permissions) {
+ ManagementPermissionReference ref = new ManagementPermissionReference();
+ ref.setEnabled(true);
+ ref.setResource(permissions.roles().resource(role).getId());
+ ref.setScopePermissions(permissions.roles().getPermissions(role));
+ return ref;
+ }
+
+ /**
+ * Return object stating whether role Authoirzation permissions have been initialized or not and a reference
+ *
+ *
+ * @param id
+ * @return initialized manage permissions reference
+ */
+ @Path("{role-id}/management/permissions")
+ @PUT
+ @Produces(MediaType.APPLICATION_JSON)
+ @Consumes(MediaType.APPLICATION_JSON)
+ @NoCache
+ public ManagementPermissionReference setManagementPermissionsEnabled(final @PathParam("role-id") String id, ManagementPermissionReference ref) {
+ RoleModel role = getRoleModel(id);
+ auth.roles().requireManage(role);
+
+ AdminPermissionManagement permissions = AdminPermissions.management(session, realm);
+ permissions.roles().setPermissionsEnabled(role, ref.isEnabled());
+ if (ref.isEnabled()) {
+ return toMgmtRef(role, permissions);
+ } else {
+ return new ManagementPermissionReference();
+ }
+ }
+
}
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RoleContainerResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RoleContainerResource.java
index 47f9c64..79bb6c8 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/RoleContainerResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/RoleContainerResource.java
@@ -19,26 +19,33 @@ package org.keycloak.services.resources.admin;
import org.jboss.resteasy.annotations.cache.NoCache;
import org.jboss.resteasy.spi.NotFoundException;
+import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
+import org.keycloak.services.resources.admin.permissions.AdminPermissionManagement;
+import org.keycloak.services.resources.admin.permissions.AdminPermissions;
import org.keycloak.events.admin.OperationType;
import org.keycloak.events.admin.ResourceType;
import org.keycloak.models.ClientModel;
+import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ModelDuplicateException;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleContainerModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.utils.ModelToRepresentation;
+import org.keycloak.representations.idm.ManagementPermissionReference;
import org.keycloak.representations.idm.RoleRepresentation;
import org.keycloak.services.ErrorResponse;
import javax.ws.rs.BadRequestException;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
+import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
@@ -54,18 +61,22 @@ import java.util.Set;
*/
public class RoleContainerResource extends RoleResource {
private final RealmModel realm;
- private final RealmAuth auth;
+ protected AdminPermissionEvaluator auth;
+
protected RoleContainerModel roleContainer;
private AdminEventBuilder adminEvent;
private UriInfo uriInfo;
+ private KeycloakSession session;
- public RoleContainerResource(UriInfo uriInfo, RealmModel realm, RealmAuth auth, RoleContainerModel roleContainer, AdminEventBuilder adminEvent) {
+ public RoleContainerResource(KeycloakSession session, UriInfo uriInfo, RealmModel realm,
+ AdminPermissionEvaluator auth, RoleContainerModel roleContainer, AdminEventBuilder adminEvent) {
super(realm);
this.uriInfo = uriInfo;
this.realm = realm;
this.auth = auth;
this.roleContainer = roleContainer;
this.adminEvent = adminEvent;
+ this.session = session;
}
/**
@@ -77,11 +88,7 @@ public class RoleContainerResource extends RoleResource {
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public List<RoleRepresentation> getRoles() {
- auth.requireAny();
-
- if (roleContainer == null) {
- throw new NotFoundException("Could not find client");
- }
+ auth.roles().requireList(roleContainer);
Set<RoleModel> roleModels = roleContainer.getRoles();
List<RoleRepresentation> roles = new ArrayList<RoleRepresentation>();
@@ -100,11 +107,7 @@ public class RoleContainerResource extends RoleResource {
@POST
@Consumes(MediaType.APPLICATION_JSON)
public Response createRole(final RoleRepresentation rep) {
- auth.requireManage();
-
- if (roleContainer == null) {
- throw new NotFoundException("Could not find client");
- }
+ auth.roles().requireManage(roleContainer);
if (rep.getName() == null) {
throw new BadRequestException();
@@ -143,11 +146,7 @@ public class RoleContainerResource extends RoleResource {
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public RoleRepresentation getRole(final @PathParam("role-name") String roleName) {
- auth.requireView();
-
- if (roleContainer == null) {
- throw new NotFoundException("Could not find client");
- }
+ auth.roles().requireView(roleContainer);
RoleModel roleModel = roleContainer.getRole(roleName);
if (roleModel == null) {
@@ -166,12 +165,7 @@ public class RoleContainerResource extends RoleResource {
@DELETE
@NoCache
public void deleteRole(final @PathParam("role-name") String roleName) {
- auth.requireManage();
-
- if (roleContainer == null) {
- throw new NotFoundException("Could not find client");
- }
-
+ auth.roles().requireManage(roleContainer);
RoleModel role = roleContainer.getRole(roleName);
if (role == null) {
throw new NotFoundException("Could not find role");
@@ -199,12 +193,7 @@ public class RoleContainerResource extends RoleResource {
@PUT
@Consumes(MediaType.APPLICATION_JSON)
public Response updateRole(final @PathParam("role-name") String roleName, final RoleRepresentation rep) {
- auth.requireManage();
-
- if (roleContainer == null) {
- throw new NotFoundException("Could not find client");
- }
-
+ auth.roles().requireManage(roleContainer);
RoleModel role = roleContainer.getRole(roleName);
if (role == null) {
throw new NotFoundException("Could not find role");
@@ -236,17 +225,12 @@ public class RoleContainerResource extends RoleResource {
@POST
@Consumes(MediaType.APPLICATION_JSON)
public void addComposites(final @PathParam("role-name") String roleName, List<RoleRepresentation> roles) {
- auth.requireManage();
-
- if (roleContainer == null) {
- throw new NotFoundException("Could not find client");
- }
-
+ auth.roles().requireManage(roleContainer);
RoleModel role = roleContainer.getRole(roleName);
if (role == null) {
throw new NotFoundException("Could not find role");
}
- addComposites(adminEvent, uriInfo, roles, role);
+ addComposites(auth, adminEvent, uriInfo, roles, role);
}
/**
@@ -260,12 +244,7 @@ public class RoleContainerResource extends RoleResource {
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public Set<RoleRepresentation> getRoleComposites(final @PathParam("role-name") String roleName) {
- auth.requireView();
-
- if (roleContainer == null) {
- throw new NotFoundException("Could not find client");
- }
-
+ auth.roles().requireView(roleContainer);
RoleModel role = roleContainer.getRole(roleName);
if (role == null) {
throw new NotFoundException("Could not find role");
@@ -284,12 +263,7 @@ public class RoleContainerResource extends RoleResource {
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public Set<RoleRepresentation> getRealmRoleComposites(final @PathParam("role-name") String roleName) {
- auth.requireView();
-
- if (roleContainer == null) {
- throw new NotFoundException("Could not find client");
- }
-
+ auth.roles().requireView(roleContainer);
RoleModel role = roleContainer.getRole(roleName);
if (role == null) {
throw new NotFoundException("Could not find role");
@@ -311,12 +285,7 @@ public class RoleContainerResource extends RoleResource {
public Set<RoleRepresentation> getClientRoleComposites(@Context final UriInfo uriInfo,
final @PathParam("role-name") String roleName,
final @PathParam("client") String client) {
- auth.requireView();
-
- if (roleContainer == null) {
- throw new NotFoundException("Could not find client");
- }
-
+ auth.roles().requireView(roleContainer);
RoleModel role = roleContainer.getRole(roleName);
if (role == null) {
throw new NotFoundException("Could not find role");
@@ -342,17 +311,66 @@ public class RoleContainerResource extends RoleResource {
public void deleteComposites(
final @PathParam("role-name") String roleName,
List<RoleRepresentation> roles) {
- auth.requireManage();
- if (roleContainer == null) {
- throw new NotFoundException("Could not find client");
+ auth.roles().requireManage(roleContainer);
+ RoleModel role = roleContainer.getRole(roleName);
+ if (role == null) {
+ throw new NotFoundException("Could not find role");
}
+ deleteComposites(adminEvent, uriInfo, roles, role);
+ }
+ /**
+ * Return object stating whether role Authoirzation permissions have been initialized or not and a reference
+ *
+ *
+ * @param roleName
+ * @return
+ */
+ @Path("{role-name}/management/permissions")
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ @NoCache
+ public ManagementPermissionReference getManagementPermissions(final @PathParam("role-name") String roleName) {
+ auth.roles().requireView(roleContainer);
RoleModel role = roleContainer.getRole(roleName);
if (role == null) {
throw new NotFoundException("Could not find role");
}
- deleteComposites(adminEvent, uriInfo, roles, role);
+
+ AdminPermissionManagement permissions = AdminPermissions.management(session, realm);
+ if (!permissions.roles().isPermissionsEnabled(role)) {
+ return new ManagementPermissionReference();
+ }
+ return RoleByIdResource.toMgmtRef(role, permissions);
+ }
+
+ /**
+ * Return object stating whether role Authoirzation permissions have been initialized or not and a reference
+ *
+ *
+ * @param roleName
+ * @return initialized manage permissions reference
+ */
+ @Path("{role-name}/management/permissions")
+ @PUT
+ @Produces(MediaType.APPLICATION_JSON)
+ @Consumes(MediaType.APPLICATION_JSON)
+ @NoCache
+ public ManagementPermissionReference setManagementPermissionsEnabled(final @PathParam("role-name") String roleName, ManagementPermissionReference ref) {
+ auth.roles().requireManage(roleContainer);
+ RoleModel role = roleContainer.getRole(roleName);
+ if (role == null) {
+ throw new NotFoundException("Could not find role");
+ }
+
+ if (ref.isEnabled()) {
+ AdminPermissionManagement permissions = AdminPermissions.management(session, realm);
+ permissions.roles().setPermissionsEnabled(role, ref.isEnabled());
+ return RoleByIdResource.toMgmtRef(role, permissions);
+ } else {
+ return new ManagementPermissionReference();
+ }
}
}
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RoleMapperResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RoleMapperResource.java
index 5b7af1f..b785b1a 100644
--- a/services/src/main/java/org/keycloak/services/resources/admin/RoleMapperResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/RoleMapperResource.java
@@ -19,6 +19,7 @@ package org.keycloak.services.resources.admin;
import org.jboss.logging.Logger;
import org.jboss.resteasy.annotations.cache.NoCache;
import org.jboss.resteasy.spi.NotFoundException;
+import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
import org.keycloak.common.ClientConnection;
import org.keycloak.events.admin.OperationType;
import org.keycloak.events.admin.ResourceType;
@@ -33,6 +34,7 @@ import org.keycloak.representations.idm.ClientMappingsRepresentation;
import org.keycloak.representations.idm.MappingsRepresentation;
import org.keycloak.representations.idm.RoleRepresentation;
import org.keycloak.services.ErrorResponseException;
+import org.keycloak.services.ForbiddenException;
import org.keycloak.services.managers.RealmManager;
import javax.ws.rs.Consumes;
@@ -55,6 +57,7 @@ import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
+import java.util.stream.Collectors;
/**
* Base resource for managing users
@@ -64,16 +67,19 @@ import java.util.Set;
* @version $Revision: 1 $
*/
public class RoleMapperResource {
+
protected static final Logger logger = Logger.getLogger(RoleMapperResource.class);
protected RealmModel realm;
- private RealmAuth auth;
-
private RoleMapperModel roleMapper;
private AdminEventBuilder adminEvent;
+ protected AdminPermissionEvaluator.RequirePermissionCheck managePermission;
+ protected AdminPermissionEvaluator.RequirePermissionCheck viewPermission;
+ private AdminPermissionEvaluator auth;
+
@Context
protected ClientConnection clientConnection;
@@ -86,15 +92,21 @@ public class RoleMapperResource {
@Context
protected HttpHeaders headers;
- public RoleMapperResource(RealmModel realm, RealmAuth auth, RoleMapperModel roleMapper, AdminEventBuilder adminEvent) {
+ public RoleMapperResource(RealmModel realm,
+ AdminPermissionEvaluator auth,
+ RoleMapperModel roleMapper,
+ AdminEventBuilder adminEvent,
+ AdminPermissionEvaluator.RequirePermissionCheck manageCheck,
+ AdminPermissionEvaluator.RequirePermissionCheck viewCheck) {
this.auth = auth;
this.realm = realm;
this.adminEvent = adminEvent.resource(ResourceType.REALM_ROLE_MAPPING);
this.roleMapper = roleMapper;
+ this.managePermission = manageCheck;
+ this.viewPermission = viewCheck;
}
-
/**
* Get role mappings
*
@@ -104,11 +116,7 @@ public class RoleMapperResource {
@Produces(MediaType.APPLICATION_JSON)
@NoCache
public MappingsRepresentation getRoleMappings() {
- auth.requireView();
-
- if (roleMapper == null) {
- throw new NotFoundException("User not found");
- }
+ viewPermission.require();
MappingsRepresentation all = new MappingsRepresentation();
Set<RoleModel> realmMappings = roleMapper.getRealmRoleMappings();
@@ -153,11 +161,7 @@ public class RoleMapperResource {
@Produces(MediaType.APPLICATION_JSON)
@NoCache
public List<RoleRepresentation> getRealmRoleMappings() {
- auth.requireView();
-
- if (roleMapper == null) {
- throw new NotFoundException("User not found");
- }
+ viewPermission.require();
Set<RoleModel> realmMappings = roleMapper.getRealmRoleMappings();
List<RoleRepresentation> realmMappingsRep = new ArrayList<RoleRepresentation>();
@@ -179,11 +183,7 @@ public class RoleMapperResource {
@Produces(MediaType.APPLICATION_JSON)
@NoCache
public List<RoleRepresentation> getCompositeRealmRoleMappings() {
- auth.requireView();
-
- if (roleMapper == null) {
- throw new NotFoundException("User not found");
- }
+ viewPermission.require();
Set<RoleModel> roles = realm.getRoles();
List<RoleRepresentation> realmMappingsRep = new ArrayList<RoleRepresentation>();
@@ -205,14 +205,13 @@ public class RoleMapperResource {
@Produces(MediaType.APPLICATION_JSON)
@NoCache
public List<RoleRepresentation> getAvailableRealmRoleMappings() {
- auth.requireView();
-
- if (roleMapper == null) {
- throw new NotFoundException("User not found");
- }
+ viewPermission.require();
Set<RoleModel> available = realm.getRoles();
- return ClientRoleMappingsResource.getAvailableRoles(roleMapper, available);
+ Set<RoleModel> set = available.stream().filter(r ->
+ canMapRole(r)
+ ).collect(Collectors.toSet());
+ return ClientRoleMappingsResource.getAvailableRoles(roleMapper, set);
}
/**
@@ -224,11 +223,7 @@ public class RoleMapperResource {
@POST
@Consumes(MediaType.APPLICATION_JSON)
public void addRealmRoleMappings(List<RoleRepresentation> roles) {
- auth.requireManage();
-
- if (roleMapper == null) {
- throw new NotFoundException("User not found");
- }
+ managePermission.require();
logger.debugv("** addRealmRoleMappings: {0}", roles);
@@ -237,6 +232,7 @@ public class RoleMapperResource {
if (roleModel == null || !roleModel.getId().equals(role.getId())) {
throw new NotFoundException("Role not found");
}
+ auth.roles().requireMapRole(roleModel);
roleMapper.grantRole(roleModel);
}
@@ -252,11 +248,7 @@ public class RoleMapperResource {
@DELETE
@Consumes(MediaType.APPLICATION_JSON)
public void deleteRealmRoleMappings(List<RoleRepresentation> roles) {
- auth.requireManage();
-
- if (roleMapper == null) {
- throw new NotFoundException("User not found");
- }
+ managePermission.require();
logger.debug("deleteRealmRoleMappings");
if (roles == null) {
@@ -264,6 +256,7 @@ public class RoleMapperResource {
roles = new LinkedList<>();
for (RoleModel roleModel : roleModels) {
+ auth.roles().requireMapRole(roleModel);
roleMapper.deleteRoleMapping(roleModel);
roles.add(ModelToRepresentation.toRepresentation(roleModel));
}
@@ -274,11 +267,11 @@ public class RoleMapperResource {
if (roleModel == null || !roleModel.getId().equals(role.getId())) {
throw new NotFoundException("Role not found");
}
-
+ auth.roles().requireMapRole(roleModel);
try {
roleMapper.deleteRoleMapping(roleModel);
} catch (ModelException me) {
- Properties messages = AdminRoot.getMessages(session, realm, auth.getAuth().getToken().getLocale());
+ Properties messages = AdminRoot.getMessages(session, realm, auth.adminAuth().getToken().getLocale());
throw new ErrorResponseException(me.getMessage(), MessageFormat.format(messages.getProperty(me.getMessage(), me.getMessage()), me.getParameters()),
Response.Status.BAD_REQUEST);
}
@@ -290,10 +283,20 @@ public class RoleMapperResource {
}
+ private boolean canMapRole(RoleModel roleModel) {
+ return auth.roles().canMapRole(roleModel);
+ }
+
@Path("clients/{client}")
public ClientRoleMappingsResource getUserClientRoleMappingsResource(@PathParam("client") String client) {
ClientModel clientModel = realm.getClientById(client);
- return new ClientRoleMappingsResource(uriInfo, session, realm, auth, roleMapper, clientModel, adminEvent);
+ if (clientModel == null) {
+ throw new NotFoundException("Client not found");
+ }
+ ClientRoleMappingsResource resource = new ClientRoleMappingsResource(uriInfo, session, realm, auth, roleMapper,
+ clientModel, adminEvent,
+ managePermission, viewPermission);
+ return resource;
}
}
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RoleResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RoleResource.java
index 5fb1d34..0161ee6 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/RoleResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/RoleResource.java
@@ -25,6 +25,7 @@ import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.utils.ModelToRepresentation;
import org.keycloak.representations.idm.RoleRepresentation;
+import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
import javax.ws.rs.core.UriInfo;
import java.util.Collections;
@@ -60,12 +61,13 @@ public abstract class RoleResource {
if (rep.isScopeParamRequired() != null) role.setScopeParamRequired(rep.isScopeParamRequired());
}
- protected void addComposites(AdminEventBuilder adminEvent, UriInfo uriInfo, List<RoleRepresentation> roles, RoleModel role) {
+ protected void addComposites(AdminPermissionEvaluator auth, AdminEventBuilder adminEvent, UriInfo uriInfo, List<RoleRepresentation> roles, RoleModel role) {
for (RoleRepresentation rep : roles) {
RoleModel composite = realm.getRoleById(rep.getId());
if (composite == null) {
throw new NotFoundException("Could not find composite role");
}
+ auth.roles().requireMapComposite(composite);
role.addCompositeRole(composite);
}
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ScopeMappedClientResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ScopeMappedClientResource.java
index 431e97c..4f7b5dc 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/ScopeMappedClientResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ScopeMappedClientResource.java
@@ -29,6 +29,7 @@ import org.keycloak.models.ScopeContainerModel;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.models.utils.ModelToRepresentation;
import org.keycloak.representations.idm.RoleRepresentation;
+import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
@@ -49,19 +50,25 @@ import java.util.Set;
*/
public class ScopeMappedClientResource {
protected RealmModel realm;
- private RealmAuth auth;
+ protected AdminPermissionEvaluator auth;
+ protected AdminPermissionEvaluator.RequirePermissionCheck managePermission;
+ protected AdminPermissionEvaluator.RequirePermissionCheck viewPermission;
protected ScopeContainerModel scopeContainer;
protected KeycloakSession session;
protected ClientModel scopedClient;
protected AdminEventBuilder adminEvent;
- public ScopeMappedClientResource(RealmModel realm, RealmAuth auth, ScopeContainerModel scopeContainer, KeycloakSession session, ClientModel scopedClient, AdminEventBuilder adminEvent) {
+ public ScopeMappedClientResource(RealmModel realm, AdminPermissionEvaluator auth, ScopeContainerModel scopeContainer, KeycloakSession session, ClientModel scopedClient, AdminEventBuilder adminEvent,
+ AdminPermissionEvaluator.RequirePermissionCheck managePermission,
+ AdminPermissionEvaluator.RequirePermissionCheck viewPermission) {
this.realm = realm;
this.auth = auth;
this.scopeContainer = scopeContainer;
this.session = session;
this.scopedClient = scopedClient;
this.adminEvent = adminEvent.resource(ResourceType.CLIENT_SCOPE_MAPPING);
+ this.managePermission = managePermission;
+ this.viewPermission = viewPermission;
}
/**
@@ -75,11 +82,7 @@ public class ScopeMappedClientResource {
@Produces(MediaType.APPLICATION_JSON)
@NoCache
public List<RoleRepresentation> getClientScopeMappings() {
- auth.requireView();
-
- if (scopeContainer == null) {
- throw new NotFoundException("Could not find client");
- }
+ viewPermission.require();
Set<RoleModel> mappings = KeycloakModelUtils.getClientScopeMappings(scopedClient, scopeContainer); //scopedClient.getClientScopeMappings(client);
List<RoleRepresentation> mapRep = new ArrayList<RoleRepresentation>();
@@ -101,14 +104,10 @@ public class ScopeMappedClientResource {
@Produces(MediaType.APPLICATION_JSON)
@NoCache
public List<RoleRepresentation> getAvailableClientScopeMappings() {
- auth.requireView();
-
- if (scopeContainer == null) {
- throw new NotFoundException("Could not find client");
- }
+ viewPermission.require();
Set<RoleModel> roles = scopedClient.getRoles();
- return ScopeMappedResource.getAvailable(scopeContainer, roles);
+ return ScopeMappedResource.getAvailable(auth, scopeContainer, roles);
}
/**
@@ -123,11 +122,7 @@ public class ScopeMappedClientResource {
@Produces(MediaType.APPLICATION_JSON)
@NoCache
public List<RoleRepresentation> getCompositeClientScopeMappings() {
- auth.requireView();
-
- if (scopeContainer == null) {
- throw new NotFoundException("Could not find client");
- }
+ viewPermission.require();
Set<RoleModel> roles = scopedClient.getRoles();
return ScopeMappedResource.getComposite(scopeContainer, roles);
@@ -141,11 +136,7 @@ public class ScopeMappedClientResource {
@POST
@Consumes(MediaType.APPLICATION_JSON)
public void addClientScopeMapping(List<RoleRepresentation> roles) {
- auth.requireManage();
-
- if (scopeContainer == null) {
- throw new NotFoundException("Could not find client");
- }
+ managePermission.require();
for (RoleRepresentation role : roles) {
RoleModel roleModel = scopedClient.getRole(role.getName());
@@ -166,11 +157,7 @@ public class ScopeMappedClientResource {
@DELETE
@Consumes(MediaType.APPLICATION_JSON)
public void deleteClientScopeMapping(List<RoleRepresentation> roles) {
- auth.requireManage();
-
- if (scopeContainer == null) {
- throw new NotFoundException("Could not find client");
- }
+ managePermission.require();
if (roles == null) {
Set<RoleModel> roleModels = KeycloakModelUtils.getClientScopeMappings(scopedClient, scopeContainer);//scopedClient.getClientScopeMappings(client);
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ScopeMappedResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ScopeMappedResource.java
index 19a32f9..286e22b 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/ScopeMappedResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ScopeMappedResource.java
@@ -31,6 +31,7 @@ import org.keycloak.models.utils.ModelToRepresentation;
import org.keycloak.representations.idm.ClientMappingsRepresentation;
import org.keycloak.representations.idm.MappingsRepresentation;
import org.keycloak.representations.idm.RoleRepresentation;
+import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
@@ -56,17 +57,25 @@ import java.util.Set;
*/
public class ScopeMappedResource {
protected RealmModel realm;
- private RealmAuth auth;
+ protected AdminPermissionEvaluator auth;
+ protected AdminPermissionEvaluator.RequirePermissionCheck managePermission;
+ protected AdminPermissionEvaluator.RequirePermissionCheck viewPermission;
+
protected ScopeContainerModel scopeContainer;
protected KeycloakSession session;
protected AdminEventBuilder adminEvent;
- public ScopeMappedResource(RealmModel realm, RealmAuth auth, ScopeContainerModel scopeContainer, KeycloakSession session, AdminEventBuilder adminEvent) {
+ public ScopeMappedResource(RealmModel realm, AdminPermissionEvaluator auth, ScopeContainerModel scopeContainer,
+ KeycloakSession session, AdminEventBuilder adminEvent,
+ AdminPermissionEvaluator.RequirePermissionCheck managePermission,
+ AdminPermissionEvaluator.RequirePermissionCheck viewPermission) {
this.realm = realm;
this.auth = auth;
this.scopeContainer = scopeContainer;
this.session = session;
this.adminEvent = adminEvent.resource(ResourceType.REALM_SCOPE_MAPPING);
+ this.managePermission = managePermission;
+ this.viewPermission = viewPermission;
}
/**
@@ -78,7 +87,7 @@ public class ScopeMappedResource {
@Produces(MediaType.APPLICATION_JSON)
@NoCache
public MappingsRepresentation getScopeMappings() {
- auth.requireView();
+ viewPermission.require();
if (scopeContainer == null) {
throw new NotFoundException("Could not find client");
@@ -126,7 +135,7 @@ public class ScopeMappedResource {
@Produces(MediaType.APPLICATION_JSON)
@NoCache
public List<RoleRepresentation> getRealmScopeMappings() {
- auth.requireView();
+ viewPermission.require();
if (scopeContainer == null) {
throw new NotFoundException("Could not find client");
@@ -150,20 +159,21 @@ public class ScopeMappedResource {
@Produces(MediaType.APPLICATION_JSON)
@NoCache
public List<RoleRepresentation> getAvailableRealmScopeMappings() {
- auth.requireView();
+ viewPermission.require();
if (scopeContainer == null) {
throw new NotFoundException("Could not find client");
}
Set<RoleModel> roles = realm.getRoles();
- return getAvailable(scopeContainer, roles);
+ return getAvailable(auth, scopeContainer, roles);
}
- public static List<RoleRepresentation> getAvailable(ScopeContainerModel client, Set<RoleModel> roles) {
+ public static List<RoleRepresentation> getAvailable(AdminPermissionEvaluator auth, ScopeContainerModel client, Set<RoleModel> roles) {
List<RoleRepresentation> available = new ArrayList<RoleRepresentation>();
for (RoleModel roleModel : roles) {
if (client.hasScope(roleModel)) continue;
+ if (!auth.roles().canMapClientScope(roleModel)) continue;
available.add(ModelToRepresentation.toRepresentation(roleModel));
}
return available;
@@ -183,7 +193,7 @@ public class ScopeMappedResource {
@Produces(MediaType.APPLICATION_JSON)
@NoCache
public List<RoleRepresentation> getCompositeRealmScopeMappings() {
- auth.requireView();
+ viewPermission.require();
if (scopeContainer == null) {
throw new NotFoundException("Could not find client");
@@ -210,7 +220,7 @@ public class ScopeMappedResource {
@POST
@Consumes(MediaType.APPLICATION_JSON)
public void addRealmScopeMappings(List<RoleRepresentation> roles) {
- auth.requireManage();
+ managePermission.require();
if (scopeContainer == null) {
throw new NotFoundException("Could not find client");
@@ -236,7 +246,7 @@ public class ScopeMappedResource {
@DELETE
@Consumes(MediaType.APPLICATION_JSON)
public void deleteRealmScopeMappings(List<RoleRepresentation> roles) {
- auth.requireManage();
+ managePermission.require();
if (scopeContainer == null) {
throw new NotFoundException("Could not find client");
@@ -268,6 +278,9 @@ public class ScopeMappedResource {
@Path("clients/{client}")
public ScopeMappedClientResource getClientByIdScopeMappings(@PathParam("client") String client) {
ClientModel clientModel = realm.getClientById(client);
- return new ScopeMappedClientResource(realm, auth, this.scopeContainer, session, clientModel, adminEvent);
+ if (clientModel == null) {
+ throw new NotFoundException("Could not find client");
+ }
+ return new ScopeMappedClientResource(realm, auth, this.scopeContainer, session, clientModel, adminEvent, managePermission, viewPermission);
}
}
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/UserResource.java b/services/src/main/java/org/keycloak/services/resources/admin/UserResource.java
new file mode 100755
index 0000000..bf3b236
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/resources/admin/UserResource.java
@@ -0,0 +1,795 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.services.resources.admin;
+
+import org.jboss.logging.Logger;
+import org.jboss.resteasy.annotations.cache.NoCache;
+import org.jboss.resteasy.spi.BadRequestException;
+import org.jboss.resteasy.spi.NotFoundException;
+import org.jboss.resteasy.spi.ResteasyProviderFactory;
+import org.keycloak.authentication.RequiredActionProvider;
+import org.keycloak.authentication.actiontoken.execactions.ExecuteActionsActionToken;
+import org.keycloak.common.ClientConnection;
+import org.keycloak.common.Profile;
+import org.keycloak.common.util.Time;
+import org.keycloak.credential.CredentialModel;
+import org.keycloak.email.EmailException;
+import org.keycloak.email.EmailTemplateProvider;
+import org.keycloak.events.Details;
+import org.keycloak.events.EventBuilder;
+import org.keycloak.events.EventType;
+import org.keycloak.events.admin.OperationType;
+import org.keycloak.events.admin.ResourceType;
+import org.keycloak.models.AuthenticatedClientSessionModel;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.Constants;
+import org.keycloak.models.FederatedIdentityModel;
+import org.keycloak.models.GroupModel;
+import org.keycloak.models.IdentityProviderModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.ModelDuplicateException;
+import org.keycloak.models.ModelException;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserConsentModel;
+import org.keycloak.models.UserCredentialModel;
+import org.keycloak.models.UserLoginFailureModel;
+import org.keycloak.models.UserManager;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.UserSessionModel;
+import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.models.utils.ModelToRepresentation;
+import org.keycloak.protocol.oidc.OIDCLoginProtocol;
+import org.keycloak.protocol.oidc.utils.RedirectUtils;
+import org.keycloak.provider.ProviderFactory;
+import org.keycloak.representations.idm.CredentialRepresentation;
+import org.keycloak.representations.idm.FederatedIdentityRepresentation;
+import org.keycloak.representations.idm.GroupRepresentation;
+import org.keycloak.representations.idm.UserConsentRepresentation;
+import org.keycloak.representations.idm.UserRepresentation;
+import org.keycloak.representations.idm.UserSessionRepresentation;
+import org.keycloak.services.ErrorResponse;
+import org.keycloak.services.ErrorResponseException;
+import org.keycloak.services.ForbiddenException;
+import org.keycloak.services.ServicesLogger;
+import org.keycloak.services.managers.AuthenticationManager;
+import org.keycloak.services.managers.BruteForceProtector;
+import org.keycloak.services.managers.UserSessionManager;
+import org.keycloak.services.resources.AccountService;
+import org.keycloak.services.resources.LoginActionsService;
+import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
+import org.keycloak.services.validation.Validation;
+import org.keycloak.storage.ReadOnlyException;
+import org.keycloak.utils.ProfileHelper;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
+import java.net.URI;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Base resource for managing users
+ *
+ * @resource Users
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class UserResource {
+ private static final Logger logger = Logger.getLogger(UserResource.class);
+
+ protected RealmModel realm;
+
+ private AdminPermissionEvaluator auth;
+
+ private AdminEventBuilder adminEvent;
+ private UserModel user;
+
+ @Context
+ protected ClientConnection clientConnection;
+
+ @Context
+ protected UriInfo uriInfo;
+
+ @Context
+ protected KeycloakSession session;
+
+ @Context
+ protected HttpHeaders headers;
+
+ public UserResource(RealmModel realm, UserModel user, AdminPermissionEvaluator auth, AdminEventBuilder adminEvent) {
+ this.auth = auth;
+ this.realm = realm;
+ this.user = user;
+ this.adminEvent = adminEvent.resource(ResourceType.USER);
+ }
+
+ /**
+ * Update the user
+ *
+ * @param rep
+ * @return
+ */
+ @PUT
+ @Consumes(MediaType.APPLICATION_JSON)
+ public Response updateUser(final UserRepresentation rep) {
+
+ auth.users().requireManage(user);
+ try {
+ Set<String> attrsToRemove;
+ if (rep.getAttributes() != null) {
+ attrsToRemove = new HashSet<>(user.getAttributes().keySet());
+ attrsToRemove.removeAll(rep.getAttributes().keySet());
+ } else {
+ attrsToRemove = Collections.emptySet();
+ }
+
+ if (rep.isEnabled() != null && rep.isEnabled()) {
+ UserLoginFailureModel failureModel = session.sessions().getUserLoginFailure(realm, user.getId());
+ if (failureModel != null) {
+ failureModel.clearFailures();
+ }
+ }
+
+ updateUserFromRep(user, rep, attrsToRemove, realm, session, true);
+ adminEvent.operation(OperationType.UPDATE).resourcePath(uriInfo).representation(rep).success();
+
+ if (session.getTransactionManager().isActive()) {
+ session.getTransactionManager().commit();
+ }
+ return Response.noContent().build();
+ } catch (ModelDuplicateException e) {
+ return ErrorResponse.exists("User exists with same username or email");
+ } catch (ReadOnlyException re) {
+ return ErrorResponse.exists("User is read only!");
+ } catch (ModelException me) {
+ logger.warn("Could not update user!", me);
+ return ErrorResponse.exists("Could not update user!");
+ } catch (ForbiddenException fe) {
+ throw fe;
+ } catch (Exception me) { // JPA
+ logger.warn("Could not update user!", me);// may be committed by JTA which can't
+ return ErrorResponse.exists("Could not update user!");
+ }
+ }
+
+ public static void updateUserFromRep(UserModel user, UserRepresentation rep, Set<String> attrsToRemove, RealmModel realm, KeycloakSession session, boolean removeMissingRequiredActions) {
+ if (rep.getUsername() != null && realm.isEditUsernameAllowed()) {
+ user.setUsername(rep.getUsername());
+ }
+ if (rep.getEmail() != null) user.setEmail(rep.getEmail());
+ if (rep.getFirstName() != null) user.setFirstName(rep.getFirstName());
+ if (rep.getLastName() != null) user.setLastName(rep.getLastName());
+
+ if (rep.isEnabled() != null) user.setEnabled(rep.isEnabled());
+ if (rep.isEmailVerified() != null) user.setEmailVerified(rep.isEmailVerified());
+
+ List<String> reqActions = rep.getRequiredActions();
+
+ if (reqActions != null) {
+ Set<String> allActions = new HashSet<>();
+ for (ProviderFactory factory : session.getKeycloakSessionFactory().getProviderFactories(RequiredActionProvider.class)) {
+ allActions.add(factory.getId());
+ }
+ for (String action : allActions) {
+ if (reqActions.contains(action)) {
+ user.addRequiredAction(action);
+ } else if (removeMissingRequiredActions) {
+ user.removeRequiredAction(action);
+ }
+ }
+ }
+
+ if (rep.getAttributes() != null) {
+ for (Map.Entry<String, List<String>> attr : rep.getAttributes().entrySet()) {
+ user.setAttribute(attr.getKey(), attr.getValue());
+ }
+
+ for (String attr : attrsToRemove) {
+ user.removeAttribute(attr);
+ }
+ }
+ }
+
+ /**
+ * Get representation of the user
+ *
+ * @return
+ */
+ @GET
+ @NoCache
+ @Produces(MediaType.APPLICATION_JSON)
+ public UserRepresentation getUser() {
+ auth.users().requireView(user);
+
+ UserRepresentation rep = ModelToRepresentation.toRepresentation(session, realm, user);
+
+ if (realm.isIdentityFederationEnabled()) {
+ List<FederatedIdentityRepresentation> reps = getFederatedIdentities(user);
+ rep.setFederatedIdentities(reps);
+ }
+
+ if (session.getProvider(BruteForceProtector.class).isTemporarilyDisabled(session, realm, user)) {
+ rep.setEnabled(false);
+ }
+ rep.setAccess(auth.users().getAccess(user));
+
+ return rep;
+ }
+
+ /**
+ * Impersonate the user
+ *
+ * @return
+ */
+ @Path("impersonation")
+ @POST
+ @NoCache
+ @Produces(MediaType.APPLICATION_JSON)
+ public Map<String, Object> impersonate() {
+ ProfileHelper.requireFeature(Profile.Feature.IMPERSONATION);
+
+ auth.users().requireImpersonate(user);
+ RealmModel authenticatedRealm = auth.adminAuth().getRealm();
+ // if same realm logout before impersonation
+ boolean sameRealm = false;
+ if (authenticatedRealm.getId().equals(realm.getId())) {
+ sameRealm = true;
+ UserSessionModel userSession = session.sessions().getUserSession(authenticatedRealm, auth.adminAuth().getToken().getSessionState());
+ AuthenticationManager.expireIdentityCookie(realm, uriInfo, clientConnection);
+ AuthenticationManager.expireRememberMeCookie(realm, uriInfo, clientConnection);
+ AuthenticationManager.backchannelLogout(session, authenticatedRealm, userSession, uriInfo, clientConnection, headers, true);
+ }
+ EventBuilder event = new EventBuilder(realm, session, clientConnection);
+
+ String sessionId = KeycloakModelUtils.generateId();
+ UserSessionModel userSession = session.sessions().createUserSession(sessionId, realm, user, user.getUsername(), clientConnection.getRemoteAddr(), "impersonate", false, null, null);
+ AuthenticationManager.createLoginCookie(session, realm, userSession.getUser(), userSession, uriInfo, clientConnection);
+ URI redirect = AccountService.accountServiceApplicationPage(uriInfo).build(realm.getName());
+ Map<String, Object> result = new HashMap<>();
+ result.put("sameRealm", sameRealm);
+ result.put("redirect", redirect.toString());
+ event.event(EventType.IMPERSONATE)
+ .session(userSession)
+ .user(user)
+ .detail(Details.IMPERSONATOR_REALM,authenticatedRealm.getName())
+ .detail(Details.IMPERSONATOR, auth.adminAuth().getUser().getUsername()).success();
+
+ return result;
+ }
+
+
+ /**
+ * Get sessions associated with the user
+ *
+ * @return
+ */
+ @Path("sessions")
+ @GET
+ @NoCache
+ @Produces(MediaType.APPLICATION_JSON)
+ public List<UserSessionRepresentation> getSessions() {
+ auth.users().requireView(user);
+ List<UserSessionModel> sessions = session.sessions().getUserSessions(realm, user);
+ List<UserSessionRepresentation> reps = new ArrayList<UserSessionRepresentation>();
+ for (UserSessionModel session : sessions) {
+ UserSessionRepresentation rep = ModelToRepresentation.toRepresentation(session);
+ reps.add(rep);
+ }
+ return reps;
+ }
+
+ /**
+ * Get offline sessions associated with the user and client
+ *
+ * @return
+ */
+ @Path("offline-sessions/{clientId}")
+ @GET
+ @NoCache
+ @Produces(MediaType.APPLICATION_JSON)
+ public List<UserSessionRepresentation> getOfflineSessions(final @PathParam("clientId") String clientId) {
+ auth.users().requireView(user);
+ ClientModel client = realm.getClientById(clientId);
+ if (client == null) {
+ throw new NotFoundException("Client not found");
+ }
+ List<UserSessionModel> sessions = new UserSessionManager(session).findOfflineSessions(realm, user);
+ List<UserSessionRepresentation> reps = new ArrayList<UserSessionRepresentation>();
+ for (UserSessionModel session : sessions) {
+ UserSessionRepresentation rep = ModelToRepresentation.toRepresentation(session);
+
+ // Update lastSessionRefresh with the timestamp from clientSession
+ AuthenticatedClientSessionModel clientSession = session.getAuthenticatedClientSessions().get(clientId);
+
+ // Skip if userSession is not for this client
+ if (clientSession == null) {
+ continue;
+ }
+
+ rep.setLastAccess(clientSession.getTimestamp());
+
+ reps.add(rep);
+ }
+ return reps;
+ }
+
+ /**
+ * Get social logins associated with the user
+ *
+ * @return
+ */
+ @Path("federated-identity")
+ @GET
+ @NoCache
+ @Produces(MediaType.APPLICATION_JSON)
+ public List<FederatedIdentityRepresentation> getFederatedIdentity() {
+ auth.users().requireView(user);
+
+ return getFederatedIdentities(user);
+ }
+
+ private List<FederatedIdentityRepresentation> getFederatedIdentities(UserModel user) {
+ Set<FederatedIdentityModel> identities = session.users().getFederatedIdentities(user, realm);
+ List<FederatedIdentityRepresentation> result = new ArrayList<FederatedIdentityRepresentation>();
+
+ for (FederatedIdentityModel identity : identities) {
+ for (IdentityProviderModel identityProviderModel : realm.getIdentityProviders()) {
+ if (identityProviderModel.getAlias().equals(identity.getIdentityProvider())) {
+ FederatedIdentityRepresentation rep = ModelToRepresentation.toRepresentation(identity);
+ result.add(rep);
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Add a social login provider to the user
+ *
+ * @param provider Social login provider id
+ * @param rep
+ * @return
+ */
+ @Path("federated-identity/{provider}")
+ @POST
+ @NoCache
+ public Response addFederatedIdentity(final @PathParam("provider") String provider, FederatedIdentityRepresentation rep) {
+ auth.users().requireManage(user);
+ if (session.users().getFederatedIdentity(user, provider, realm) != null) {
+ return ErrorResponse.exists("User is already linked with provider");
+ }
+
+ FederatedIdentityModel socialLink = new FederatedIdentityModel(provider, rep.getUserId(), rep.getUserName());
+ session.users().addFederatedIdentity(realm, user, socialLink);
+ adminEvent.operation(OperationType.CREATE).resourcePath(uriInfo).representation(rep).success();
+ return Response.noContent().build();
+ }
+
+ /**
+ * Remove a social login provider from user
+ *
+ * @param provider Social login provider id
+ */
+ @Path("federated-identity/{provider}")
+ @DELETE
+ @NoCache
+ public void removeFederatedIdentity(final @PathParam("provider") String provider) {
+ auth.users().requireManage(user);
+ if (!session.users().removeFederatedIdentity(realm, user, provider)) {
+ throw new NotFoundException("Link not found");
+ }
+ adminEvent.operation(OperationType.DELETE).resourcePath(uriInfo).success();
+ }
+
+ /**
+ * Get consents granted by the user
+ *
+ * @return
+ */
+ @Path("consents")
+ @GET
+ @NoCache
+ @Produces(MediaType.APPLICATION_JSON)
+ public List<Map<String, Object>> getConsents() {
+ auth.users().requireView(user);
+ List<Map<String, Object>> result = new LinkedList<>();
+
+ Set<ClientModel> offlineClients = new UserSessionManager(session).findClientsWithOfflineToken(realm, user);
+
+ for (ClientModel client : realm.getClients()) {
+ UserConsentModel consent = session.users().getConsentByClient(realm, user.getId(), client.getId());
+ boolean hasOfflineToken = offlineClients.contains(client);
+
+ if (consent == null && !hasOfflineToken) {
+ continue;
+ }
+
+ UserConsentRepresentation rep = (consent == null) ? null : ModelToRepresentation.toRepresentation(consent);
+
+ Map<String, Object> currentRep = new HashMap<>();
+ currentRep.put("clientId", client.getClientId());
+ currentRep.put("grantedProtocolMappers", (rep==null ? Collections.emptyMap() : rep.getGrantedProtocolMappers()));
+ currentRep.put("grantedRealmRoles", (rep==null ? Collections.emptyList() : rep.getGrantedRealmRoles()));
+ currentRep.put("grantedClientRoles", (rep==null ? Collections.emptyMap() : rep.getGrantedClientRoles()));
+ currentRep.put("createdDate", (rep==null ? null : rep.getCreatedDate()));
+ currentRep.put("lastUpdatedDate", (rep==null ? null : rep.getLastUpdatedDate()));
+
+ List<Map<String, String>> additionalGrants = new LinkedList<>();
+ if (hasOfflineToken) {
+ Map<String, String> offlineTokens = new HashMap<>();
+ offlineTokens.put("client", client.getId());
+ // TODO: translate
+ offlineTokens.put("key", "Offline Token");
+ additionalGrants.add(offlineTokens);
+ }
+ currentRep.put("additionalGrants", additionalGrants);
+
+ result.add(currentRep);
+ }
+
+ return result;
+ }
+
+ /**
+ * Revoke consent and offline tokens for particular client from user
+ *
+ * @param clientId Client id
+ */
+ @Path("consents/{client}")
+ @DELETE
+ @NoCache
+ public void revokeConsent(final @PathParam("client") String clientId) {
+ auth.users().requireManage(user);
+
+ ClientModel client = realm.getClientByClientId(clientId);
+ if (client == null) {
+ throw new NotFoundException("Client not found");
+ }
+ boolean revokedConsent = session.users().revokeConsentForClient(realm, user.getId(), client.getId());
+ boolean revokedOfflineToken = new UserSessionManager(session).revokeOfflineToken(user, client);
+
+ if (revokedConsent) {
+ // Logout clientSessions for this user and client
+ AuthenticationManager.backchannelUserFromClient(session, realm, user, client, uriInfo, headers);
+ }
+
+ if (!revokedConsent && !revokedOfflineToken) {
+ throw new NotFoundException("Consent nor offline token not found");
+ }
+ adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo).success();
+ }
+
+ /**
+ * Remove all user sessions associated with the user
+ *
+ * Also send notification to all clients that have an admin URL to invalidate the sessions for the particular user.
+ *
+ */
+ @Path("logout")
+ @POST
+ public void logout() {
+ auth.users().requireManage(user);
+
+ List<UserSessionModel> userSessions = session.sessions().getUserSessions(realm, user);
+ for (UserSessionModel userSession : userSessions) {
+ AuthenticationManager.backchannelLogout(session, realm, userSession, uriInfo, clientConnection, headers, true);
+ }
+ adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo).success();
+ }
+
+ /**
+ * Delete the user
+ */
+ @DELETE
+ @NoCache
+ public Response deleteUser() {
+ auth.users().requireManage(user);
+
+ boolean removed = new UserManager(session).removeUser(realm, user);
+ if (removed) {
+ adminEvent.operation(OperationType.DELETE).resourcePath(uriInfo).success();
+ return Response.noContent().build();
+ } else {
+ return ErrorResponse.error("User couldn't be deleted", Status.BAD_REQUEST);
+ }
+ }
+
+ @Path("role-mappings")
+ public RoleMapperResource getRoleMappings() {
+ AdminPermissionEvaluator.RequirePermissionCheck manageCheck = () -> auth.users().requireMapRoles(user);
+ AdminPermissionEvaluator.RequirePermissionCheck viewCheck = () -> auth.users().requireView(user);
+ RoleMapperResource resource = new RoleMapperResource(realm, auth, user, adminEvent, manageCheck, viewCheck);
+ ResteasyProviderFactory.getInstance().injectProperties(resource);
+ return resource;
+
+ }
+
+ /**
+ * Disable all credentials for a user of a specific type
+ *
+ * @param credentialTypes
+ */
+ @Path("disable-credential-types")
+ @PUT
+ @Consumes(MediaType.APPLICATION_JSON)
+ public void disableCredentialType(List<String> credentialTypes) {
+ auth.users().requireManage(user);
+ if (credentialTypes == null) return;
+ for (String type : credentialTypes) {
+ session.userCredentialManager().disableCredentialType(realm, user, type);
+
+ }
+
+
+ }
+
+ /**
+ * Set up a temporary password for the user
+ *
+ * User will have to reset the temporary password next time they log in.
+ *
+ * @param pass A Temporary password
+ */
+ @Path("reset-password")
+ @PUT
+ @Consumes(MediaType.APPLICATION_JSON)
+ public void resetPassword(CredentialRepresentation pass) {
+ auth.users().requireManage(user);
+ if (pass == null || pass.getValue() == null || !CredentialRepresentation.PASSWORD.equals(pass.getType())) {
+ throw new BadRequestException("No password provided");
+ }
+ if (Validation.isBlank(pass.getValue())) {
+ throw new BadRequestException("Empty password not allowed");
+ }
+
+ UserCredentialModel cred = UserCredentialModel.password(pass.getValue(), true);
+ try {
+ session.userCredentialManager().updateCredential(realm, user, cred);
+ } catch (IllegalStateException ise) {
+ throw new BadRequestException("Resetting to N old passwords is not allowed.");
+ } catch (ReadOnlyException mre) {
+ throw new BadRequestException("Can't reset password as account is read only");
+ } catch (ModelException e) {
+ Properties messages = AdminRoot.getMessages(session, realm, auth.adminAuth().getToken().getLocale());
+ throw new ErrorResponseException(e.getMessage(), MessageFormat.format(messages.getProperty(e.getMessage(), e.getMessage()), e.getParameters()),
+ Status.BAD_REQUEST);
+ }
+ if (pass.isTemporary() != null && pass.isTemporary()) user.addRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD);
+
+ adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo).success();
+ }
+
+ /**
+ * Remove TOTP from the user
+ *
+ */
+ @Path("remove-totp")
+ @PUT
+ @Consumes(MediaType.APPLICATION_JSON)
+ public void removeTotp() {
+ auth.users().requireManage(user);
+
+ session.userCredentialManager().disableCredentialType(realm, user, CredentialModel.OTP);
+ adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo).success();
+ }
+
+ /**
+ * Send an email to the user with a link they can click to reset their password.
+ * The redirectUri and clientId parameters are optional. The default for the
+ * redirect is the account client.
+ *
+ * This endpoint has been deprecated. Please use the execute-actions-email passing a list with
+ * UPDATE_PASSWORD within it.
+ *
+ * @param redirectUri redirect uri
+ * @param clientId client id
+ * @return
+ */
+ @Deprecated
+ @Path("reset-password-email")
+ @PUT
+ @Consumes(MediaType.APPLICATION_JSON)
+ public Response resetPasswordEmail(@QueryParam(OIDCLoginProtocol.REDIRECT_URI_PARAM) String redirectUri,
+ @QueryParam(OIDCLoginProtocol.CLIENT_ID_PARAM) String clientId) {
+ List<String> actions = new LinkedList<>();
+ actions.add(UserModel.RequiredAction.UPDATE_PASSWORD.name());
+ return executeActionsEmail(redirectUri, clientId, null, actions);
+ }
+
+
+ /**
+ * Send a update account email to the user
+ *
+ * An email contains a link the user can click to perform a set of required actions.
+ * The redirectUri and clientId parameters are optional. If no redirect is given, then there will
+ * be no link back to click after actions have completed. Redirect uri must be a valid uri for the
+ * particular clientId.
+ *
+ * @param redirectUri Redirect uri
+ * @param clientId Client id
+ * @param lifespan Number of seconds after which the generated token expires
+ * @param actions required actions the user needs to complete
+ * @return
+ */
+ @Path("execute-actions-email")
+ @PUT
+ @Consumes(MediaType.APPLICATION_JSON)
+ public Response executeActionsEmail(@QueryParam(OIDCLoginProtocol.REDIRECT_URI_PARAM) String redirectUri,
+ @QueryParam(OIDCLoginProtocol.CLIENT_ID_PARAM) String clientId,
+ @QueryParam("lifespan") Integer lifespan,
+ List<String> actions) {
+ auth.users().requireManage(user);
+
+ if (user.getEmail() == null) {
+ return ErrorResponse.error("User email missing", Status.BAD_REQUEST);
+ }
+
+ if (!user.isEnabled()) {
+ throw new WebApplicationException(
+ ErrorResponse.error("User is disabled", Status.BAD_REQUEST));
+ }
+
+ if (redirectUri != null && clientId == null) {
+ throw new WebApplicationException(
+ ErrorResponse.error("Client id missing", Status.BAD_REQUEST));
+ }
+
+ if (clientId == null) {
+ clientId = Constants.ACCOUNT_MANAGEMENT_CLIENT_ID;
+ }
+
+ ClientModel client = realm.getClientByClientId(clientId);
+ if (client == null || !client.isEnabled()) {
+ throw new WebApplicationException(
+ ErrorResponse.error(clientId + " not enabled", Status.BAD_REQUEST));
+ }
+
+ String redirect;
+ if (redirectUri != null) {
+ redirect = RedirectUtils.verifyRedirectUri(uriInfo, redirectUri, realm, client);
+ if (redirect == null) {
+ throw new WebApplicationException(
+ ErrorResponse.error("Invalid redirect uri.", Status.BAD_REQUEST));
+ }
+ }
+
+ if (lifespan == null) {
+ lifespan = realm.getActionTokenGeneratedByAdminLifespan();
+ }
+ int expiration = Time.currentTime() + lifespan;
+ ExecuteActionsActionToken token = new ExecuteActionsActionToken(user.getId(), expiration, actions, redirectUri, clientId);
+
+ try {
+ UriBuilder builder = LoginActionsService.actionTokenProcessor(uriInfo);
+ builder.queryParam("key", token.serialize(session, realm, uriInfo));
+
+ String link = builder.build(realm.getName()).toString();
+
+ this.session.getProvider(EmailTemplateProvider.class)
+ .setRealm(realm)
+ .setUser(user)
+ .sendExecuteActions(link, TimeUnit.SECONDS.toMinutes(lifespan));
+
+ //audit.user(user).detail(Details.EMAIL, user.getEmail()).detail(Details.CODE_ID, accessCode.getCodeId()).success();
+
+ adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo).success();
+
+ return Response.ok().build();
+ } catch (EmailException e) {
+ ServicesLogger.LOGGER.failedToSendActionsEmail(e);
+ return ErrorResponse.error("Failed to send execute actions email", Status.INTERNAL_SERVER_ERROR);
+ }
+ }
+
+ /**
+ * Send an email-verification email to the user
+ *
+ * An email contains a link the user can click to verify their email address.
+ * The redirectUri and clientId parameters are optional. The default for the
+ * redirect is the account client.
+ *
+ * @param redirectUri Redirect uri
+ * @param clientId Client id
+ * @return
+ */
+ @Path("send-verify-email")
+ @PUT
+ @Consumes(MediaType.APPLICATION_JSON)
+ public Response sendVerifyEmail(@QueryParam(OIDCLoginProtocol.REDIRECT_URI_PARAM) String redirectUri, @QueryParam(OIDCLoginProtocol.CLIENT_ID_PARAM) String clientId) {
+ List<String> actions = new LinkedList<>();
+ actions.add(UserModel.RequiredAction.VERIFY_EMAIL.name());
+ return executeActionsEmail(redirectUri, clientId, null, actions);
+ }
+
+ @GET
+ @Path("groups")
+ @NoCache
+ @Produces(MediaType.APPLICATION_JSON)
+ public List<GroupRepresentation> groupMembership() {
+ auth.users().requireView(user);
+ List<GroupRepresentation> memberships = new LinkedList<>();
+ for (GroupModel group : user.getGroups()) {
+ memberships.add(ModelToRepresentation.toRepresentation(group, false));
+ }
+ return memberships;
+ }
+
+ @DELETE
+ @Path("groups/{groupId}")
+ @NoCache
+ public void removeMembership(@PathParam("groupId") String groupId) {
+ auth.users().requireManageGroupMembership(user);
+
+ GroupModel group = session.realms().getGroupById(groupId, realm);
+ if (group == null) {
+ throw new NotFoundException("Group not found");
+ }
+ auth.groups().requireManageMembership(group);
+
+ try {
+ if (user.isMemberOf(group)){
+ user.leaveGroup(group);
+ adminEvent.operation(OperationType.DELETE).resource(ResourceType.GROUP_MEMBERSHIP).representation(ModelToRepresentation.toRepresentation(group, true)).resourcePath(uriInfo).success();
+ }
+ } catch (ModelException me) {
+ Properties messages = AdminRoot.getMessages(session, realm, auth.adminAuth().getToken().getLocale());
+ throw new ErrorResponseException(me.getMessage(), MessageFormat.format(messages.getProperty(me.getMessage(), me.getMessage()), me.getParameters()),
+ Status.BAD_REQUEST);
+ }
+ }
+
+ @PUT
+ @Path("groups/{groupId}")
+ @NoCache
+ public void joinGroup(@PathParam("groupId") String groupId) {
+ auth.users().requireManageGroupMembership(user);
+ GroupModel group = session.realms().getGroupById(groupId, realm);
+ if (group == null) {
+ throw new NotFoundException("Group not found");
+ }
+ auth.groups().requireManageMembership(group);
+ if (!user.isMemberOf(group)){
+ user.joinGroup(group);
+ adminEvent.operation(OperationType.CREATE).resource(ResourceType.GROUP_MEMBERSHIP).representation(ModelToRepresentation.toRepresentation(group, true)).resourcePath(uriInfo).success();
+ }
+ }
+
+}
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java b/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java
index 815ee89..6ed677f 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java
@@ -23,6 +23,7 @@ import org.jboss.resteasy.spi.NotFoundException;
import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.keycloak.authentication.RequiredActionProvider;
import org.keycloak.authentication.actiontoken.execactions.ExecuteActionsActionToken;
+import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
import org.keycloak.common.ClientConnection;
import org.keycloak.common.Profile;
import org.keycloak.common.util.Time;
@@ -34,6 +35,20 @@ import org.keycloak.events.EventBuilder;
import org.keycloak.events.EventType;
import org.keycloak.events.admin.OperationType;
import org.keycloak.events.admin.ResourceType;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.Constants;
+import org.keycloak.models.FederatedIdentityModel;
+import org.keycloak.models.GroupModel;
+import org.keycloak.models.IdentityProviderModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.ModelDuplicateException;
+import org.keycloak.models.ModelException;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserConsentModel;
+import org.keycloak.models.UserCredentialModel;
+import org.keycloak.models.UserLoginFailureModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.UserSessionModel;
import org.keycloak.models.*;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.models.utils.ModelToRepresentation;
@@ -67,18 +82,26 @@ import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
+import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
+import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
import java.net.URI;
import java.text.MessageFormat;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
import java.util.concurrent.TimeUnit;
-import javax.ws.rs.*;
-import javax.ws.rs.core.*;
/**
* Base resource for managing users
@@ -92,7 +115,7 @@ public class UsersResource {
protected RealmModel realm;
- private RealmAuth auth;
+ private AdminPermissionEvaluator auth;
private AdminEventBuilder adminEvent;
@@ -108,66 +131,10 @@ public class UsersResource {
@Context
protected HttpHeaders headers;
- public UsersResource(RealmModel realm, RealmAuth auth, AdminEventBuilder adminEvent) {
+ public UsersResource(RealmModel realm, AdminPermissionEvaluator auth, AdminEventBuilder adminEvent) {
this.auth = auth;
this.realm = realm;
this.adminEvent = adminEvent.resource(ResourceType.USER);
-
- auth.init(RealmAuth.Resource.USER);
- }
-
- /**
- * Update the user
- *
- * @param id User id
- * @param rep
- * @return
- */
- @Path("{id}")
- @PUT
- @Consumes(MediaType.APPLICATION_JSON)
- public Response updateUser(final @PathParam("id") String id, final UserRepresentation rep) {
- auth.requireManage();
-
- try {
- UserModel user = session.users().getUserById(id, realm);
- if (user == null) {
- return Response.status(Status.NOT_FOUND).build();
- }
-
- Set<String> attrsToRemove;
- if (rep.getAttributes() != null) {
- attrsToRemove = new HashSet<>(user.getAttributes().keySet());
- attrsToRemove.removeAll(rep.getAttributes().keySet());
- } else {
- attrsToRemove = Collections.emptySet();
- }
-
- if (rep.isEnabled() != null && rep.isEnabled()) {
- UserLoginFailureModel failureModel = session.sessions().getUserLoginFailure(realm, id);
- if (failureModel != null) {
- failureModel.clearFailures();
- }
- }
-
- updateUserFromRep(user, rep, attrsToRemove, realm, session, true);
- adminEvent.operation(OperationType.UPDATE).resourcePath(uriInfo).representation(rep).success();
-
- if (session.getTransactionManager().isActive()) {
- session.getTransactionManager().commit();
- }
- return Response.noContent().build();
- } catch (ModelDuplicateException e) {
- return ErrorResponse.exists("User exists with same username or email");
- } catch (ReadOnlyException re) {
- return ErrorResponse.exists("User is read only!");
- } catch (ModelException me) {
- logger.warn("Could not update user!", me);
- return ErrorResponse.exists("Could not update user!");
- } catch (Exception me) { // JPA
- logger.warn("Could not update user!", me);// may be committed by JTA which can't
- return ErrorResponse.exists("Could not update user!");
- }
}
/**
@@ -182,7 +149,7 @@ public class UsersResource {
@POST
@Consumes(MediaType.APPLICATION_JSON)
public Response createUser(final @Context UriInfo uriInfo, final UserRepresentation rep) {
- auth.requireManage();
+ auth.users().requireManage();
// Double-check duplicated username and email here due to federation
if (session.users().getUserByUsername(rep.getUsername(), realm) != null) {
@@ -195,7 +162,7 @@ public class UsersResource {
try {
UserModel user = session.users().addUser(realm, rep.getUsername());
Set<String> emptySet = Collections.emptySet();
- updateUserFromRep(user, rep, emptySet, realm, session, false);
+ UserResource.updateUserFromRep(user, rep, emptySet, realm, session, false);
adminEvent.operation(OperationType.CREATE).resourcePath(uriInfo, user.getId()).representation(rep).success();
@@ -217,45 +184,6 @@ public class UsersResource {
return ErrorResponse.exists("Could not create user");
}
}
-
- public static void updateUserFromRep(UserModel user, UserRepresentation rep, Set<String> attrsToRemove, RealmModel realm, KeycloakSession session, boolean removeMissingRequiredActions) {
- if (rep.getUsername() != null && realm.isEditUsernameAllowed()) {
- user.setUsername(rep.getUsername());
- }
- if (rep.getEmail() != null) user.setEmail(rep.getEmail());
- if (rep.getFirstName() != null) user.setFirstName(rep.getFirstName());
- if (rep.getLastName() != null) user.setLastName(rep.getLastName());
-
- if (rep.isEnabled() != null) user.setEnabled(rep.isEnabled());
- if (rep.isEmailVerified() != null) user.setEmailVerified(rep.isEmailVerified());
-
- List<String> reqActions = rep.getRequiredActions();
-
- if (reqActions != null) {
- Set<String> allActions = new HashSet<>();
- for (ProviderFactory factory : session.getKeycloakSessionFactory().getProviderFactories(RequiredActionProvider.class)) {
- allActions.add(factory.getId());
- }
- for (String action : allActions) {
- if (reqActions.contains(action)) {
- user.addRequiredAction(action);
- } else if (removeMissingRequiredActions) {
- user.removeRequiredAction(action);
- }
- }
- }
-
- if (rep.getAttributes() != null) {
- for (Map.Entry<String, List<String>> attr : rep.getAttributes().entrySet()) {
- user.setAttribute(attr.getKey(), attr.getValue());
- }
-
- for (String attr : attrsToRemove) {
- user.removeAttribute(attr);
- }
- }
- }
-
/**
* Get representation of the user
*
@@ -263,368 +191,17 @@ public class UsersResource {
* @return
*/
@Path("{id}")
- @GET
- @NoCache
- @Produces(MediaType.APPLICATION_JSON)
- public UserRepresentation getUser(final @PathParam("id") String id) {
- auth.requireView();
-
- UserModel user = session.users().getUserById(id, realm);
- if (user == null) {
- throw new NotFoundException("User not found");
- }
-
- UserRepresentation rep = ModelToRepresentation.toRepresentation(session, realm, user);
-
- if (realm.isIdentityFederationEnabled()) {
- List<FederatedIdentityRepresentation> reps = getFederatedIdentities(user);
- rep.setFederatedIdentities(reps);
- }
-
- if (session.getProvider(BruteForceProtector.class).isTemporarilyDisabled(session, realm, user)) {
- rep.setEnabled(false);
- }
-
- return rep;
- }
-
- /**
- * Impersonate the user
- *
- * @param id User id
- * @return
- */
- @Path("{id}/impersonation")
- @POST
- @NoCache
- @Produces(MediaType.APPLICATION_JSON)
- public Map<String, Object> impersonate(final @PathParam("id") String id) {
- ProfileHelper.requireFeature(Profile.Feature.IMPERSONATION);
-
- auth.init(RealmAuth.Resource.IMPERSONATION);
- auth.requireManage();
-
- UserModel user = session.users().getUserById(id, realm);
- if (user == null) {
- throw new NotFoundException("User not found");
- }
- RealmModel authenticatedRealm = auth.getAuth().getRealm();
- // if same realm logout before impersonation
- boolean sameRealm = false;
- if (authenticatedRealm.getId().equals(realm.getId())) {
- sameRealm = true;
- UserSessionModel userSession = session.sessions().getUserSession(authenticatedRealm, auth.getAuth().getToken().getSessionState());
- AuthenticationManager.expireIdentityCookie(realm, uriInfo, clientConnection);
- AuthenticationManager.expireRememberMeCookie(realm, uriInfo, clientConnection);
- AuthenticationManager.backchannelLogout(session, authenticatedRealm, userSession, uriInfo, clientConnection, headers, true);
- }
- EventBuilder event = new EventBuilder(realm, session, clientConnection);
-
- String sessionId = KeycloakModelUtils.generateId();
- UserSessionModel userSession = session.sessions().createUserSession(sessionId, realm, user, user.getUsername(), clientConnection.getRemoteAddr(), "impersonate", false, null, null);
- AuthenticationManager.createLoginCookie(session, realm, userSession.getUser(), userSession, uriInfo, clientConnection);
- URI redirect = AccountService.accountServiceApplicationPage(uriInfo).build(realm.getName());
- Map<String, Object> result = new HashMap<>();
- result.put("sameRealm", sameRealm);
- result.put("redirect", redirect.toString());
- event.event(EventType.IMPERSONATE)
- .session(userSession)
- .user(user)
- .detail(Details.IMPERSONATOR_REALM,authenticatedRealm.getName())
- .detail(Details.IMPERSONATOR, auth.getAuth().getUser().getUsername()).success();
-
- return result;
- }
-
-
- /**
- * Get sessions associated with the user
- *
- * @param id User id
- * @return
- */
- @Path("{id}/sessions")
- @GET
- @NoCache
- @Produces(MediaType.APPLICATION_JSON)
- public List<UserSessionRepresentation> getSessions(final @PathParam("id") String id) {
- auth.requireView();
-
- UserModel user = session.users().getUserById(id, realm);
- if (user == null) {
- throw new NotFoundException("User not found");
- }
- List<UserSessionModel> sessions = session.sessions().getUserSessions(realm, user);
- List<UserSessionRepresentation> reps = new ArrayList<UserSessionRepresentation>();
- for (UserSessionModel session : sessions) {
- UserSessionRepresentation rep = ModelToRepresentation.toRepresentation(session);
- reps.add(rep);
- }
- return reps;
- }
-
- /**
- * Get offline sessions associated with the user and client
- *
- * @param id User id
- * @return
- */
- @Path("{id}/offline-sessions/{clientId}")
- @GET
- @NoCache
- @Produces(MediaType.APPLICATION_JSON)
- public List<UserSessionRepresentation> getOfflineSessions(final @PathParam("id") String id, final @PathParam("clientId") String clientId) {
- auth.requireView();
-
- UserModel user = session.users().getUserById(id, realm);
- if (user == null) {
- throw new NotFoundException("User not found");
- }
- ClientModel client = realm.getClientById(clientId);
- if (client == null) {
- throw new NotFoundException("Client not found");
- }
- List<UserSessionModel> sessions = new UserSessionManager(session).findOfflineSessions(realm, user);
- List<UserSessionRepresentation> reps = new ArrayList<UserSessionRepresentation>();
- for (UserSessionModel session : sessions) {
- UserSessionRepresentation rep = ModelToRepresentation.toRepresentation(session);
-
- // Update lastSessionRefresh with the timestamp from clientSession
- AuthenticatedClientSessionModel clientSession = session.getAuthenticatedClientSessions().get(clientId);
-
- // Skip if userSession is not for this client
- if (clientSession == null) {
- continue;
- }
-
- rep.setLastAccess(clientSession.getTimestamp());
-
- reps.add(rep);
- }
- return reps;
- }
-
- /**
- * Get social logins associated with the user
- *
- * @param id User id
- * @return
- */
- @Path("{id}/federated-identity")
- @GET
- @NoCache
- @Produces(MediaType.APPLICATION_JSON)
- public List<FederatedIdentityRepresentation> getFederatedIdentity(final @PathParam("id") String id) {
- auth.requireView();
-
- UserModel user = session.users().getUserById(id, realm);
- if (user == null) {
- throw new NotFoundException("User not found");
- }
-
- return getFederatedIdentities(user);
- }
-
- private List<FederatedIdentityRepresentation> getFederatedIdentities(UserModel user) {
- Set<FederatedIdentityModel> identities = session.users().getFederatedIdentities(user, realm);
- List<FederatedIdentityRepresentation> result = new ArrayList<FederatedIdentityRepresentation>();
-
- for (FederatedIdentityModel identity : identities) {
- for (IdentityProviderModel identityProviderModel : realm.getIdentityProviders()) {
- if (identityProviderModel.getAlias().equals(identity.getIdentityProvider())) {
- FederatedIdentityRepresentation rep = ModelToRepresentation.toRepresentation(identity);
- result.add(rep);
- }
- }
- }
- return result;
- }
-
- /**
- * Add a social login provider to the user
- *
- * @param id User id
- * @param provider Social login provider id
- * @param rep
- * @return
- */
- @Path("{id}/federated-identity/{provider}")
- @POST
- @NoCache
- public Response addFederatedIdentity(final @PathParam("id") String id, final @PathParam("provider") String provider, FederatedIdentityRepresentation rep) {
- auth.requireManage();
-
- UserModel user = session.users().getUserById(id, realm);
- if (user == null) {
- throw new NotFoundException("User not found");
- }
- if (session.users().getFederatedIdentity(user, provider, realm) != null) {
- return ErrorResponse.exists("User is already linked with provider");
- }
-
- FederatedIdentityModel socialLink = new FederatedIdentityModel(provider, rep.getUserId(), rep.getUserName());
- session.users().addFederatedIdentity(realm, user, socialLink);
- adminEvent.operation(OperationType.CREATE).resourcePath(uriInfo).representation(rep).success();
- return Response.noContent().build();
- }
-
- /**
- * Remove a social login provider from user
- *
- * @param id User id
- * @param provider Social login provider id
- */
- @Path("{id}/federated-identity/{provider}")
- @DELETE
- @NoCache
- public void removeFederatedIdentity(final @PathParam("id") String id, final @PathParam("provider") String provider) {
- auth.requireManage();
-
- UserModel user = session.users().getUserById(id, realm);
- if (user == null) {
- throw new NotFoundException("User not found");
- }
- if (!session.users().removeFederatedIdentity(realm, user, provider)) {
- throw new NotFoundException("Link not found");
- }
- adminEvent.operation(OperationType.DELETE).resourcePath(uriInfo).success();
- }
-
- /**
- * Get consents granted by the user
- *
- * @param id User id
- * @return
- */
- @Path("{id}/consents")
- @GET
- @NoCache
- @Produces(MediaType.APPLICATION_JSON)
- public List<Map<String, Object>> getConsents(final @PathParam("id") String id) {
- auth.requireView();
-
- UserModel user = session.users().getUserById(id, realm);
- if (user == null) {
- throw new NotFoundException("User not found");
- }
-
- List<Map<String, Object>> result = new LinkedList<>();
-
- Set<ClientModel> offlineClients = new UserSessionManager(session).findClientsWithOfflineToken(realm, user);
-
- for (ClientModel client : realm.getClients()) {
- UserConsentModel consent = session.users().getConsentByClient(realm, user.getId(), client.getId());
- boolean hasOfflineToken = offlineClients.contains(client);
-
- if (consent == null && !hasOfflineToken) {
- continue;
- }
-
- UserConsentRepresentation rep = (consent == null) ? null : ModelToRepresentation.toRepresentation(consent);
-
- Map<String, Object> currentRep = new HashMap<>();
- currentRep.put("clientId", client.getClientId());
- currentRep.put("grantedProtocolMappers", (rep==null ? Collections.emptyMap() : rep.getGrantedProtocolMappers()));
- currentRep.put("grantedRealmRoles", (rep==null ? Collections.emptyList() : rep.getGrantedRealmRoles()));
- currentRep.put("grantedClientRoles", (rep==null ? Collections.emptyMap() : rep.getGrantedClientRoles()));
- currentRep.put("createdDate", (rep==null ? null : rep.getCreatedDate()));
- currentRep.put("lastUpdatedDate", (rep==null ? null : rep.getLastUpdatedDate()));
-
- List<Map<String, String>> additionalGrants = new LinkedList<>();
- if (hasOfflineToken) {
- Map<String, String> offlineTokens = new HashMap<>();
- offlineTokens.put("client", client.getId());
- // TODO: translate
- offlineTokens.put("key", "Offline Token");
- additionalGrants.add(offlineTokens);
- }
- currentRep.put("additionalGrants", additionalGrants);
-
- result.add(currentRep);
- }
-
- return result;
- }
-
- /**
- * Revoke consent and offline tokens for particular client from user
- *
- * @param id User id
- * @param clientId Client id
- */
- @Path("{id}/consents/{client}")
- @DELETE
- @NoCache
- public void revokeConsent(final @PathParam("id") String id, final @PathParam("client") String clientId) {
- auth.requireManage();
-
- UserModel user = session.users().getUserById(id, realm);
- if (user == null) {
- throw new NotFoundException("User not found");
- }
-
- ClientModel client = realm.getClientByClientId(clientId);
- boolean revokedConsent = session.users().revokeConsentForClient(realm, user.getId(), client.getId());
- boolean revokedOfflineToken = new UserSessionManager(session).revokeOfflineToken(user, client);
-
- if (revokedConsent) {
- // Logout clientSessions for this user and client
- AuthenticationManager.backchannelUserFromClient(session, realm, user, client, uriInfo, headers);
- }
-
- if (!revokedConsent && !revokedOfflineToken) {
- throw new NotFoundException("Consent nor offline token not found");
- }
- adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo).success();
- }
-
- /**
- * Remove all user sessions associated with the user
- *
- * Also send notification to all clients that have an admin URL to invalidate the sessions for the particular user.
- *
- * @param id User id
- */
- @Path("{id}/logout")
- @POST
- public void logout(final @PathParam("id") String id) {
- auth.requireManage();
-
- UserModel user = session.users().getUserById(id, realm);
- if (user == null) {
- throw new NotFoundException("User not found");
- }
-
- List<UserSessionModel> userSessions = session.sessions().getUserSessions(realm, user);
- for (UserSessionModel userSession : userSessions) {
- AuthenticationManager.backchannelLogout(session, realm, userSession, uriInfo, clientConnection, headers, true);
- }
- adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo).success();
- }
-
- /**
- * Delete the user
- *
- * @param id User id
- */
- @Path("{id}")
- @DELETE
- @NoCache
- public Response deleteUser(final @PathParam("id") String id) {
- auth.requireManage();
-
+ public UserResource user(final @PathParam("id") String id) {
UserModel user = session.users().getUserById(id, realm);
if (user == null) {
- throw new NotFoundException("User not found");
- }
-
- boolean removed = new UserManager(session).removeUser(realm, user);
- if (removed) {
- adminEvent.operation(OperationType.DELETE).resourcePath(uriInfo).success();
- return Response.noContent().build();
- } else {
- return ErrorResponse.error("User couldn't be deleted", Response.Status.BAD_REQUEST);
+ // we do this to make sure somebody can't phish ids
+ if (auth.users().canQuery()) throw new NotFoundException("User not found");
+ else throw new ForbiddenException();
}
+ UserResource resource = new UserResource(realm, user, auth, adminEvent);
+ ResteasyProviderFactory.getInstance().injectProperties(resource);
+ //resourceContext.initResource(users);
+ return resource;
}
/**
@@ -651,7 +228,7 @@ public class UsersResource {
@QueryParam("username") String username,
@QueryParam("first") Integer firstResult,
@QueryParam("max") Integer maxResults) {
- auth.requireView();
+ auth.users().requireQuery();
firstResult = firstResult != null ? firstResult : -1;
maxResults = maxResults != null ? maxResults : Constants.DEFAULT_MAX_RESULTS;
@@ -679,8 +256,12 @@ public class UsersResource {
userModels = session.users().getUsers(realm, firstResult, maxResults, false);
}
+ boolean canViewGlobal = auth.users().canView();
for (UserModel user : userModels) {
- results.add(ModelToRepresentation.toRepresentation(session, realm, user));
+ if (!canViewGlobal && !auth.users().canView(user)) continue;
+ UserRepresentation userRep = ModelToRepresentation.toRepresentation(session, realm, user);
+ userRep.setAccess(auth.users().getAccess(user));
+ results.add(userRep);
}
return results;
}
@@ -690,311 +271,8 @@ public class UsersResource {
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public Integer getUsersCount() {
- auth.requireView();
+ auth.users().requireView();
return session.users().getUsersCount(realm);
}
-
- @Path("{id}/role-mappings")
- public RoleMapperResource getRoleMappings(@PathParam("id") String id) {
- auth.init(RealmAuth.Resource.USER);
-
- UserModel user = session.users().getUserById(id, realm);
-
- RoleMapperResource resource = new RoleMapperResource(realm, auth, user, adminEvent);
- ResteasyProviderFactory.getInstance().injectProperties(resource);
- return resource;
-
- }
-
- /**
- * Disable all credentials for a user of a specific type
- *
- * @param id
- * @param credentialTypes
- */
- @Path("{id}/disable-credential-types")
- @PUT
- @Consumes(MediaType.APPLICATION_JSON)
- public void disableCredentialType(@PathParam("id") String id, List<String> credentialTypes) {
- auth.requireManage();
-
- UserModel user = session.users().getUserById(id, realm);
- if (user == null) {
- throw new NotFoundException("User not found");
- }
- if (credentialTypes == null) return;
- for (String type : credentialTypes) {
- session.userCredentialManager().disableCredentialType(realm, user, type);
-
- }
-
-
- }
-
- /**
- * Set up a temporary password for the user
- *
- * User will have to reset the temporary password next time they log in.
- *
- * @param id User id
- * @param pass A Temporary password
- */
- @Path("{id}/reset-password")
- @PUT
- @Consumes(MediaType.APPLICATION_JSON)
- public void resetPassword(@PathParam("id") String id, CredentialRepresentation pass) {
- auth.requireManage();
-
- UserModel user = session.users().getUserById(id, realm);
- if (user == null) {
- throw new NotFoundException("User not found");
- }
- if (pass == null || pass.getValue() == null || !CredentialRepresentation.PASSWORD.equals(pass.getType())) {
- throw new BadRequestException("No password provided");
- }
- if (Validation.isBlank(pass.getValue())) {
- throw new BadRequestException("Empty password not allowed");
- }
-
- UserCredentialModel cred = UserCredentialModel.password(pass.getValue(), true);
- try {
- session.userCredentialManager().updateCredential(realm, user, cred);
- } catch (IllegalStateException ise) {
- throw new BadRequestException("Resetting to N old passwords is not allowed.");
- } catch (ReadOnlyException mre) {
- throw new BadRequestException("Can't reset password as account is read only");
- } catch (ModelException e) {
- Properties messages = AdminRoot.getMessages(session, realm, auth.getAuth().getToken().getLocale());
- throw new ErrorResponseException(e.getMessage(), MessageFormat.format(messages.getProperty(e.getMessage(), e.getMessage()), e.getParameters()),
- Status.BAD_REQUEST);
- }
- if (pass.isTemporary() != null && pass.isTemporary()) user.addRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD);
-
- adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo).success();
- }
-
- /**
- * Remove TOTP from the user
- *
- * @param id User id
- */
- @Path("{id}/remove-totp")
- @PUT
- @Consumes(MediaType.APPLICATION_JSON)
- public void removeTotp(@PathParam("id") String id) {
- auth.requireManage();
-
- UserModel user = session.users().getUserById(id, realm);
- if (user == null) {
- throw new NotFoundException("User not found");
- }
-
- session.userCredentialManager().disableCredentialType(realm, user, CredentialModel.OTP);
- adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo).success();
- }
-
- /**
- * Send an email to the user with a link they can click to reset their password.
- * The redirectUri and clientId parameters are optional. The default for the
- * redirect is the account client.
- *
- * This endpoint has been deprecated. Please use the execute-actions-email passing a list with
- * UPDATE_PASSWORD within it.
- *
- * @param id
- * @param redirectUri redirect uri
- * @param clientId client id
- * @return
- */
- @Deprecated
- @Path("{id}/reset-password-email")
- @PUT
- @Consumes(MediaType.APPLICATION_JSON)
- public Response resetPasswordEmail(@PathParam("id") String id,
- @QueryParam(OIDCLoginProtocol.REDIRECT_URI_PARAM) String redirectUri,
- @QueryParam(OIDCLoginProtocol.CLIENT_ID_PARAM) String clientId) {
- List<String> actions = new LinkedList<>();
- actions.add(UserModel.RequiredAction.UPDATE_PASSWORD.name());
- return executeActionsEmail(id, redirectUri, clientId, null, actions);
- }
-
-
- /**
- * Send a update account email to the user
- *
- * An email contains a link the user can click to perform a set of required actions.
- * The redirectUri and clientId parameters are optional. If no redirect is given, then there will
- * be no link back to click after actions have completed. Redirect uri must be a valid uri for the
- * particular clientId.
- *
- * @param id User is
- * @param redirectUri Redirect uri
- * @param clientId Client id
- * @param lifespan Number of seconds after which the generated token expires
- * @param actions required actions the user needs to complete
- * @return
- */
- @Path("{id}/execute-actions-email")
- @PUT
- @Consumes(MediaType.APPLICATION_JSON)
- public Response executeActionsEmail(@PathParam("id") String id,
- @QueryParam(OIDCLoginProtocol.REDIRECT_URI_PARAM) String redirectUri,
- @QueryParam(OIDCLoginProtocol.CLIENT_ID_PARAM) String clientId,
- @QueryParam("lifespan") Integer lifespan,
- List<String> actions) {
- auth.requireManage();
-
- UserModel user = session.users().getUserById(id, realm);
- if (user == null) {
- return ErrorResponse.error("User not found", Response.Status.NOT_FOUND);
- }
-
- if (user.getEmail() == null) {
- return ErrorResponse.error("User email missing", Response.Status.BAD_REQUEST);
- }
-
- if (!user.isEnabled()) {
- throw new WebApplicationException(
- ErrorResponse.error("User is disabled", Response.Status.BAD_REQUEST));
- }
-
- if (redirectUri != null && clientId == null) {
- throw new WebApplicationException(
- ErrorResponse.error("Client id missing", Response.Status.BAD_REQUEST));
- }
-
- if (clientId == null) {
- clientId = Constants.ACCOUNT_MANAGEMENT_CLIENT_ID;
- }
-
- ClientModel client = realm.getClientByClientId(clientId);
- if (client == null || !client.isEnabled()) {
- throw new WebApplicationException(
- ErrorResponse.error(clientId + " not enabled", Response.Status.BAD_REQUEST));
- }
-
- String redirect;
- if (redirectUri != null) {
- redirect = RedirectUtils.verifyRedirectUri(uriInfo, redirectUri, realm, client);
- if (redirect == null) {
- throw new WebApplicationException(
- ErrorResponse.error("Invalid redirect uri.", Response.Status.BAD_REQUEST));
- }
- }
-
- if (lifespan == null) {
- lifespan = realm.getActionTokenGeneratedByAdminLifespan();
- }
- int expiration = Time.currentTime() + lifespan;
- ExecuteActionsActionToken token = new ExecuteActionsActionToken(id, expiration, actions, redirectUri, clientId);
-
- try {
- UriBuilder builder = LoginActionsService.actionTokenProcessor(uriInfo);
- builder.queryParam("key", token.serialize(session, realm, uriInfo));
-
- String link = builder.build(realm.getName()).toString();
-
- this.session.getProvider(EmailTemplateProvider.class)
- .setRealm(realm)
- .setUser(user)
- .sendExecuteActions(link, TimeUnit.SECONDS.toMinutes(lifespan));
-
- //audit.user(user).detail(Details.EMAIL, user.getEmail()).detail(Details.CODE_ID, accessCode.getCodeId()).success();
-
- adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo).success();
-
- return Response.ok().build();
- } catch (EmailException e) {
- ServicesLogger.LOGGER.failedToSendActionsEmail(e);
- return ErrorResponse.error("Failed to send execute actions email", Response.Status.INTERNAL_SERVER_ERROR);
- }
- }
-
- /**
- * Send an email-verification email to the user
- *
- * An email contains a link the user can click to verify their email address.
- * The redirectUri and clientId parameters are optional. The default for the
- * redirect is the account client.
- *
- * @param id User id
- * @param redirectUri Redirect uri
- * @param clientId Client id
- * @return
- */
- @Path("{id}/send-verify-email")
- @PUT
- @Consumes(MediaType.APPLICATION_JSON)
- public Response sendVerifyEmail(@PathParam("id") String id, @QueryParam(OIDCLoginProtocol.REDIRECT_URI_PARAM) String redirectUri, @QueryParam(OIDCLoginProtocol.CLIENT_ID_PARAM) String clientId) {
- List<String> actions = new LinkedList<>();
- actions.add(UserModel.RequiredAction.VERIFY_EMAIL.name());
- return executeActionsEmail(id, redirectUri, clientId, null, actions);
- }
-
- @GET
- @Path("{id}/groups")
- @NoCache
- @Produces(MediaType.APPLICATION_JSON)
- public List<GroupRepresentation> groupMembership(@PathParam("id") String id) {
- auth.requireView();
-
- UserModel user = session.users().getUserById(id, realm);
- if (user == null) {
- throw new NotFoundException("User not found");
- }
- List<GroupRepresentation> memberships = new LinkedList<>();
- for (GroupModel group : user.getGroups()) {
- memberships.add(ModelToRepresentation.toRepresentation(group, false));
- }
- return memberships;
- }
-
- @DELETE
- @Path("{id}/groups/{groupId}")
- @NoCache
- public void removeMembership(@PathParam("id") String id, @PathParam("groupId") String groupId) {
- auth.requireManage();
-
- UserModel user = session.users().getUserById(id, realm);
- if (user == null) {
- throw new NotFoundException("User not found");
- }
- GroupModel group = session.realms().getGroupById(groupId, realm);
- if (group == null) {
- throw new NotFoundException("Group not found");
- }
-
- try {
- if (user.isMemberOf(group)){
- user.leaveGroup(group);
- adminEvent.operation(OperationType.DELETE).resource(ResourceType.GROUP_MEMBERSHIP).representation(ModelToRepresentation.toRepresentation(group, true)).resourcePath(uriInfo).success();
- }
- } catch (ModelException me) {
- Properties messages = AdminRoot.getMessages(session, realm, auth.getAuth().getToken().getLocale());
- throw new ErrorResponseException(me.getMessage(), MessageFormat.format(messages.getProperty(me.getMessage(), me.getMessage()), me.getParameters()),
- Response.Status.BAD_REQUEST);
- }
- }
-
- @PUT
- @Path("{id}/groups/{groupId}")
- @NoCache
- public void joinGroup(@PathParam("id") String id, @PathParam("groupId") String groupId) {
- auth.requireManage();
-
- UserModel user = session.users().getUserById(id, realm);
- if (user == null) {
- throw new NotFoundException("User not found");
- }
- GroupModel group = session.realms().getGroupById(groupId, realm);
- if (group == null) {
- throw new NotFoundException("Group not found");
- }
- if (!user.isMemberOf(group)){
- user.joinGroup(group);
- adminEvent.operation(OperationType.CREATE).resource(ResourceType.GROUP_MEMBERSHIP).representation(ModelToRepresentation.toRepresentation(group, true)).resourcePath(uriInfo).success();
- }
- }
-
}
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/UserStorageProviderResource.java b/services/src/main/java/org/keycloak/services/resources/admin/UserStorageProviderResource.java
index 4ffcf86..638f57b 100644
--- a/services/src/main/java/org/keycloak/services/resources/admin/UserStorageProviderResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/UserStorageProviderResource.java
@@ -26,6 +26,7 @@ import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.services.ServicesLogger;
import org.keycloak.services.managers.UserStorageSyncManager;
+import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
import org.keycloak.storage.UserStorageProvider;
import org.keycloak.storage.UserStorageProviderModel;
import org.keycloak.storage.ldap.LDAPStorageProvider;
@@ -55,7 +56,7 @@ public class UserStorageProviderResource {
protected RealmModel realm;
- protected RealmAuth auth;
+ protected AdminPermissionEvaluator auth;
protected AdminEventBuilder adminEvent;
@@ -71,12 +72,10 @@ public class UserStorageProviderResource {
@Context
protected HttpHeaders headers;
- public UserStorageProviderResource(RealmModel realm, RealmAuth auth, AdminEventBuilder adminEvent) {
+ public UserStorageProviderResource(RealmModel realm, AdminPermissionEvaluator auth, AdminEventBuilder adminEvent) {
this.auth = auth;
this.realm = realm;
this.adminEvent = adminEvent;
-
- auth.init(RealmAuth.Resource.USER);
}
/**
@@ -94,7 +93,7 @@ public class UserStorageProviderResource {
@Produces(MediaType.APPLICATION_JSON)
public SynchronizationResult syncUsers(@PathParam("id") String id,
@QueryParam("action") String action) {
- auth.requireManage();
+ auth.users().requireManage();
ComponentModel model = realm.getComponent(id);
if (model == null) {
@@ -139,7 +138,7 @@ public class UserStorageProviderResource {
@Path("{id}/remove-imported-users")
@NoCache
public void removeImportedUsers(@PathParam("id") String id) {
- auth.requireManage();
+ auth.users().requireManage();
ComponentModel model = realm.getComponent(id);
if (model == null) {
@@ -162,7 +161,7 @@ public class UserStorageProviderResource {
@Path("{id}/unlink-users")
@NoCache
public void unlinkUsers(@PathParam("id") String id) {
- auth.requireManage();
+ auth.users().requireManage();
ComponentModel model = realm.getComponent(id);
if (model == null) {
@@ -187,7 +186,7 @@ public class UserStorageProviderResource {
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public SynchronizationResult syncMapperData(@PathParam("parentId") String parentId, @PathParam("id") String mapperId, @QueryParam("direction") String direction) {
- auth.requireManage();
+ auth.users().requireManage();
ComponentModel parentModel = realm.getComponent(parentId);
if (parentModel == null) throw new NotFoundException("Parent model not found");
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/AdminClientUtil.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/AdminClientUtil.java
index a6f42c8..7dc4d6e 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/AdminClientUtil.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/AdminClientUtil.java
@@ -43,6 +43,14 @@ import static org.keycloak.testsuite.util.IOUtil.PROJECT_BUILD_DIRECTORY;
public class AdminClientUtil {
public static Keycloak createAdminClient(boolean ignoreUnknownProperties, String authServerContextRoot) throws Exception {
+ return createAdminClient(ignoreUnknownProperties, authServerContextRoot, MASTER, ADMIN, ADMIN, Constants.ADMIN_CLI_CLIENT_ID, null);
+
+ }
+ public static Keycloak createAdminClient(boolean ignoreUnknownProperties, String realmName, String username, String password, String clientId, String clientSecret) throws CertificateException, NoSuchAlgorithmException, KeyStoreException, IOException, KeyManagementException {
+ return createAdminClient(ignoreUnknownProperties, AuthServerTestEnricher.getAuthServerContextRoot(), realmName, username, password, clientId, clientSecret);
+ }
+
+ public static Keycloak createAdminClient(boolean ignoreUnknownProperties, String authServerContextRoot, String realmName, String username, String password, String clientId, String clientSecret) throws CertificateException, NoSuchAlgorithmException, KeyStoreException, IOException, KeyManagementException {
SSLContext ssl = null;
if ("true".equals(System.getProperty("auth.server.ssl.required"))) {
File trustore = new File(PROJECT_BUILD_DIRECTORY, "dependency/keystore/keycloak.truststore");
@@ -63,13 +71,17 @@ public class AdminClientUtil {
}
return Keycloak.getInstance(authServerContextRoot + "/auth",
- MASTER, ADMIN, ADMIN, Constants.ADMIN_CLI_CLIENT_ID, null, ssl, jacksonProvider);
+ realmName, username, password, clientId, clientSecret, ssl, jacksonProvider);
}
public static Keycloak createAdminClient() throws Exception {
return createAdminClient(false, AuthServerTestEnricher.getAuthServerContextRoot());
}
+ public static Keycloak createAdminClient(boolean ignoreUnknownProperties) throws Exception {
+ return createAdminClient(ignoreUnknownProperties, AuthServerTestEnricher.getAuthServerContextRoot());
+ }
+
private static SSLContext getSSLContextWithTrustore(File file, String password) throws CertificateException, NoSuchAlgorithmException, KeyStoreException, IOException, KeyManagementException {
if (!file.isFile()) {
throw new RuntimeException("Truststore file not found: " + file.getAbsolutePath());
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractKeycloakTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractKeycloakTest.java
index 1739370..262d0b2 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractKeycloakTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractKeycloakTest.java
@@ -167,6 +167,7 @@ public abstract class AbstractKeycloakTest {
removeRealm(testRealm.getRealm());
}
} else {
+ log.info("calling all TestCleanup");
// Logout all users after the test
List<RealmRepresentation> realms = testContext.getTestRealmReps();
for (RealmRepresentation realm : realms) {
@@ -175,7 +176,12 @@ public abstract class AbstractKeycloakTest {
// Cleanup objects
for (TestCleanup cleanup : testContext.getCleanups().values()) {
- cleanup.executeCleanup();
+ try {
+ if (cleanup != null) cleanup.executeCleanup();
+ } catch (Exception e) {
+ log.error("failed cleanup!", e);
+ throw new RuntimeException(e);
+ }
}
testContext.getCleanups().clear();
}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ClientTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ClientTest.java
index 257eb7d..9b001ac 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ClientTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ClientTest.java
@@ -479,8 +479,8 @@ public class ClientTest extends AbstractAdminTest {
// Create foo mapper
ProtocolMapperRepresentation fooMapper = new ProtocolMapperRepresentation();
fooMapper.setName("foo");
- fooMapper.setProtocol("fooProtocol");
- fooMapper.setProtocolMapper("fooMapper");
+ fooMapper.setProtocol("openid-connect");
+ fooMapper.setProtocolMapper("oidc-hardcoded-claim-mapper");
fooMapper.setConsentRequired(true);
Response response = mappersResource.createMapper(fooMapper);
String location = response.getLocation().toString();
@@ -493,13 +493,13 @@ public class ClientTest extends AbstractAdminTest {
assertEquals(fooMapper.getName(), "foo");
// Update foo mapper
- fooMapper.setProtocolMapper("foo-mapper-updated");
+ fooMapper.setConsentRequired(false);
mappersResource.update(fooMapperId, fooMapper);
assertAdminEvents.assertEvent(realmId, OperationType.UPDATE, AdminEventPaths.clientProtocolMapperPath(clientDbId, fooMapperId), fooMapper, ResourceType.PROTOCOL_MAPPER);
fooMapper = mappersResource.getMapperById(fooMapperId);
- assertEquals(fooMapper.getProtocolMapper(), "foo-mapper-updated");
+ assertFalse(fooMapper.isConsentRequired());
// Remove foo mapper
mappersResource.delete(fooMapperId);
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/FineGrainAdminUnitTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/FineGrainAdminUnitTest.java
new file mode 100644
index 0000000..8386aa6
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/FineGrainAdminUnitTest.java
@@ -0,0 +1,705 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.testsuite.admin;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.keycloak.admin.client.Keycloak;
+import org.keycloak.authorization.AuthorizationProvider;
+import org.keycloak.authorization.AuthorizationProviderFactory;
+import org.keycloak.authorization.model.Resource;
+import org.keycloak.models.ClientTemplateModel;
+import org.keycloak.models.GroupModel;
+import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.models.utils.RepresentationToModel;
+import org.keycloak.representations.idm.ClientTemplateRepresentation;
+import org.keycloak.representations.idm.authorization.Logic;
+import org.keycloak.representations.idm.authorization.UserPolicyRepresentation;
+import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
+import org.keycloak.services.resources.admin.permissions.AdminPermissionManagement;
+import org.keycloak.services.resources.admin.permissions.AdminPermissions;
+import org.keycloak.authorization.model.Policy;
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.models.AdminRoles;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.Constants;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleModel;
+import org.keycloak.models.UserCredentialModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.representations.idm.RoleRepresentation;
+import org.keycloak.representations.idm.UserRepresentation;
+import org.keycloak.representations.idm.authorization.DecisionStrategy;
+import org.keycloak.testsuite.AbstractKeycloakTest;
+import org.keycloak.testsuite.util.AdminClientUtil;
+
+import javax.ws.rs.ClientErrorException;
+import java.util.LinkedList;
+import java.util.List;
+
+import static org.keycloak.testsuite.auth.page.AuthRealm.TEST;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+//@Ignore
+public class FineGrainAdminUnitTest extends AbstractKeycloakTest {
+
+ public static final String CLIENT_NAME = "application";
+
+ @Override
+ public void addTestRealms(List<RealmRepresentation> testRealms) {
+ RealmRepresentation testRealmRep = new RealmRepresentation();
+ testRealmRep.setId(TEST);
+ testRealmRep.setRealm(TEST);
+ testRealmRep.setEnabled(true);
+ testRealms.add(testRealmRep);
+ }
+ public static void setupDemo(KeycloakSession session) {
+ RealmModel realm = session.realms().getRealmByName(TEST);
+ ClientModel client = realm.addClient("sales-pipeline-application");
+ RoleModel clientAdmin = client.addRole("admin");
+ client.addRole("leader-creator");
+ client.addRole("viewLeads");
+ ClientModel client2 = realm.addClient("market-analysis-application");
+ RoleModel client2Admin = client2.addRole("admin");
+ client2.addRole("market-manager");
+ client2.addRole("viewMarkets");
+ GroupModel sales = realm.createGroup("sales");
+ RoleModel salesAppsAdminRole = realm.addRole("sales-apps-admin");
+ salesAppsAdminRole.addCompositeRole(clientAdmin);
+ salesAppsAdminRole.addCompositeRole(client2Admin);
+ ClientModel realmManagementClient = realm.getClientByClientId("realm-management");
+ RoleModel queryClient = realmManagementClient.getRole(AdminRoles.QUERY_CLIENTS);
+
+
+ UserModel admin = session.users().addUser(realm, "salesManager");
+ admin.setEnabled(true);
+ session.userCredentialManager().updateCredential(realm, admin, UserCredentialModel.password("password"));
+ admin = session.users().addUser(realm, "sales-group-admin");
+ admin.setEnabled(true);
+ session.userCredentialManager().updateCredential(realm, admin, UserCredentialModel.password("password"));
+ admin = session.users().addUser(realm, "sales-it");
+ admin.setEnabled(true);
+ session.userCredentialManager().updateCredential(realm, admin, UserCredentialModel.password("password"));
+ admin = session.users().addUser(realm, "sales-pipeline-admin");
+ admin.setEnabled(true);
+ session.userCredentialManager().updateCredential(realm, admin, UserCredentialModel.password("password"));
+ admin = session.users().addUser(realm, "client-admin");
+ admin.setEnabled(true);
+ admin.grantRole(queryClient);
+ session.userCredentialManager().updateCredential(realm, admin, UserCredentialModel.password("password"));
+
+ UserModel user = session.users().addUser(realm, "salesman");
+ user.setEnabled(true);
+ user.joinGroup(sales);
+
+ user = session.users().addUser(realm, "saleswoman");
+ user.setEnabled(true);
+
+ }
+
+ public static void setupPolices(KeycloakSession session) {
+ RealmModel realm = session.realms().getRealmByName(TEST);
+ AdminPermissionManagement permissions = AdminPermissions.management(session, realm);
+ RoleModel realmRole = realm.addRole("realm-role");
+ RoleModel realmRole2 = realm.addRole("realm-role2");
+ ClientModel client1 = realm.addClient(CLIENT_NAME);
+ ClientTemplateModel template = realm.addClientTemplate("template");
+ client1.setFullScopeAllowed(false);
+ RoleModel client1Role = client1.addRole("client-role");
+ GroupModel group = realm.createGroup("top");
+
+ RoleModel mapperRole = realm.addRole("mapper");
+ RoleModel managerRole = realm.addRole("manager");
+ RoleModel compositeRole = realm.addRole("composite-role");
+ compositeRole.addCompositeRole(mapperRole);
+ compositeRole.addCompositeRole(managerRole);
+
+ // realm-role and application.client-role will have a role policy associated with their map-role permission
+ {
+ permissions.roles().setPermissionsEnabled(client1Role, true);
+ Policy mapRolePermission = permissions.roles().mapRolePermission(client1Role);
+ ResourceServer server = permissions.roles().resourceServer(client1Role);
+ Policy mapperPolicy = permissions.roles().rolePolicy(server, mapperRole);
+ mapRolePermission.addAssociatedPolicy(mapperPolicy);
+ }
+
+ {
+ permissions.roles().setPermissionsEnabled(realmRole, true);
+ Policy mapRolePermission = permissions.roles().mapRolePermission(realmRole);
+ ResourceServer server = permissions.roles().resourceServer(realmRole);
+ Policy mapperPolicy = permissions.roles().rolePolicy(server, mapperRole);
+ mapRolePermission.addAssociatedPolicy(mapperPolicy);
+ }
+
+ // realmRole2 will have an empty map-role policy
+ {
+ permissions.roles().setPermissionsEnabled(realmRole2, true);
+ }
+
+ // setup Users manage policies
+ {
+ permissions.users().setPermissionsEnabled(true);
+ ResourceServer server = permissions.realmResourceServer();
+ Policy managerPolicy = permissions.roles().rolePolicy(server, managerRole);
+ Policy permission = permissions.users().managePermission();
+ permission.addAssociatedPolicy(managerPolicy);
+ permission.setDecisionStrategy(DecisionStrategy.AFFIRMATIVE);
+ }
+ {
+ permissions.groups().setPermissionsEnabled(group, true);
+ }
+ {
+ permissions.clients().setPermissionsEnabled(client1, true);
+ }
+ // setup Users impersonate policy
+ {
+ ClientModel realmManagementClient = realm.getClientByClientId("realm-management");
+ RoleModel adminRole = realmManagementClient.getRole(AdminRoles.REALM_ADMIN);
+ permissions.users().setPermissionsEnabled(true);
+ ResourceServer server = permissions.realmResourceServer();
+ Policy adminPolicy = permissions.roles().rolePolicy(server, adminRole);
+ adminPolicy.setLogic(Logic.NEGATIVE);
+ Policy permission = permissions.users().userImpersonatedPermission();
+ permission.addAssociatedPolicy(adminPolicy);
+ permission.setDecisionStrategy(DecisionStrategy.UNANIMOUS);
+ }
+
+
+ }
+
+ public static void setupUsers(KeycloakSession session) {
+ RealmModel realm = session.realms().getRealmByName(TEST);
+ ClientModel client = realm.getClientByClientId(CLIENT_NAME);
+ RoleModel realmRole = realm.getRole("realm-role");
+ RoleModel realmRole2 = realm.getRole("realm-role2");
+ RoleModel clientRole = client.getRole("client-role");
+ RoleModel mapperRole = realm.getRole("mapper");
+ RoleModel managerRole = realm.getRole("manager");
+ RoleModel compositeRole = realm.getRole("composite-role");
+ ClientModel realmManagementClient = realm.getClientByClientId("realm-management");
+ RoleModel adminRole = realmManagementClient.getRole(AdminRoles.REALM_ADMIN);
+ RoleModel queryGroupsRole = realmManagementClient.getRole(AdminRoles.QUERY_GROUPS);
+ RoleModel queryUsersRole = realmManagementClient.getRole(AdminRoles.QUERY_USERS);
+ RoleModel queryClientsRole = realmManagementClient.getRole(AdminRoles.QUERY_CLIENTS);
+
+ UserModel nomapAdmin = session.users().addUser(realm, "nomap-admin");
+ nomapAdmin.setEnabled(true);
+ session.userCredentialManager().updateCredential(realm, nomapAdmin, UserCredentialModel.password("password"));
+ nomapAdmin.grantRole(adminRole);
+
+ UserModel anotherAdmin = session.users().addUser(realm, "anotherAdmin");
+ anotherAdmin.setEnabled(true);
+ session.userCredentialManager().updateCredential(realm, anotherAdmin, UserCredentialModel.password("password"));
+ anotherAdmin.grantRole(adminRole);
+
+ UserModel authorizedUser = session.users().addUser(realm, "authorized");
+ authorizedUser.setEnabled(true);
+ session.userCredentialManager().updateCredential(realm, authorizedUser, UserCredentialModel.password("password"));
+ authorizedUser.grantRole(mapperRole);
+ authorizedUser.grantRole(managerRole);
+
+ UserModel authorizedComposite = session.users().addUser(realm, "authorizedComposite");
+ authorizedComposite.setEnabled(true);
+ session.userCredentialManager().updateCredential(realm, authorizedComposite, UserCredentialModel.password("password"));
+ authorizedComposite.grantRole(compositeRole);
+
+ UserModel unauthorizedUser = session.users().addUser(realm, "unauthorized");
+ unauthorizedUser.setEnabled(true);
+ session.userCredentialManager().updateCredential(realm, unauthorizedUser, UserCredentialModel.password("password"));
+
+ UserModel unauthorizedMapper = session.users().addUser(realm, "unauthorizedMapper");
+ unauthorizedMapper.setEnabled(true);
+ session.userCredentialManager().updateCredential(realm, unauthorizedMapper, UserCredentialModel.password("password"));
+ unauthorizedMapper.grantRole(managerRole);
+
+ UserModel user1 = session.users().addUser(realm, "user1");
+ user1.setEnabled(true);
+
+ // group management
+ AdminPermissionManagement permissions = AdminPermissions.management(session, realm);
+
+ GroupModel group = KeycloakModelUtils.findGroupByPath(realm, "top");
+ UserModel groupMember = session.users().addUser(realm, "groupMember");
+ groupMember.joinGroup(group);
+ groupMember.setEnabled(true);
+ UserModel groupManager = session.users().addUser(realm, "groupManager");
+ groupManager.grantRole(queryGroupsRole);
+ groupManager.grantRole(queryUsersRole);
+ groupManager.setEnabled(true);
+ groupManager.grantRole(mapperRole);
+ session.userCredentialManager().updateCredential(realm, groupManager, UserCredentialModel.password("password"));
+
+ UserModel groupManagerNoMapper = session.users().addUser(realm, "noMapperGroupManager");
+ groupManagerNoMapper.setEnabled(true);
+ session.userCredentialManager().updateCredential(realm, groupManagerNoMapper, UserCredentialModel.password("password"));
+ groupManagerNoMapper.grantRole(queryGroupsRole);
+ groupManagerNoMapper.grantRole(queryUsersRole);
+
+ UserPolicyRepresentation groupManagerRep = new UserPolicyRepresentation();
+ groupManagerRep.setName("groupManagers");
+ groupManagerRep.addUser("groupManager");
+ groupManagerRep.addUser("noMapperGroupManager");
+ ResourceServer server = permissions.realmResourceServer();
+ Policy groupManagerPolicy = permissions.authz().getStoreFactory().getPolicyStore().create(groupManagerRep, server);
+ Policy groupManagerPermission = permissions.groups().manageMembersPermission(group);
+ groupManagerPermission.addAssociatedPolicy(groupManagerPolicy);
+ permissions.groups().viewPermission(group).addAssociatedPolicy(groupManagerPolicy);
+
+ UserModel clientMapper = session.users().addUser(realm, "clientMapper");
+ clientMapper.setEnabled(true);
+ clientMapper.grantRole(managerRole);
+ clientMapper.grantRole(queryUsersRole);
+ session.userCredentialManager().updateCredential(realm, clientMapper, UserCredentialModel.password("password"));
+ Policy clientMapperPolicy = permissions.clients().mapRolesPermission(client);
+ UserPolicyRepresentation userRep = new UserPolicyRepresentation();
+ userRep.setName("userClientMapper");
+ userRep.addUser("clientMapper");
+ Policy userPolicy = permissions.authz().getStoreFactory().getPolicyStore().create(userRep, permissions.clients().resourceServer(client));
+ clientMapperPolicy.addAssociatedPolicy(userPolicy);
+
+ UserModel clientManager = session.users().addUser(realm, "clientManager");
+ clientManager.setEnabled(true);
+ clientManager.grantRole(queryClientsRole);
+ session.userCredentialManager().updateCredential(realm, clientManager, UserCredentialModel.password("password"));
+
+ Policy clientManagerPolicy = permissions.clients().managePermission(client);
+ userRep = new UserPolicyRepresentation();
+ userRep.setName("clientManager");
+ userRep.addUser("clientManager");
+ userPolicy = permissions.authz().getStoreFactory().getPolicyStore().create(userRep, permissions.clients().resourceServer(client));
+ clientManagerPolicy.addAssociatedPolicy(userPolicy);
+
+
+ UserModel clientConfigurer = session.users().addUser(realm, "clientConfigurer");
+ clientConfigurer.setEnabled(true);
+ clientConfigurer.grantRole(queryClientsRole);
+ session.userCredentialManager().updateCredential(realm, clientConfigurer, UserCredentialModel.password("password"));
+
+ Policy clientConfigurePolicy = permissions.clients().configurePermission(client);
+ userRep = new UserPolicyRepresentation();
+ userRep.setName("clientConfigure");
+ userRep.addUser("clientConfigurer");
+ userPolicy = permissions.authz().getStoreFactory().getPolicyStore().create(userRep, permissions.clients().resourceServer(client));
+ clientConfigurePolicy.addAssociatedPolicy(userPolicy);
+
+
+
+
+
+
+ }
+
+ public static void evaluateLocally(KeycloakSession session) {
+ RealmModel realm = session.realms().getRealmByName(TEST);
+ RoleModel realmRole = realm.getRole("realm-role");
+ RoleModel realmRole2 = realm.getRole("realm-role2");
+ ClientModel client = realm.getClientByClientId(CLIENT_NAME);
+ RoleModel clientRole = client.getRole("client-role");
+
+ // test authorized
+ {
+ UserModel user = session.users().getUserByUsername("authorized", realm);
+ AdminPermissionEvaluator permissionsForAdmin = AdminPermissions.evaluator(session, realm, realm, user);
+ Assert.assertTrue(permissionsForAdmin.users().canManage());
+ Assert.assertTrue(permissionsForAdmin.roles().canMapRole(realmRole));
+ Assert.assertFalse(permissionsForAdmin.roles().canMapRole(realmRole2));
+ Assert.assertTrue(permissionsForAdmin.roles().canMapRole(clientRole));
+ }
+ // test composite role
+ {
+ UserModel user = session.users().getUserByUsername("authorizedComposite", realm);
+ AdminPermissionEvaluator permissionsForAdmin = AdminPermissions.evaluator(session, realm, realm, user);
+ Assert.assertTrue(permissionsForAdmin.users().canManage());
+ Assert.assertTrue(permissionsForAdmin.roles().canMapRole(realmRole));
+ Assert.assertFalse(permissionsForAdmin.roles().canMapRole(realmRole2));
+ Assert.assertTrue(permissionsForAdmin.roles().canMapRole(clientRole));
+ }
+
+ // test unauthorized
+ {
+ UserModel user = session.users().getUserByUsername("unauthorized", realm);
+ AdminPermissionEvaluator permissionsForAdmin = AdminPermissions.evaluator(session, realm, realm, user);
+ Assert.assertFalse(permissionsForAdmin.users().canManage());
+ Assert.assertFalse(permissionsForAdmin.roles().canMapRole(realmRole));
+ Assert.assertFalse(permissionsForAdmin.roles().canMapRole(clientRole));
+ Assert.assertFalse(permissionsForAdmin.roles().canMapRole(realmRole2));
+ }
+ // test unauthorized mapper
+ {
+ UserModel user = session.users().getUserByUsername("unauthorizedMapper", realm);
+ AdminPermissionEvaluator permissionsForAdmin = AdminPermissions.evaluator(session, realm, realm, user);
+ Assert.assertTrue(permissionsForAdmin.users().canManage());
+ Assert.assertFalse(permissionsForAdmin.roles().canMapRole(realmRole));
+ Assert.assertFalse(permissionsForAdmin.roles().canMapRole(clientRole));
+ // will result to true because realmRole2 does not have any policies attached to this permission
+ Assert.assertFalse(permissionsForAdmin.roles().canMapRole(realmRole2));
+ }
+ // test group management
+ {
+ UserModel admin = session.users().getUserByUsername("groupManager", realm);
+ AdminPermissionEvaluator permissionsForAdmin = AdminPermissions.evaluator(session, realm, realm, admin);
+ UserModel user = session.users().getUserByUsername("authorized", realm);
+ Assert.assertFalse(permissionsForAdmin.users().canManage(user));
+ Assert.assertFalse(permissionsForAdmin.users().canView(user));
+ UserModel member = session.users().getUserByUsername("groupMember", realm);
+ Assert.assertTrue(permissionsForAdmin.users().canManage(member));
+ Assert.assertTrue(permissionsForAdmin.users().canView(member));
+ Assert.assertTrue(permissionsForAdmin.roles().canMapRole(realmRole));
+ Assert.assertTrue(permissionsForAdmin.roles().canMapRole(clientRole));
+ Assert.assertFalse(permissionsForAdmin.roles().canMapRole(realmRole2));
+
+ }
+ // test client.mapRoles
+ {
+ UserModel admin = session.users().getUserByUsername("clientMapper", realm);
+ AdminPermissionEvaluator permissionsForAdmin = AdminPermissions.evaluator(session, realm, realm, admin);
+ UserModel user = session.users().getUserByUsername("authorized", realm);
+ Assert.assertTrue(permissionsForAdmin.users().canManage(user));
+ Assert.assertFalse(permissionsForAdmin.roles().canMapRole(realmRole));
+ Assert.assertTrue(permissionsForAdmin.roles().canMapRole(clientRole));
+ Assert.assertFalse(permissionsForAdmin.roles().canMapRole(realmRole2));
+
+ }
+
+ }
+
+
+ protected boolean isImportAfterEachMethod() {
+ return true;
+ }
+ //@Test
+ public void testDemo() throws Exception {
+ testingClient.server().run(FineGrainAdminUnitTest::setupDemo);
+ Thread.sleep(1000000000);
+ }
+
+
+ @Test
+ public void testEvaluationLocal() throws Exception {
+ testingClient.server().run(FineGrainAdminUnitTest::setupPolices);
+ testingClient.server().run(FineGrainAdminUnitTest::setupUsers);
+ testingClient.server().run(FineGrainAdminUnitTest::evaluateLocally);
+ }
+
+ @Test
+ public void testRestEvaluation() throws Exception {
+ testingClient.server().run(FineGrainAdminUnitTest::setupPolices);
+ testingClient.server().run(FineGrainAdminUnitTest::setupUsers);
+
+ UserRepresentation user1 = adminClient.realm(TEST).users().search("user1").get(0);
+ UserRepresentation anotherAdmin = adminClient.realm(TEST).users().search("anotherAdmin").get(0);
+ UserRepresentation groupMember = adminClient.realm(TEST).users().search("groupMember").get(0);
+ RoleRepresentation realmRole = adminClient.realm(TEST).roles().get("realm-role").toRepresentation();
+ List<RoleRepresentation> realmRoleSet = new LinkedList<>();
+ realmRoleSet.add(realmRole);
+ RoleRepresentation realmRole2 = adminClient.realm(TEST).roles().get("realm-role2").toRepresentation();
+ List<RoleRepresentation> realmRole2Set = new LinkedList<>();
+ realmRole2Set.add(realmRole2);
+ ClientRepresentation client = adminClient.realm(TEST).clients().findByClientId(CLIENT_NAME).get(0);
+ ClientTemplateRepresentation template = adminClient.realm(TEST).clientTemplates().findAll().get(0);
+ RoleRepresentation clientRole = adminClient.realm(TEST).clients().get(client.getId()).roles().get("client-role").toRepresentation();
+ List<RoleRepresentation> clientRoleSet = new LinkedList<>();
+ clientRoleSet.add(clientRole);
+
+ // test configure client
+ {
+ Keycloak realmClient = AdminClientUtil.createAdminClient(suiteContext.isAdapterCompatTesting(),
+ TEST, "clientConfigurer", "password", Constants.ADMIN_CLI_CLIENT_ID, null);
+ client.setAdminUrl("http://nowhere");
+ realmClient.realm(TEST).clients().get(client.getId()).update(client);
+ client.setFullScopeAllowed(true);
+ try {
+ realmClient.realm(TEST).clients().get(client.getId()).update(client);
+ Assert.fail("should fail with forbidden exception");
+ } catch (ClientErrorException e) {
+ Assert.assertEquals(e.getResponse().getStatus(), 403);
+
+ }
+ client.setFullScopeAllowed(false);
+ realmClient.realm(TEST).clients().get(client.getId()).update(client);
+
+ client.setClientTemplate(template.getName());
+ try {
+ realmClient.realm(TEST).clients().get(client.getId()).update(client);
+ Assert.fail("should fail with forbidden exception");
+ } catch (ClientErrorException e) {
+ Assert.assertEquals(e.getResponse().getStatus(), 403);
+
+ }
+ client.setClientTemplate(null);
+ realmClient.realm(TEST).clients().get(client.getId()).update(client);
+
+ try {
+ realmClient.realm(TEST).clients().get(client.getId()).getScopeMappings().realmLevel().add(realmRoleSet);
+ Assert.fail("should fail with forbidden exception");
+ } catch (ClientErrorException e) {
+ Assert.assertEquals(e.getResponse().getStatus(), 403);
+
+ }
+ }
+
+ // test illegal impersonation
+ {
+ Keycloak realmClient = AdminClientUtil.createAdminClient(suiteContext.isAdapterCompatTesting(),
+ TEST, "nomap-admin", "password", Constants.ADMIN_CLI_CLIENT_ID, null);
+ realmClient.realm(TEST).users().get(user1.getId()).impersonate();
+ realmClient.close(); // just in case of cookie settings
+ realmClient = AdminClientUtil.createAdminClient(suiteContext.isAdapterCompatTesting(),
+ TEST, "nomap-admin", "password", Constants.ADMIN_CLI_CLIENT_ID, null);
+ try {
+ realmClient.realm(TEST).users().get(anotherAdmin.getId()).impersonate();
+ Assert.fail("should fail with forbidden exception");
+ } catch (ClientErrorException e) {
+ Assert.assertEquals(e.getResponse().getStatus(), 403);
+
+ }
+
+ }
+
+
+ {
+ Keycloak realmClient = AdminClientUtil.createAdminClient(suiteContext.isAdapterCompatTesting(),
+ TEST, "authorized", "password", Constants.ADMIN_CLI_CLIENT_ID, null);
+ realmClient.realm(TEST).users().get(user1.getId()).roles().realmLevel().add(realmRoleSet);
+ List<RoleRepresentation> roles = adminClient.realm(TEST).users().get(user1.getId()).roles().realmLevel().listAll();
+ Assert.assertTrue(roles.stream().anyMatch((r) -> {
+ return r.getName().equals("realm-role");
+ }));
+ realmClient.realm(TEST).users().get(user1.getId()).roles().realmLevel().remove(realmRoleSet);
+ roles = adminClient.realm(TEST).users().get(user1.getId()).roles().realmLevel().listAll();
+ Assert.assertTrue(roles.stream().noneMatch((r) -> {
+ return r.getName().equals("realm-role");
+ }));
+
+ realmClient.realm(TEST).users().get(user1.getId()).roles().clientLevel(client.getId()).add(clientRoleSet);
+ roles = adminClient.realm(TEST).users().get(user1.getId()).roles().clientLevel(client.getId()).listAll();
+ Assert.assertTrue(roles.stream().anyMatch((r) -> {
+ return r.getName().equals("client-role");
+ }));
+ realmClient.realm(TEST).users().get(user1.getId()).roles().clientLevel(client.getId()).remove(clientRoleSet);
+ roles = adminClient.realm(TEST).users().get(user1.getId()).roles().clientLevel(client.getId()).listAll();
+ Assert.assertTrue(roles.stream().noneMatch((r) -> {
+ return r.getName().equals("client-role");
+ }));
+ realmClient.close();
+ }
+
+ {
+ Keycloak realmClient= AdminClientUtil.createAdminClient(suiteContext.isAdapterCompatTesting(),
+ TEST, "authorizedComposite", "password", Constants.ADMIN_CLI_CLIENT_ID, null);
+ realmClient.realm(TEST).users().get(user1.getId()).roles().realmLevel().add(realmRoleSet);
+ List<RoleRepresentation> roles = adminClient.realm(TEST).users().get(user1.getId()).roles().realmLevel().listAll();
+ Assert.assertTrue(roles.stream().anyMatch((r) -> {
+ return r.getName().equals("realm-role");
+ }));
+ realmClient.realm(TEST).users().get(user1.getId()).roles().realmLevel().remove(realmRoleSet);
+ roles = adminClient.realm(TEST).users().get(user1.getId()).roles().realmLevel().listAll();
+ Assert.assertTrue(roles.stream().noneMatch((r) -> {
+ return r.getName().equals("realm-role");
+ }));
+
+ realmClient.realm(TEST).users().get(user1.getId()).roles().clientLevel(client.getId()).add(clientRoleSet);
+ roles = adminClient.realm(TEST).users().get(user1.getId()).roles().clientLevel(client.getId()).listAll();
+ Assert.assertTrue(roles.stream().anyMatch((r) -> {
+ return r.getName().equals("client-role");
+ }));
+ realmClient.realm(TEST).users().get(user1.getId()).roles().clientLevel(client.getId()).remove(clientRoleSet);
+ roles = adminClient.realm(TEST).users().get(user1.getId()).roles().clientLevel(client.getId()).listAll();
+ Assert.assertTrue(roles.stream().noneMatch((r) -> {
+ return r.getName().equals("client-role");
+ }));
+ }
+ {
+ Keycloak realmClient= AdminClientUtil.createAdminClient(suiteContext.isAdapterCompatTesting(),
+ TEST, "unauthorized", "password", Constants.ADMIN_CLI_CLIENT_ID, null);
+ try {
+ realmClient.realm(TEST).users().get(user1.getId()).roles().realmLevel().add(realmRoleSet);
+ Assert.fail("should fail with forbidden exception");
+ } catch (ClientErrorException e) {
+ Assert.assertEquals(e.getResponse().getStatus(), 403);
+
+ }
+ }
+ {
+ Keycloak realmClient= AdminClientUtil.createAdminClient(suiteContext.isAdapterCompatTesting(),
+ TEST, "unauthorizedMapper", "password", Constants.ADMIN_CLI_CLIENT_ID, null);
+ try {
+ realmClient.realm(TEST).users().get(user1.getId()).roles().realmLevel().add(realmRoleSet);
+ Assert.fail("should fail with forbidden exception");
+ } catch (ClientErrorException e) {
+ Assert.assertEquals(e.getResponse().getStatus(), 403);
+
+ }
+ }
+
+ {
+ Keycloak realmClient = AdminClientUtil.createAdminClient(suiteContext.isAdapterCompatTesting(),
+ TEST, "groupManager", "password", Constants.ADMIN_CLI_CLIENT_ID, null);
+ List<RoleRepresentation> roles = null;
+ realmClient.realm(TEST).users().get(groupMember.getId()).roles().clientLevel(client.getId()).add(clientRoleSet);
+ roles = realmClient.realm(TEST).users().get(groupMember.getId()).roles().clientLevel(client.getId()).listAll();
+ Assert.assertTrue(roles.stream().anyMatch((r) -> {
+ return r.getName().equals("client-role");
+ }));
+ realmClient.realm(TEST).users().get(groupMember.getId()).roles().clientLevel(client.getId()).remove(clientRoleSet);
+
+ roles = realmClient.realm(TEST).users().get(groupMember.getId()).roles().realmLevel().listAvailable();
+ Assert.assertEquals(roles.size(), 1);
+ realmClient.realm(TEST).users().get(groupMember.getId()).roles().realmLevel().add(realmRoleSet);
+ realmClient.realm(TEST).users().get(groupMember.getId()).roles().realmLevel().remove(realmRoleSet);
+ try {
+ realmClient.realm(TEST).users().get(groupMember.getId()).roles().realmLevel().add(realmRole2Set);
+ Assert.fail("should fail with forbidden exception");
+ } catch (ClientErrorException e) {
+ Assert.assertEquals(e.getResponse().getStatus(), 403);
+
+ }
+ try {
+ realmClient.realm(TEST).users().get(user1.getId()).roles().realmLevel().add(realmRoleSet);
+ Assert.fail("should fail with forbidden exception");
+ } catch (ClientErrorException e) {
+ Assert.assertEquals(e.getResponse().getStatus(), 403);
+
+ }
+
+ }
+
+
+ // test client.mapRoles
+ {
+ Keycloak realmClient = AdminClientUtil.createAdminClient(suiteContext.isAdapterCompatTesting(),
+ TEST, "clientMapper", "password", Constants.ADMIN_CLI_CLIENT_ID, null);
+ List<RoleRepresentation> roles = null;
+ roles = realmClient.realm(TEST).users().get(user1.getId()).roles().clientLevel(client.getId()).listAll();
+ Assert.assertTrue(roles.isEmpty());
+ realmClient.realm(TEST).users().get(user1.getId()).roles().clientLevel(client.getId()).add(clientRoleSet);
+ roles = realmClient.realm(TEST).users().get(user1.getId()).roles().clientLevel(client.getId()).listAll();
+ Assert.assertTrue(roles.stream().anyMatch((r) -> {
+ return r.getName().equals("client-role");
+ }));
+ roles = realmClient.realm(TEST).users().get(user1.getId()).roles().realmLevel().listAvailable();
+ Assert.assertTrue(roles.isEmpty());
+ try {
+ realmClient.realm(TEST).users().get(user1.getId()).roles().realmLevel().add(realmRoleSet);
+ Assert.fail("should fail with forbidden exception");
+ } catch (ClientErrorException e) {
+ Assert.assertEquals(e.getResponse().getStatus(), 403);
+
+ }
+ }
+
+
+ }
+
+ @Test
+ public void testMasterRealm() throws Exception {
+ // test that master realm can still perform operations when policies are in place
+ //
+ testingClient.server().run(FineGrainAdminUnitTest::setupPolices);
+ testingClient.server().run(FineGrainAdminUnitTest::setupUsers);
+
+ UserRepresentation user1 = adminClient.realm(TEST).users().search("user1").get(0);
+ RoleRepresentation realmRole = adminClient.realm(TEST).roles().get("realm-role").toRepresentation();
+ List<RoleRepresentation> realmRoleSet = new LinkedList<>();
+ realmRoleSet.add(realmRole);
+ RoleRepresentation realmRole2 = adminClient.realm(TEST).roles().get("realm-role2").toRepresentation();
+ List<RoleRepresentation> realmRole2Set = new LinkedList<>();
+ realmRole2Set.add(realmRole);
+ ClientRepresentation client = adminClient.realm(TEST).clients().findByClientId(CLIENT_NAME).get(0);
+ RoleRepresentation clientRole = adminClient.realm(TEST).clients().get(client.getId()).roles().get("client-role").toRepresentation();
+ List<RoleRepresentation> clientRoleSet = new LinkedList<>();
+ clientRoleSet.add(clientRole);
+
+
+ {
+ Keycloak realmClient = AdminClientUtil.createAdminClient(suiteContext.isAdapterCompatTesting());
+ realmClient.realm(TEST).users().get(user1.getId()).roles().realmLevel().add(realmRoleSet);
+ List<RoleRepresentation> roles = adminClient.realm(TEST).users().get(user1.getId()).roles().realmLevel().listAll();
+ Assert.assertTrue(roles.stream().anyMatch((r) -> {
+ return r.getName().equals("realm-role");
+ }));
+ realmClient.realm(TEST).users().get(user1.getId()).roles().realmLevel().remove(realmRoleSet);
+ roles = adminClient.realm(TEST).users().get(user1.getId()).roles().realmLevel().listAll();
+ Assert.assertTrue(roles.stream().noneMatch((r) -> {
+ return r.getName().equals("realm-role");
+ }));
+
+ realmClient.realm(TEST).users().get(user1.getId()).roles().clientLevel(client.getId()).add(clientRoleSet);
+ roles = adminClient.realm(TEST).users().get(user1.getId()).roles().clientLevel(client.getId()).listAll();
+ Assert.assertTrue(roles.stream().anyMatch((r) -> {
+ return r.getName().equals("client-role");
+ }));
+ realmClient.realm(TEST).users().get(user1.getId()).roles().clientLevel(client.getId()).remove(clientRoleSet);
+ roles = adminClient.realm(TEST).users().get(user1.getId()).roles().clientLevel(client.getId()).listAll();
+ Assert.assertTrue(roles.stream().noneMatch((r) -> {
+ return r.getName().equals("client-role");
+ }));
+ realmClient.close();
+ }
+
+ }
+ // testRestEvaluationMasterRealm
+ // testRestEvaluationMasterAdminTestRealm
+
+ // test role deletion that it cleans up authz objects
+ public static void setupDeleteTest(KeycloakSession session ) {
+ RealmModel realm = session.realms().getRealmByName(TEST);
+ RoleModel removedRole = realm.addRole("removedRole");
+ ClientModel client = realm.addClient("removedClient");
+ RoleModel removedClientRole = client.addRole("removedClientRole");
+ GroupModel removedGroup = realm.createGroup("removedGroup");
+ AdminPermissionManagement management = AdminPermissions.management(session, realm);
+ management.roles().setPermissionsEnabled(removedRole, true);
+ management.roles().setPermissionsEnabled(removedClientRole, true);
+ management.groups().setPermissionsEnabled(removedGroup, true);
+ management.clients().setPermissionsEnabled(client, true);
+ }
+
+ public static void invokeDelete(KeycloakSession session) {
+ RealmModel realm = session.realms().getRealmByName(TEST);
+ AdminPermissionManagement management = AdminPermissions.management(session, realm);
+ List<Resource> byResourceServer = management.authz().getStoreFactory().getResourceStore().findByResourceServer(management.realmResourceServer().getId());
+ Assert.assertEquals(4, byResourceServer.size());
+ RoleModel removedRole = realm.getRole("removedRole");
+ realm.removeRole(removedRole);
+ ClientModel client = realm.getClientByClientId("removedClient");
+ RoleModel removedClientRole = client.getRole("removedClientRole");
+ client.removeRole(removedClientRole);
+ GroupModel group = KeycloakModelUtils.findGroupByPath(realm, "removedGroup");
+ realm.removeGroup(group);
+ byResourceServer = management.authz().getStoreFactory().getResourceStore().findByResourceServer(management.realmResourceServer().getId());
+ Assert.assertEquals(1, byResourceServer.size());
+ realm.removeClient(client.getId());
+ byResourceServer = management.authz().getStoreFactory().getResourceStore().findByResourceServer(management.realmResourceServer().getId());
+ Assert.assertEquals(0, byResourceServer.size());
+ }
+
+ @Test
+ public void testRemoveCleanup() throws Exception {
+ testingClient.server().run(FineGrainAdminUnitTest::setupDeleteTest);
+ testingClient.server().run(FineGrainAdminUnitTest::invokeDelete);
+ }
+
+
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/IllegalAdminUpgradeTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/IllegalAdminUpgradeTest.java
new file mode 100644
index 0000000..7c6ced5
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/IllegalAdminUpgradeTest.java
@@ -0,0 +1,648 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.testsuite.admin;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.keycloak.admin.client.Keycloak;
+import org.keycloak.authorization.model.Policy;
+import org.keycloak.authorization.model.ResourceServer;
+import org.keycloak.models.AdminRoles;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.Constants;
+import org.keycloak.models.GroupModel;
+import org.keycloak.models.ImpersonationConstants;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleModel;
+import org.keycloak.models.UserCredentialModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.representations.idm.RoleRepresentation;
+import org.keycloak.representations.idm.UserRepresentation;
+import org.keycloak.representations.idm.authorization.UserPolicyRepresentation;
+import org.keycloak.services.resources.admin.permissions.AdminPermissionManagement;
+import org.keycloak.services.resources.admin.permissions.AdminPermissions;
+import org.keycloak.testsuite.AbstractKeycloakTest;
+import org.keycloak.testsuite.util.AdminClientUtil;
+
+import javax.ws.rs.ClientErrorException;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+import static org.keycloak.testsuite.auth.page.AuthRealm.TEST;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class IllegalAdminUpgradeTest extends AbstractKeycloakTest {
+
+ public static final String CLIENT_NAME = "application";
+
+ @Override
+ public void addTestRealms(List<RealmRepresentation> testRealms) {
+ RealmRepresentation testRealmRep = new RealmRepresentation();
+ testRealmRep.setId(TEST);
+ testRealmRep.setRealm(TEST);
+ testRealmRep.setEnabled(true);
+ testRealms.add(testRealmRep);
+ }
+
+ protected boolean isImportAfterEachMethod() {
+ return true;
+ }
+
+
+ public static void setupUsers(KeycloakSession session) {
+ RealmModel realm = session.realms().getRealmByName(TEST);
+ RealmModel master = session.realms().getRealmByName("master");
+ ClientModel realmAdminClient = realm.getClientByClientId(Constants.REALM_MANAGEMENT_CLIENT_ID);
+ ClientModel realmMasterAdminClient = realm.getMasterAdminClient();
+ RoleModel realmManageUsers = realmAdminClient.getRole(AdminRoles.MANAGE_USERS);
+ RoleModel masterManageUsers = realmMasterAdminClient.getRole(AdminRoles.MANAGE_USERS);
+ RoleModel masterMasterManageUSers = master.getMasterAdminClient().getRole(AdminRoles.MANAGE_USERS);
+
+ UserModel realmUser = session.users().addUser(realm, "userAdmin");
+ realmUser.grantRole(realmManageUsers);
+ realmUser.setEnabled(true);
+ session.userCredentialManager().updateCredential(realm, realmUser, UserCredentialModel.password("password"));
+
+ UserModel masterUser = session.users().addUser(master, "userAdmin");
+ masterUser.grantRole(masterManageUsers);
+ masterUser.setEnabled(true);
+ session.userCredentialManager().updateCredential(master, masterUser, UserCredentialModel.password("password"));
+
+ UserModel masterAdmin = session.users().addUser(master, "masterAdmin");
+ masterAdmin.grantRole(masterMasterManageUSers);
+ masterAdmin.setEnabled(true);
+ session.userCredentialManager().updateCredential(master, masterAdmin, UserCredentialModel.password("password"));
+
+ UserModel user = session.users().addUser(master, "user");
+ user.grantRole(masterManageUsers);
+ user.setEnabled(true);
+ session.userCredentialManager().updateCredential(master, user, UserCredentialModel.password("password"));
+
+ user = session.users().addUser(realm, "user");
+ user.grantRole(realmManageUsers);
+ user.setEnabled(true);
+ session.userCredentialManager().updateCredential(realm, user, UserCredentialModel.password("password"));
+ }
+
+ @Test
+ public void testRestEvaluation() throws Exception {
+ testingClient.server().run(IllegalAdminUpgradeTest::setupUsers);
+
+ UserRepresentation realmUserAdmin = adminClient.realm(TEST).users().search("userAdmin").get(0);
+ UserRepresentation masterUserAdmin = adminClient.realm("master").users().search("userAdmin").get(0);
+ UserRepresentation realmUser = adminClient.realm(TEST).users().search("user").get(0);
+ UserRepresentation masterUser = adminClient.realm("master").users().search("user").get(0);
+
+ ClientRepresentation realmAdminClient = adminClient.realm(TEST).clients().findByClientId(Constants.REALM_MANAGEMENT_CLIENT_ID).get(0);
+ RoleRepresentation realmManageAuthorization = adminClient.realm(TEST).clients().get(realmAdminClient.getId()).roles().get(AdminRoles.MANAGE_AUTHORIZATION).toRepresentation();
+ RoleRepresentation realmViewAuthorization = adminClient.realm(TEST).clients().get(realmAdminClient.getId()).roles().get(AdminRoles.VIEW_AUTHORIZATION).toRepresentation();
+ RoleRepresentation realmManageClients = adminClient.realm(TEST).clients().get(realmAdminClient.getId()).roles().get(AdminRoles.MANAGE_CLIENTS).toRepresentation();
+ RoleRepresentation realmViewClients = adminClient.realm(TEST).clients().get(realmAdminClient.getId()).roles().get(AdminRoles.VIEW_CLIENTS).toRepresentation();
+ RoleRepresentation realmManageEvents = adminClient.realm(TEST).clients().get(realmAdminClient.getId()).roles().get(AdminRoles.MANAGE_EVENTS).toRepresentation();
+ RoleRepresentation realmViewEvents = adminClient.realm(TEST).clients().get(realmAdminClient.getId()).roles().get(AdminRoles.VIEW_EVENTS).toRepresentation();
+ RoleRepresentation realmManageIdentityProviders = adminClient.realm(TEST).clients().get(realmAdminClient.getId()).roles().get(AdminRoles.MANAGE_IDENTITY_PROVIDERS).toRepresentation();
+ RoleRepresentation realmViewIdentityProviders = adminClient.realm(TEST).clients().get(realmAdminClient.getId()).roles().get(AdminRoles.VIEW_IDENTITY_PROVIDERS).toRepresentation();
+ RoleRepresentation realmManageRealm = adminClient.realm(TEST).clients().get(realmAdminClient.getId()).roles().get(AdminRoles.MANAGE_REALM).toRepresentation();
+ RoleRepresentation realmViewRealm = adminClient.realm(TEST).clients().get(realmAdminClient.getId()).roles().get(AdminRoles.VIEW_REALM).toRepresentation();
+ RoleRepresentation realmImpersonate = adminClient.realm(TEST).clients().get(realmAdminClient.getId()).roles().get(ImpersonationConstants.IMPERSONATION_ROLE).toRepresentation();
+ RoleRepresentation realmManageUsers = adminClient.realm(TEST).clients().get(realmAdminClient.getId()).roles().get(AdminRoles.MANAGE_USERS).toRepresentation();
+ RoleRepresentation realmViewUsers = adminClient.realm(TEST).clients().get(realmAdminClient.getId()).roles().get(AdminRoles.VIEW_USERS).toRepresentation();
+ RoleRepresentation realmQueryUsers = adminClient.realm(TEST).clients().get(realmAdminClient.getId()).roles().get(AdminRoles.QUERY_USERS).toRepresentation();
+ RoleRepresentation realmQueryClients = adminClient.realm(TEST).clients().get(realmAdminClient.getId()).roles().get(AdminRoles.QUERY_CLIENTS).toRepresentation();
+ RoleRepresentation realmQueryGroups = adminClient.realm(TEST).clients().get(realmAdminClient.getId()).roles().get(AdminRoles.QUERY_GROUPS).toRepresentation();
+
+ ClientRepresentation masterClient = adminClient.realm("master").clients().findByClientId(TEST + "-realm").get(0);
+ RoleRepresentation masterManageAuthorization = adminClient.realm("master").clients().get(masterClient.getId()).roles().get(AdminRoles.MANAGE_AUTHORIZATION).toRepresentation();
+ RoleRepresentation masterViewAuthorization = adminClient.realm("master").clients().get(masterClient.getId()).roles().get(AdminRoles.VIEW_AUTHORIZATION).toRepresentation();
+ RoleRepresentation masterManageClients = adminClient.realm("master").clients().get(masterClient.getId()).roles().get(AdminRoles.MANAGE_CLIENTS).toRepresentation();
+ RoleRepresentation masterViewClients = adminClient.realm("master").clients().get(masterClient.getId()).roles().get(AdminRoles.VIEW_CLIENTS).toRepresentation();
+ RoleRepresentation masterManageEvents = adminClient.realm("master").clients().get(masterClient.getId()).roles().get(AdminRoles.MANAGE_EVENTS).toRepresentation();
+ RoleRepresentation masterViewEvents = adminClient.realm("master").clients().get(masterClient.getId()).roles().get(AdminRoles.VIEW_EVENTS).toRepresentation();
+ RoleRepresentation masterManageIdentityProviders = adminClient.realm("master").clients().get(masterClient.getId()).roles().get(AdminRoles.MANAGE_IDENTITY_PROVIDERS).toRepresentation();
+ RoleRepresentation masterViewIdentityProviders = adminClient.realm("master").clients().get(masterClient.getId()).roles().get(AdminRoles.VIEW_IDENTITY_PROVIDERS).toRepresentation();
+ RoleRepresentation masterManageRealm = adminClient.realm("master").clients().get(masterClient.getId()).roles().get(AdminRoles.MANAGE_REALM).toRepresentation();
+ RoleRepresentation masterViewRealm = adminClient.realm("master").clients().get(masterClient.getId()).roles().get(AdminRoles.VIEW_REALM).toRepresentation();
+ RoleRepresentation masterImpersonate = adminClient.realm("master").clients().get(masterClient.getId()).roles().get(ImpersonationConstants.IMPERSONATION_ROLE).toRepresentation();
+ RoleRepresentation masterManageUsers = adminClient.realm("master").clients().get(masterClient.getId()).roles().get(AdminRoles.MANAGE_USERS).toRepresentation();
+ RoleRepresentation masterViewUsers = adminClient.realm("master").clients().get(masterClient.getId()).roles().get(AdminRoles.VIEW_USERS).toRepresentation();
+ RoleRepresentation masterQueryUsers = adminClient.realm("master").clients().get(masterClient.getId()).roles().get(AdminRoles.QUERY_USERS).toRepresentation();
+ RoleRepresentation masterQueryClients = adminClient.realm("master").clients().get(masterClient.getId()).roles().get(AdminRoles.QUERY_CLIENTS).toRepresentation();
+ RoleRepresentation masterQueryGroups = adminClient.realm("master").clients().get(masterClient.getId()).roles().get(AdminRoles.QUERY_GROUPS).toRepresentation();
+
+ List<RoleRepresentation> roles = new LinkedList<>();
+
+ {
+ ClientRepresentation client = realmAdminClient;
+ Keycloak realmClient = AdminClientUtil.createAdminClient(suiteContext.isAdapterCompatTesting(),
+ TEST, "userAdmin", "password", Constants.ADMIN_CLI_CLIENT_ID, null);
+ roles.clear();
+ roles.add(realmManageAuthorization);
+ try {
+ realmClient.realm(TEST).users().get(realmUser.getId()).roles().clientLevel(client.getId()).add(roles);
+ Assert.fail("should fail with forbidden exception");
+ } catch (ClientErrorException e) {
+ Assert.assertEquals(e.getResponse().getStatus(), 403);
+
+ }
+
+ roles.clear();
+ roles.add(realmViewAuthorization);
+ try {
+ realmClient.realm(TEST).users().get(realmUser.getId()).roles().clientLevel(client.getId()).add(roles);
+ Assert.fail("should fail with forbidden exception");
+ } catch (ClientErrorException e) {
+ Assert.assertEquals(e.getResponse().getStatus(), 403);
+
+ }
+
+ roles.clear();
+ roles.add(realmManageClients);
+ try {
+ realmClient.realm(TEST).users().get(realmUser.getId()).roles().clientLevel(client.getId()).add(roles);
+ Assert.fail("should fail with forbidden exception");
+ } catch (ClientErrorException e) {
+ Assert.assertEquals(e.getResponse().getStatus(), 403);
+
+ }
+
+ roles.clear();
+ roles.add(realmViewClients);
+ try {
+ realmClient.realm(TEST).users().get(realmUser.getId()).roles().clientLevel(client.getId()).add(roles);
+ Assert.fail("should fail with forbidden exception");
+ } catch (ClientErrorException e) {
+ Assert.assertEquals(e.getResponse().getStatus(), 403);
+
+ }
+
+ roles.clear();
+ roles.add(realmManageEvents);
+ try {
+ realmClient.realm(TEST).users().get(realmUser.getId()).roles().clientLevel(client.getId()).add(roles);
+ Assert.fail("should fail with forbidden exception");
+ } catch (ClientErrorException e) {
+ Assert.assertEquals(e.getResponse().getStatus(), 403);
+
+ }
+
+ roles.clear();
+ roles.add(realmViewEvents);
+ try {
+ realmClient.realm(TEST).users().get(realmUser.getId()).roles().clientLevel(client.getId()).add(roles);
+ Assert.fail("should fail with forbidden exception");
+ } catch (ClientErrorException e) {
+ Assert.assertEquals(e.getResponse().getStatus(), 403);
+
+ }
+
+ roles.clear();
+ roles.add(realmManageIdentityProviders);
+ try {
+ realmClient.realm(TEST).users().get(realmUser.getId()).roles().clientLevel(client.getId()).add(roles);
+ Assert.fail("should fail with forbidden exception");
+ } catch (ClientErrorException e) {
+ Assert.assertEquals(e.getResponse().getStatus(), 403);
+
+ }
+
+ roles.clear();
+ roles.add(realmViewIdentityProviders);
+ try {
+ realmClient.realm(TEST).users().get(realmUser.getId()).roles().clientLevel(client.getId()).add(roles);
+ Assert.fail("should fail with forbidden exception");
+ } catch (ClientErrorException e) {
+ Assert.assertEquals(e.getResponse().getStatus(), 403);
+
+ }
+
+ roles.clear();
+ roles.add(realmManageRealm);
+ try {
+ realmClient.realm(TEST).users().get(realmUser.getId()).roles().clientLevel(client.getId()).add(roles);
+ Assert.fail("should fail with forbidden exception");
+ } catch (ClientErrorException e) {
+ Assert.assertEquals(e.getResponse().getStatus(), 403);
+
+ }
+
+ roles.clear();
+ roles.add(realmViewRealm);
+ try {
+ realmClient.realm(TEST).users().get(realmUser.getId()).roles().clientLevel(client.getId()).add(roles);
+ Assert.fail("should fail with forbidden exception");
+ } catch (ClientErrorException e) {
+ Assert.assertEquals(e.getResponse().getStatus(), 403);
+
+ }
+
+ roles.clear();
+ roles.add(realmImpersonate);
+ try {
+ realmClient.realm(TEST).users().get(realmUser.getId()).roles().clientLevel(client.getId()).add(roles);
+ Assert.fail("should fail with forbidden exception");
+ } catch (ClientErrorException e) {
+ Assert.assertEquals(e.getResponse().getStatus(), 403);
+
+ }
+
+ roles.clear();
+ roles.add(realmManageUsers);
+ realmClient.realm(TEST).users().get(realmUser.getId()).roles().clientLevel(client.getId()).add(roles);
+ realmClient.realm(TEST).users().get(realmUser.getId()).roles().clientLevel(client.getId()).remove(roles);
+
+ roles.clear();
+ roles.add(realmViewUsers);
+ realmClient.realm(TEST).users().get(realmUser.getId()).roles().clientLevel(client.getId()).add(roles);
+ realmClient.realm(TEST).users().get(realmUser.getId()).roles().clientLevel(client.getId()).remove(roles);
+
+ roles.clear();
+ roles.add(realmQueryUsers);
+ realmClient.realm(TEST).users().get(realmUser.getId()).roles().clientLevel(client.getId()).add(roles);
+ realmClient.realm(TEST).users().get(realmUser.getId()).roles().clientLevel(client.getId()).remove(roles);
+
+ roles.clear();
+ roles.add(realmQueryGroups);
+ realmClient.realm(TEST).users().get(realmUser.getId()).roles().clientLevel(client.getId()).add(roles);
+ realmClient.realm(TEST).users().get(realmUser.getId()).roles().clientLevel(client.getId()).remove(roles);
+
+ roles.clear();
+ roles.add(realmQueryClients);
+ realmClient.realm(TEST).users().get(realmUser.getId()).roles().clientLevel(client.getId()).add(roles);
+ realmClient.realm(TEST).users().get(realmUser.getId()).roles().clientLevel(client.getId()).remove(roles);
+
+ realmClient.close();
+ }
+ // test master manageUsers only admin can do with master realm admin roles
+ {
+ ClientRepresentation client = masterClient;
+ Keycloak realmClient = AdminClientUtil.createAdminClient(suiteContext.isAdapterCompatTesting(),
+ "master", "masterAdmin", "password", Constants.ADMIN_CLI_CLIENT_ID, null);
+ roles.clear();
+ roles.add(masterManageAuthorization);
+ try {
+ realmClient.realm("master").users().get(masterUser.getId()).roles().clientLevel(client.getId()).add(roles);
+ Assert.fail("should fail with forbidden exception");
+ } catch (ClientErrorException e) {
+ Assert.assertEquals(e.getResponse().getStatus(), 403);
+
+ }
+
+ roles.clear();
+ roles.add(masterViewAuthorization);
+ try {
+ realmClient.realm("master").users().get(masterUser.getId()).roles().clientLevel(client.getId()).add(roles);
+ Assert.fail("should fail with forbidden exception");
+ } catch (ClientErrorException e) {
+ Assert.assertEquals(e.getResponse().getStatus(), 403);
+
+ }
+
+ roles.clear();
+ roles.add(masterManageClients);
+ try {
+ realmClient.realm("master").users().get(masterUser.getId()).roles().clientLevel(client.getId()).add(roles);
+ Assert.fail("should fail with forbidden exception");
+ } catch (ClientErrorException e) {
+ Assert.assertEquals(e.getResponse().getStatus(), 403);
+
+ }
+
+ roles.clear();
+ roles.add(masterViewClients);
+ try {
+ realmClient.realm("master").users().get(masterUser.getId()).roles().clientLevel(client.getId()).add(roles);
+ Assert.fail("should fail with forbidden exception");
+ } catch (ClientErrorException e) {
+ Assert.assertEquals(e.getResponse().getStatus(), 403);
+
+ }
+
+ roles.clear();
+ roles.add(masterManageEvents);
+ try {
+ realmClient.realm("master").users().get(masterUser.getId()).roles().clientLevel(client.getId()).add(roles);
+ Assert.fail("should fail with forbidden exception");
+ } catch (ClientErrorException e) {
+ Assert.assertEquals(e.getResponse().getStatus(), 403);
+
+ }
+
+ roles.clear();
+ roles.add(masterViewEvents);
+ try {
+ realmClient.realm("master").users().get(masterUser.getId()).roles().clientLevel(client.getId()).add(roles);
+ Assert.fail("should fail with forbidden exception");
+ } catch (ClientErrorException e) {
+ Assert.assertEquals(e.getResponse().getStatus(), 403);
+
+ }
+
+ roles.clear();
+ roles.add(masterManageIdentityProviders);
+ try {
+ realmClient.realm("master").users().get(masterUser.getId()).roles().clientLevel(client.getId()).add(roles);
+ Assert.fail("should fail with forbidden exception");
+ } catch (ClientErrorException e) {
+ Assert.assertEquals(e.getResponse().getStatus(), 403);
+
+ }
+
+ roles.clear();
+ roles.add(masterViewIdentityProviders);
+ try {
+ realmClient.realm("master").users().get(masterUser.getId()).roles().clientLevel(client.getId()).add(roles);
+ Assert.fail("should fail with forbidden exception");
+ } catch (ClientErrorException e) {
+ Assert.assertEquals(e.getResponse().getStatus(), 403);
+
+ }
+
+ roles.clear();
+ roles.add(masterManageRealm);
+ try {
+ realmClient.realm("master").users().get(masterUser.getId()).roles().clientLevel(client.getId()).add(roles);
+ Assert.fail("should fail with forbidden exception");
+ } catch (ClientErrorException e) {
+ Assert.assertEquals(e.getResponse().getStatus(), 403);
+
+ }
+
+ roles.clear();
+ roles.add(masterViewRealm);
+ try {
+ realmClient.realm("master").users().get(masterUser.getId()).roles().clientLevel(client.getId()).add(roles);
+ Assert.fail("should fail with forbidden exception");
+ } catch (ClientErrorException e) {
+ Assert.assertEquals(e.getResponse().getStatus(), 403);
+
+ }
+
+ roles.clear();
+ roles.add(masterImpersonate);
+ try {
+ realmClient.realm("master").users().get(masterUser.getId()).roles().clientLevel(client.getId()).add(roles);
+ Assert.fail("should fail with forbidden exception");
+ } catch (ClientErrorException e) {
+ Assert.assertEquals(e.getResponse().getStatus(), 403);
+
+ }
+
+ roles.clear();
+ roles.add(masterManageUsers);
+ try {
+ realmClient.realm("master").users().get(masterUser.getId()).roles().clientLevel(client.getId()).add(roles);
+ Assert.fail("should fail with forbidden exception");
+ } catch (ClientErrorException e) {
+ Assert.assertEquals(e.getResponse().getStatus(), 403);
+
+ }
+
+ roles.clear();
+ roles.add(masterViewUsers);
+ try {
+ realmClient.realm("master").users().get(masterUser.getId()).roles().clientLevel(client.getId()).add(roles);
+ Assert.fail("should fail with forbidden exception");
+ } catch (ClientErrorException e) {
+ Assert.assertEquals(e.getResponse().getStatus(), 403);
+
+ }
+
+ roles.clear();
+ roles.add(masterQueryUsers);
+ try {
+ realmClient.realm("master").users().get(masterUser.getId()).roles().clientLevel(client.getId()).add(roles);
+ Assert.fail("should fail with forbidden exception");
+ } catch (ClientErrorException e) {
+ Assert.assertEquals(e.getResponse().getStatus(), 403);
+
+ }
+
+ roles.clear();
+ roles.add(masterQueryGroups);
+ try {
+ realmClient.realm("master").users().get(masterUser.getId()).roles().clientLevel(client.getId()).add(roles);
+ Assert.fail("should fail with forbidden exception");
+ } catch (ClientErrorException e) {
+ Assert.assertEquals(e.getResponse().getStatus(), 403);
+
+ }
+
+ roles.clear();
+ roles.add(masterQueryClients);
+ try {
+ realmClient.realm("master").users().get(masterUser.getId()).roles().clientLevel(client.getId()).add(roles);
+ Assert.fail("should fail with forbidden exception");
+ } catch (ClientErrorException e) {
+ Assert.assertEquals(e.getResponse().getStatus(), 403);
+
+ }
+
+ realmClient.close();
+ }
+ // test master admin can add all admin roles in realm
+ {
+ ClientRepresentation client = realmAdminClient;
+ Keycloak realmClient = AdminClientUtil.createAdminClient();
+ roles.clear();
+ roles.add(realmManageAuthorization);
+ realmClient.realm(TEST).users().get(realmUser.getId()).roles().clientLevel(client.getId()).add(roles);
+ realmClient.realm(TEST).users().get(realmUser.getId()).roles().clientLevel(client.getId()).remove(roles);
+
+ roles.clear();
+ roles.add(realmViewAuthorization);
+ realmClient.realm(TEST).users().get(realmUser.getId()).roles().clientLevel(client.getId()).add(roles);
+ realmClient.realm(TEST).users().get(realmUser.getId()).roles().clientLevel(client.getId()).remove(roles);
+
+ roles.clear();
+ roles.add(realmManageClients);
+ realmClient.realm(TEST).users().get(realmUser.getId()).roles().clientLevel(client.getId()).add(roles);
+ realmClient.realm(TEST).users().get(realmUser.getId()).roles().clientLevel(client.getId()).remove(roles);
+
+ roles.clear();
+ roles.add(realmViewClients);
+ realmClient.realm(TEST).users().get(realmUser.getId()).roles().clientLevel(client.getId()).add(roles);
+ realmClient.realm(TEST).users().get(realmUser.getId()).roles().clientLevel(client.getId()).remove(roles);
+
+ roles.clear();
+ roles.add(realmManageEvents);
+ realmClient.realm(TEST).users().get(realmUser.getId()).roles().clientLevel(client.getId()).add(roles);
+ realmClient.realm(TEST).users().get(realmUser.getId()).roles().clientLevel(client.getId()).remove(roles);
+
+ roles.clear();
+ roles.add(realmViewEvents);
+ realmClient.realm(TEST).users().get(realmUser.getId()).roles().clientLevel(client.getId()).add(roles);
+ realmClient.realm(TEST).users().get(realmUser.getId()).roles().clientLevel(client.getId()).remove(roles);
+
+ roles.clear();
+ roles.add(realmManageIdentityProviders);
+ realmClient.realm(TEST).users().get(realmUser.getId()).roles().clientLevel(client.getId()).add(roles);
+ realmClient.realm(TEST).users().get(realmUser.getId()).roles().clientLevel(client.getId()).remove(roles);
+
+ roles.clear();
+ roles.add(realmViewIdentityProviders);
+ realmClient.realm(TEST).users().get(realmUser.getId()).roles().clientLevel(client.getId()).add(roles);
+ realmClient.realm(TEST).users().get(realmUser.getId()).roles().clientLevel(client.getId()).remove(roles);
+
+ roles.clear();
+ roles.add(realmManageRealm);
+ realmClient.realm(TEST).users().get(realmUser.getId()).roles().clientLevel(client.getId()).add(roles);
+ realmClient.realm(TEST).users().get(realmUser.getId()).roles().clientLevel(client.getId()).remove(roles);
+
+ roles.clear();
+ roles.add(realmViewRealm);
+ realmClient.realm(TEST).users().get(realmUser.getId()).roles().clientLevel(client.getId()).add(roles);
+ realmClient.realm(TEST).users().get(realmUser.getId()).roles().clientLevel(client.getId()).remove(roles);
+
+ roles.clear();
+ roles.add(realmImpersonate);
+ realmClient.realm(TEST).users().get(realmUser.getId()).roles().clientLevel(client.getId()).add(roles);
+ realmClient.realm(TEST).users().get(realmUser.getId()).roles().clientLevel(client.getId()).remove(roles);
+
+ roles.clear();
+ roles.add(realmManageUsers);
+ realmClient.realm(TEST).users().get(realmUser.getId()).roles().clientLevel(client.getId()).add(roles);
+ realmClient.realm(TEST).users().get(realmUser.getId()).roles().clientLevel(client.getId()).remove(roles);
+
+
+ roles.clear();
+ roles.add(realmViewUsers);
+ realmClient.realm(TEST).users().get(realmUser.getId()).roles().clientLevel(client.getId()).add(roles);
+ realmClient.realm(TEST).users().get(realmUser.getId()).roles().clientLevel(client.getId()).remove(roles);
+
+
+ roles.clear();
+ roles.add(realmQueryUsers);
+ realmClient.realm(TEST).users().get(realmUser.getId()).roles().clientLevel(client.getId()).add(roles);
+ realmClient.realm(TEST).users().get(realmUser.getId()).roles().clientLevel(client.getId()).remove(roles);
+
+
+ roles.clear();
+ roles.add(realmQueryGroups);
+ realmClient.realm(TEST).users().get(realmUser.getId()).roles().clientLevel(client.getId()).add(roles);
+ realmClient.realm(TEST).users().get(realmUser.getId()).roles().clientLevel(client.getId()).remove(roles);
+
+ roles.clear();
+ roles.add(realmQueryClients);
+ realmClient.realm(TEST).users().get(realmUser.getId()).roles().clientLevel(client.getId()).add(roles);
+ realmClient.realm(TEST).users().get(realmUser.getId()).roles().clientLevel(client.getId()).remove(roles);
+
+ realmClient.close();
+ }
+ // test that "admin" in master realm can assign all roles of master realm realm client admin roles
+ {
+ ClientRepresentation client = masterClient;
+ Keycloak realmClient = AdminClientUtil.createAdminClient();
+ roles.clear();
+ roles.add(masterManageAuthorization);
+ realmClient.realm("master").users().get(masterUser.getId()).roles().clientLevel(client.getId()).add(roles);
+ realmClient.realm("master").users().get(masterUser.getId()).roles().clientLevel(client.getId()).remove(roles);
+
+ roles.clear();
+ roles.add(masterViewAuthorization);
+ realmClient.realm("master").users().get(masterUser.getId()).roles().clientLevel(client.getId()).add(roles);
+ realmClient.realm("master").users().get(masterUser.getId()).roles().clientLevel(client.getId()).remove(roles);
+
+ roles.clear();
+ roles.add(masterManageClients);
+ realmClient.realm("master").users().get(masterUser.getId()).roles().clientLevel(client.getId()).add(roles);
+ realmClient.realm("master").users().get(masterUser.getId()).roles().clientLevel(client.getId()).remove(roles);
+
+ roles.clear();
+ roles.add(masterViewClients);
+ realmClient.realm("master").users().get(masterUser.getId()).roles().clientLevel(client.getId()).add(roles);
+ realmClient.realm("master").users().get(masterUser.getId()).roles().clientLevel(client.getId()).remove(roles);
+
+ roles.clear();
+ roles.add(masterManageEvents);
+ realmClient.realm("master").users().get(masterUser.getId()).roles().clientLevel(client.getId()).add(roles);
+ realmClient.realm("master").users().get(masterUser.getId()).roles().clientLevel(client.getId()).remove(roles);
+
+ roles.clear();
+ roles.add(masterViewEvents);
+ realmClient.realm("master").users().get(masterUser.getId()).roles().clientLevel(client.getId()).add(roles);
+ realmClient.realm("master").users().get(masterUser.getId()).roles().clientLevel(client.getId()).remove(roles);
+
+ roles.clear();
+ roles.add(masterManageIdentityProviders);
+ realmClient.realm("master").users().get(masterUser.getId()).roles().clientLevel(client.getId()).add(roles);
+ realmClient.realm("master").users().get(masterUser.getId()).roles().clientLevel(client.getId()).remove(roles);
+
+ roles.clear();
+ roles.add(masterViewIdentityProviders);
+ realmClient.realm("master").users().get(masterUser.getId()).roles().clientLevel(client.getId()).add(roles);
+ realmClient.realm("master").users().get(masterUser.getId()).roles().clientLevel(client.getId()).remove(roles);
+
+ roles.clear();
+ roles.add(masterManageRealm);
+ realmClient.realm("master").users().get(masterUser.getId()).roles().clientLevel(client.getId()).add(roles);
+ realmClient.realm("master").users().get(masterUser.getId()).roles().clientLevel(client.getId()).remove(roles);
+
+ roles.clear();
+ roles.add(masterViewRealm);
+ realmClient.realm("master").users().get(masterUser.getId()).roles().clientLevel(client.getId()).add(roles);
+ realmClient.realm("master").users().get(masterUser.getId()).roles().clientLevel(client.getId()).remove(roles);
+
+ roles.clear();
+ roles.add(masterImpersonate);
+ realmClient.realm("master").users().get(masterUser.getId()).roles().clientLevel(client.getId()).add(roles);
+ realmClient.realm("master").users().get(masterUser.getId()).roles().clientLevel(client.getId()).remove(roles);
+
+ roles.clear();
+ roles.add(masterManageUsers);
+ realmClient.realm("master").users().get(masterUser.getId()).roles().clientLevel(client.getId()).add(roles);
+ realmClient.realm("master").users().get(masterUser.getId()).roles().clientLevel(client.getId()).remove(roles);
+
+
+ roles.clear();
+ roles.add(masterViewUsers);
+ realmClient.realm("master").users().get(masterUser.getId()).roles().clientLevel(client.getId()).add(roles);
+ realmClient.realm("master").users().get(masterUser.getId()).roles().clientLevel(client.getId()).remove(roles);
+
+
+ roles.clear();
+ roles.add(masterQueryUsers);
+ realmClient.realm("master").users().get(masterUser.getId()).roles().clientLevel(client.getId()).add(roles);
+ realmClient.realm("master").users().get(masterUser.getId()).roles().clientLevel(client.getId()).remove(roles);
+
+
+ roles.clear();
+ roles.add(masterQueryGroups);
+ realmClient.realm("master").users().get(masterUser.getId()).roles().clientLevel(client.getId()).add(roles);
+ realmClient.realm("master").users().get(masterUser.getId()).roles().clientLevel(client.getId()).remove(roles);
+
+ roles.clear();
+ roles.add(masterQueryClients);
+ realmClient.realm("master").users().get(masterUser.getId()).roles().clientLevel(client.getId()).add(roles);
+ realmClient.realm("master").users().get(masterUser.getId()).roles().clientLevel(client.getId()).remove(roles);
+
+ realmClient.close();
+ }
+ }
+
+
+
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/PermissionsTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/PermissionsTest.java
index 531157d..16b0804 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/PermissionsTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/PermissionsTest.java
@@ -51,7 +51,7 @@ import org.keycloak.representations.idm.authorization.PolicyRepresentation;
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
import org.keycloak.representations.idm.authorization.ResourceServerRepresentation;
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
-import org.keycloak.services.resources.admin.RealmAuth.Resource;
+import org.keycloak.services.resources.admin.AdminAuth.Resource;
import org.keycloak.testsuite.AbstractKeycloakTest;
import org.keycloak.testsuite.Assert;
import org.keycloak.testsuite.arquillian.AuthServerTestEnricher;
@@ -77,8 +77,8 @@ import java.util.concurrent.atomic.AtomicReference;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail;
-import static org.keycloak.services.resources.admin.RealmAuth.Resource.AUTHORIZATION;
-import static org.keycloak.services.resources.admin.RealmAuth.Resource.CLIENT;
+import static org.keycloak.services.resources.admin.AdminAuth.Resource.AUTHORIZATION;
+import static org.keycloak.services.resources.admin.AdminAuth.Resource.CLIENT;
import org.keycloak.testsuite.ProfileAssume;
/**
@@ -313,11 +313,18 @@ public class PermissionsTest extends AbstractKeycloakTest {
realm.removeDefaultGroup("nosuch");
}
}, Resource.REALM, true);
+ GroupRepresentation newGroup = new GroupRepresentation();
+ newGroup.setName("sample");
+ adminClient.realm(REALM_NAME).groups().add(newGroup);
+ GroupRepresentation group = adminClient.realms().realm(REALM_NAME).getGroupByPath("sample");
+
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.getGroupByPath("nosuch");
+ realm.getGroupByPath("sample");
}
- }, Resource.REALM, false);
+ }, Resource.USER, false);
+
+ adminClient.realms().realm(REALM_NAME).groups().group(group.getId()).remove();
invoke(new InvocationWithResponse() {
public void invoke(RealmResource realm, AtomicReference<Response> response) {
@@ -425,14 +432,19 @@ public class PermissionsTest extends AbstractKeycloakTest {
@Test
public void attackDetection() {
+ UserRepresentation newUser = new UserRepresentation();
+ newUser.setUsername("attacked");
+ newUser.setEnabled(true);
+ adminClient.realms().realm(REALM_NAME).users().create(newUser);
+ UserRepresentation user = adminClient.realms().realm(REALM_NAME).users().search("attacked").get(0);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.attackDetection().bruteForceUserStatus("nosuch");
+ realm.attackDetection().bruteForceUserStatus(user.getId());
}
}, Resource.USER, false);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.attackDetection().clearBruteForceForUser("nosuch");
+ realm.attackDetection().clearBruteForceForUser(user.getId());
}
}, Resource.USER, true);
invoke(new Invocation() {
@@ -440,6 +452,7 @@ public class PermissionsTest extends AbstractKeycloakTest {
realm.attackDetection().clearAllBruteForce();
}
}, Resource.USER, true);
+ adminClient.realms().realm(REALM_NAME).users().get(user.getId()).remove();
}
@Test
@@ -462,183 +475,187 @@ public class PermissionsTest extends AbstractKeycloakTest {
response.set(realm.clients().create(ClientBuilder.create().clientId("foo").build()));
}
}, Resource.CLIENT, true);
+ ClientRepresentation foo = adminClient.realms().realm(REALM_NAME).clients().findByClientId("foo").get(0);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.clients().get("nosuch").toRepresentation();
+ realm.clients().get(foo.getId()).toRepresentation();
}
}, Resource.CLIENT, false);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.clients().get("nosuch").getInstallationProvider("nosuch");
+ realm.clients().get(foo.getId()).getInstallationProvider("nosuch");
}
}, Resource.CLIENT, false);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.clients().get("nosuch").update(new ClientRepresentation());
+ realm.clients().get(foo.getId()).update(foo);
}
}, Resource.CLIENT, true);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.clients().get("nosuch").remove();
+ realm.clients().get(foo.getId()).remove();
+ realm.clients().create(foo);
+ ClientRepresentation temp = realm.clients().findByClientId("foo").get(0);
+ foo.setId(temp.getId());
}
}, Resource.CLIENT, true);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.clients().get("nosuch").generateNewSecret();
+ realm.clients().get(foo.getId()).generateNewSecret();
}
}, Resource.CLIENT, true);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.clients().get("nosuch").regenerateRegistrationAccessToken();
+ realm.clients().get(foo.getId()).regenerateRegistrationAccessToken();
}
}, Resource.CLIENT, true);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.clients().get("nosuch").getSecret();
+ realm.clients().get(foo.getId()).getSecret();
}
}, Resource.CLIENT, false);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.clients().get("nosuch").getServiceAccountUser();
+ realm.clients().get(foo.getId()).getServiceAccountUser();
}
}, Resource.CLIENT, false);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.clients().get("nosuch").pushRevocation();
+ realm.clients().get(foo.getId()).pushRevocation();
}
}, Resource.CLIENT, true);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.clients().get("nosuch").getApplicationSessionCount();
+ realm.clients().get(foo.getId()).getApplicationSessionCount();
}
}, Resource.CLIENT, false);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.clients().get("nosuch").getUserSessions(0, 100);
+ realm.clients().get(foo.getId()).getUserSessions(0, 100);
}
}, Resource.CLIENT, false);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.clients().get("nosuch").getOfflineSessionCount();
+ realm.clients().get(foo.getId()).getOfflineSessionCount();
}
}, Resource.CLIENT, false);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.clients().get("nosuch").getOfflineUserSessions(0, 100);
+ realm.clients().get(foo.getId()).getOfflineUserSessions(0, 100);
}
}, Resource.CLIENT, false);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.clients().get("nosuch").registerNode(Collections.<String, String>emptyMap());
+ realm.clients().get(foo.getId()).registerNode(Collections.<String, String>emptyMap());
}
}, Resource.CLIENT, true);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.clients().get("nosuch").unregisterNode("nosuch");
+ realm.clients().get(foo.getId()).unregisterNode("nosuch");
}
}, Resource.CLIENT, true);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.clients().get("nosuch").testNodesAvailable();
+ realm.clients().get(foo.getId()).testNodesAvailable();
}
}, Resource.CLIENT, true);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.clients().get("nosuch").getCertficateResource("nosuch").generate();
+ realm.clients().get(foo.getId()).getCertficateResource("nosuch").generate();
}
}, Resource.CLIENT, true);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.clients().get("nosuch").getCertficateResource("nosuch").generateAndGetKeystore(new KeyStoreConfig());
+ realm.clients().get(foo.getId()).getCertficateResource("nosuch").generateAndGetKeystore(new KeyStoreConfig());
}
}, Resource.CLIENT, true);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.clients().get("nosuch").getCertficateResource("nosuch").getKeyInfo();
+ realm.clients().get(foo.getId()).getCertficateResource("nosuch").getKeyInfo();
}
}, Resource.CLIENT, false);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.clients().get("nosuch").getCertficateResource("nosuch").getKeystore(new KeyStoreConfig());
+ realm.clients().get(foo.getId()).getCertficateResource("nosuch").getKeystore(new KeyStoreConfig());
}
}, Resource.CLIENT, false);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.clients().get("nosuch").getCertficateResource("nosuch").uploadJks(new MultipartFormDataOutput());
+ realm.clients().get(foo.getId()).getCertficateResource("nosuch").uploadJks(new MultipartFormDataOutput());
}
}, Resource.CLIENT, true);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.clients().get("nosuch").getCertficateResource("nosuch").uploadJksCertificate(new MultipartFormDataOutput());
+ realm.clients().get(foo.getId()).getCertficateResource("nosuch").uploadJksCertificate(new MultipartFormDataOutput());
}
}, Resource.CLIENT, true);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.clients().get("nosuch").getProtocolMappers().createMapper(Collections.EMPTY_LIST);
+ realm.clients().get(foo.getId()).getProtocolMappers().createMapper(Collections.EMPTY_LIST);
}
}, Resource.CLIENT, true);
invoke(new InvocationWithResponse() {
public void invoke(RealmResource realm, AtomicReference<Response> response) {
- response.set(realm.clients().get("nosuch").getProtocolMappers().createMapper(new ProtocolMapperRepresentation()));
+ response.set(realm.clients().get(foo.getId()).getProtocolMappers().createMapper(new ProtocolMapperRepresentation()));
}
}, Resource.CLIENT, true);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.clients().get("nosuch").getProtocolMappers().getMapperById("nosuch");
+ realm.clients().get(foo.getId()).getProtocolMappers().getMapperById("nosuch");
}
}, Resource.CLIENT, false, true);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.clients().get("nosuch").getProtocolMappers().getMappers();
+ realm.clients().get(foo.getId()).getProtocolMappers().getMappers();
}
}, Resource.CLIENT, false, true);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.clients().get("nosuch").getProtocolMappers().getMappersPerProtocol("nosuch");
+ realm.clients().get(foo.getId()).getProtocolMappers().getMappersPerProtocol("nosuch");
}
}, Resource.CLIENT, false, true);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.clients().get("nosuch").getProtocolMappers().update("nosuch", new ProtocolMapperRepresentation());
+ realm.clients().get(foo.getId()).getProtocolMappers().update("nosuch", new ProtocolMapperRepresentation());
}
}, Resource.CLIENT, true);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.clients().get("nosuch").getProtocolMappers().delete("nosuch");
+ realm.clients().get(foo.getId()).getProtocolMappers().delete("nosuch");
}
}, Resource.CLIENT, true);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.clients().get("nosuch").getScopeMappings().getAll();
+ realm.clients().get(foo.getId()).getScopeMappings().getAll();
}
}, Resource.CLIENT, false);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.clients().get("nosuch").getScopeMappings().realmLevel().listAll();
+ realm.clients().get(foo.getId()).getScopeMappings().realmLevel().listAll();
}
}, Resource.CLIENT, false);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.clients().get("nosuch").getScopeMappings().realmLevel().listEffective();
+ realm.clients().get(foo.getId()).getScopeMappings().realmLevel().listEffective();
}
}, Resource.CLIENT, false);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.clients().get("nosuch").getScopeMappings().realmLevel().listAvailable();
+ realm.clients().get(foo.getId()).getScopeMappings().realmLevel().listAvailable();
}
}, Resource.CLIENT, false);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.clients().get("nosuch").getScopeMappings().realmLevel().add(Collections.<RoleRepresentation>emptyList());
+ realm.clients().get(foo.getId()).getScopeMappings().realmLevel().add(Collections.<RoleRepresentation>emptyList());
}
}, Resource.CLIENT, true);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.clients().get("nosuch").getScopeMappings().realmLevel().remove(Collections.<RoleRepresentation>emptyList());
+ realm.clients().get(foo.getId()).getScopeMappings().realmLevel().remove(Collections.<RoleRepresentation>emptyList());
}
}, Resource.CLIENT, true);
@@ -649,47 +666,47 @@ public class PermissionsTest extends AbstractKeycloakTest {
}, Resource.CLIENT, false, true);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.clients().get("nosuch").roles().create(new RoleRepresentation());
+ realm.clients().get(foo.getId()).roles().create(new RoleRepresentation());
}
}, Resource.CLIENT, true);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.clients().get("nosuch").roles().get("nosuch").toRepresentation();
+ realm.clients().get(foo.getId()).roles().get("nosuch").toRepresentation();
}
}, Resource.CLIENT, false);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.clients().get("nosuch").roles().deleteRole("nosuch");
+ realm.clients().get(foo.getId()).roles().deleteRole("nosuch");
}
}, Resource.CLIENT, true);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.clients().get("nosuch").roles().get("nosuch").update(new RoleRepresentation());
+ realm.clients().get(foo.getId()).roles().get("nosuch").update(new RoleRepresentation());
}
}, Resource.CLIENT, true);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.clients().get("nosuch").roles().get("nosuch").addComposites(Collections.<RoleRepresentation>emptyList());
+ realm.clients().get(foo.getId()).roles().get("nosuch").addComposites(Collections.<RoleRepresentation>emptyList());
}
}, Resource.CLIENT, true);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.clients().get("nosuch").roles().get("nosuch").deleteComposites(Collections.<RoleRepresentation>emptyList());
+ realm.clients().get(foo.getId()).roles().get("nosuch").deleteComposites(Collections.<RoleRepresentation>emptyList());
}
}, Resource.CLIENT, true);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.clients().get("nosuch").roles().get("nosuch").getRoleComposites();
+ realm.clients().get(foo.getId()).roles().get("nosuch").getRoleComposites();
}
}, Resource.CLIENT, false);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.clients().get("nosuch").roles().get("nosuch").getRealmRoleComposites();
+ realm.clients().get(foo.getId()).roles().get("nosuch").getRealmRoleComposites();
}
}, Resource.CLIENT, false);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.clients().get("nosuch").roles().get("nosuch").getClientRoleComposites("nosuch");
+ realm.clients().get(foo.getId()).roles().get("nosuch").getClientRoleComposites("nosuch");
}
}, Resource.CLIENT, false);
}
@@ -700,117 +717,123 @@ public class PermissionsTest extends AbstractKeycloakTest {
public void invoke(RealmResource realm) {
realm.clientTemplates().findAll();
}
- }, Resource.CLIENT, false);
+ }, Resource.CLIENT, false, true);
invoke(new InvocationWithResponse() {
public void invoke(RealmResource realm, AtomicReference<Response> response) {
- response.set(realm.clientTemplates().create(new ClientTemplateRepresentation()));
+ ClientTemplateRepresentation template = new ClientTemplateRepresentation();
+ template.setName("template");
+ response.set(realm.clientTemplates().create(template));
}
}, Resource.CLIENT, true);
+
+ ClientTemplateRepresentation template = adminClient.realms().realm(REALM_NAME).clientTemplates().findAll().get(0);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.clientTemplates().get("nosuch").toRepresentation();
+ realm.clientTemplates().get(template.getId()).toRepresentation();
}
}, Resource.CLIENT, false);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.clientTemplates().get("nosuch").update(new ClientTemplateRepresentation());
+ realm.clientTemplates().get(template.getId()).update(template);
}
}, Resource.CLIENT, true);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.clientTemplates().get("nosuch").remove();
+ realm.clientTemplates().get(template.getId()).remove();
+ realm.clientTemplates().create(template);
}
}, Resource.CLIENT, true);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.clientTemplates().get("nosuch").getProtocolMappers().getMappers();
+ realm.clientTemplates().get(template.getId()).getProtocolMappers().getMappers();
}
}, Resource.CLIENT, false, true);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.clientTemplates().get("nosuch").getProtocolMappers().getMappersPerProtocol("nosuch");
+ realm.clientTemplates().get(template.getId()).getProtocolMappers().getMappersPerProtocol("nosuch");
}
}, Resource.CLIENT, false, true);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.clientTemplates().get("nosuch").getProtocolMappers().getMapperById("nosuch");
+ realm.clientTemplates().get(template.getId()).getProtocolMappers().getMapperById("nosuch");
}
}, Resource.CLIENT, false, true);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.clientTemplates().get("nosuch").getProtocolMappers().update("nosuch", new ProtocolMapperRepresentation());
+ realm.clientTemplates().get(template.getId()).getProtocolMappers().update("nosuch", new ProtocolMapperRepresentation());
}
}, Resource.CLIENT, true);
invoke(new InvocationWithResponse() {
public void invoke(RealmResource realm, AtomicReference<Response> response) {
- response.set(realm.clientTemplates().get("nosuch").getProtocolMappers().createMapper(new ProtocolMapperRepresentation()));
+ response.set(realm.clientTemplates().get(template.getId()).getProtocolMappers().createMapper(new ProtocolMapperRepresentation()));
}
}, Resource.CLIENT, true);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.clientTemplates().get("nosuch").getProtocolMappers().createMapper(Collections.<ProtocolMapperRepresentation>emptyList());
+ realm.clientTemplates().get(template.getId()).getProtocolMappers().createMapper(Collections.<ProtocolMapperRepresentation>emptyList());
}
}, Resource.CLIENT, true);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.clientTemplates().get("nosuch").getProtocolMappers().delete("nosuch");
+ realm.clientTemplates().get(template.getId()).getProtocolMappers().delete("nosuch");
}
}, Resource.CLIENT, true);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.clientTemplates().get("nosuch").getScopeMappings().getAll();
+ realm.clientTemplates().get(template.getId()).getScopeMappings().getAll();
}
}, Resource.CLIENT, false);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.clientTemplates().get("nosuch").getScopeMappings().realmLevel().listAll();
+ realm.clientTemplates().get(template.getId()).getScopeMappings().realmLevel().listAll();
}
}, Resource.CLIENT, false);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.clientTemplates().get("nosuch").getScopeMappings().realmLevel().listAvailable();
+ realm.clientTemplates().get(template.getId()).getScopeMappings().realmLevel().listAvailable();
}
}, Resource.CLIENT, false);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.clientTemplates().get("nosuch").getScopeMappings().realmLevel().listEffective();
+ realm.clientTemplates().get(template.getId()).getScopeMappings().realmLevel().listEffective();
}
}, Resource.CLIENT, false);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.clientTemplates().get("nosuch").getScopeMappings().realmLevel().add(Collections.<RoleRepresentation>emptyList());
+ realm.clientTemplates().get(template.getId()).getScopeMappings().realmLevel().add(Collections.<RoleRepresentation>emptyList());
}
}, Resource.CLIENT, true);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.clientTemplates().get("nosuch").getScopeMappings().realmLevel().remove(Collections.<RoleRepresentation>emptyList());
+ realm.clientTemplates().get(template.getId()).getScopeMappings().realmLevel().remove(Collections.<RoleRepresentation>emptyList());
}
}, Resource.CLIENT, true);
+ ClientRepresentation realmAccessClient = adminClient.realms().realm(REALM_NAME).clients().findByClientId(Constants.REALM_MANAGEMENT_CLIENT_ID).get(0);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.clientTemplates().get("nosuch").getScopeMappings().clientLevel("nosuch").listAll();
+ realm.clientTemplates().get(template.getId()).getScopeMappings().clientLevel(realmAccessClient.getId()).listAll();
}
}, Resource.CLIENT, false);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.clientTemplates().get("nosuch").getScopeMappings().clientLevel("nosuch").listAvailable();
+ realm.clientTemplates().get(template.getId()).getScopeMappings().clientLevel(realmAccessClient.getId()).listAvailable();
}
}, Resource.CLIENT, false);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.clientTemplates().get("nosuch").getScopeMappings().clientLevel("nosuch").listEffective();
+ realm.clientTemplates().get(template.getId()).getScopeMappings().clientLevel(realmAccessClient.getId()).listEffective();
}
}, Resource.CLIENT, false);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.clientTemplates().get("nosuch").getScopeMappings().clientLevel("nosuch").add(Collections.<RoleRepresentation>emptyList());
+ realm.clientTemplates().get(template.getId()).getScopeMappings().clientLevel(realmAccessClient.getId()).add(Collections.<RoleRepresentation>emptyList());
}
}, Resource.CLIENT, true);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.clientTemplates().get("nosuch").getScopeMappings().clientLevel("nosuch").remove(Collections.<RoleRepresentation>emptyList());
+ realm.clientTemplates().get(template.getId()).getScopeMappings().clientLevel(realmAccessClient.getId()).remove(Collections.<RoleRepresentation>emptyList());
}
}, Resource.CLIENT, true);
}
@@ -837,10 +860,13 @@ public class PermissionsTest extends AbstractKeycloakTest {
@Test
public void clientAuthorization() {
ProfileAssume.assumePreview();
+
+ ClientRepresentation newClient = new ClientRepresentation();
+ newClient.setClientId("foo-authz");
+ adminClient.realms().realm(REALM_NAME).clients().create(newClient);
+ ClientRepresentation foo = adminClient.realms().realm(REALM_NAME).clients().findByClientId("foo-authz").get(0);
invoke(new InvocationWithResponse() {
public void invoke(RealmResource realm, AtomicReference<Response> response) {
- realm.clients().create(ClientBuilder.create().clientId("foo-authz").build());
- org.keycloak.representations.idm.ClientRepresentation foo = realm.clients().findByClientId("foo-authz").get(0);
foo.setServiceAccountsEnabled(true);
foo.setAuthorizationServicesEnabled(true);
realm.clients().get(foo.getId()).update(foo);
@@ -848,13 +874,11 @@ public class PermissionsTest extends AbstractKeycloakTest {
}, CLIENT, true);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- org.keycloak.representations.idm.ClientRepresentation foo = realm.clients().findByClientId("foo-authz").get(0);
realm.clients().get(foo.getId()).authorization().getSettings();
}
}, AUTHORIZATION, false);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- org.keycloak.representations.idm.ClientRepresentation foo = realm.clients().findByClientId("foo-authz").get(0);
AuthorizationResource authorization = realm.clients().get(foo.getId()).authorization();
ResourceServerRepresentation settings = authorization.getSettings();
authorization.update(settings);
@@ -862,42 +886,36 @@ public class PermissionsTest extends AbstractKeycloakTest {
}, AUTHORIZATION, true);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- org.keycloak.representations.idm.ClientRepresentation foo = realm.clients().findByClientId("foo-authz").get(0);
AuthorizationResource authorization = realm.clients().get(foo.getId()).authorization();
authorization.resources().resources();
}
}, AUTHORIZATION, false);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- org.keycloak.representations.idm.ClientRepresentation foo = realm.clients().findByClientId("foo-authz").get(0);
AuthorizationResource authorization = realm.clients().get(foo.getId()).authorization();
authorization.scopes().scopes();
}
}, AUTHORIZATION, false);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- org.keycloak.representations.idm.ClientRepresentation foo = realm.clients().findByClientId("foo-authz").get(0);
AuthorizationResource authorization = realm.clients().get(foo.getId()).authorization();
authorization.policies().policies();
}
}, AUTHORIZATION, false);
invoke(new InvocationWithResponse() {
public void invoke(RealmResource realm, AtomicReference<Response> response) {
- org.keycloak.representations.idm.ClientRepresentation foo = realm.clients().findByClientId("foo-authz").get(0);
AuthorizationResource authorization = realm.clients().get(foo.getId()).authorization();
response.set(authorization.resources().create(new ResourceRepresentation("Test", Collections.emptySet())));
}
}, AUTHORIZATION, true);
invoke(new InvocationWithResponse() {
public void invoke(RealmResource realm, AtomicReference<Response> response) {
- org.keycloak.representations.idm.ClientRepresentation foo = realm.clients().findByClientId("foo-authz").get(0);
AuthorizationResource authorization = realm.clients().get(foo.getId()).authorization();
response.set(authorization.scopes().create(new ScopeRepresentation("Test")));
}
}, AUTHORIZATION, true);
invoke(new InvocationWithResponse() {
public void invoke(RealmResource realm, AtomicReference<Response> response) {
- org.keycloak.representations.idm.ClientRepresentation foo = realm.clients().findByClientId("foo-authz").get(0);
AuthorizationResource authorization = realm.clients().get(foo.getId()).authorization();
PolicyRepresentation representation = new PolicyRepresentation();
representation.setName("Test PermissionsTest");
@@ -910,42 +928,36 @@ public class PermissionsTest extends AbstractKeycloakTest {
}, AUTHORIZATION, true);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- org.keycloak.representations.idm.ClientRepresentation foo = realm.clients().findByClientId("foo-authz").get(0);
AuthorizationResource authorization = realm.clients().get(foo.getId()).authorization();
authorization.resources().resource("nosuch").update(new ResourceRepresentation());
}
}, AUTHORIZATION, true);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- org.keycloak.representations.idm.ClientRepresentation foo = realm.clients().findByClientId("foo-authz").get(0);
AuthorizationResource authorization = realm.clients().get(foo.getId()).authorization();
authorization.scopes().scope("nosuch").update(new ScopeRepresentation());
}
}, AUTHORIZATION, true);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- org.keycloak.representations.idm.ClientRepresentation foo = realm.clients().findByClientId("foo-authz").get(0);
AuthorizationResource authorization = realm.clients().get(foo.getId()).authorization();
authorization.policies().policy("nosuch").update(new PolicyRepresentation());
}
}, AUTHORIZATION, true);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- org.keycloak.representations.idm.ClientRepresentation foo = realm.clients().findByClientId("foo-authz").get(0);
AuthorizationResource authorization = realm.clients().get(foo.getId()).authorization();
authorization.resources().resource("nosuch").remove();
}
}, AUTHORIZATION, true);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- org.keycloak.representations.idm.ClientRepresentation foo = realm.clients().findByClientId("foo-authz").get(0);
AuthorizationResource authorization = realm.clients().get(foo.getId()).authorization();
authorization.scopes().scope("nosuch").remove();
}
}, AUTHORIZATION, true);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- org.keycloak.representations.idm.ClientRepresentation foo = realm.clients().findByClientId("foo-authz").get(0);
AuthorizationResource authorization = realm.clients().get(foo.getId()).authorization();
authorization.policies().policy("nosuch").remove();
}
@@ -954,6 +966,10 @@ public class PermissionsTest extends AbstractKeycloakTest {
@Test
public void roles() {
+ RoleRepresentation newRole = new RoleRepresentation();
+ newRole.setName("sample-role");
+ adminClient.realm(REALM_NAME).roles().create(newRole);
+
invoke(new Invocation() {
public void invoke(RealmResource realm) {
realm.roles().list();
@@ -961,12 +977,12 @@ public class PermissionsTest extends AbstractKeycloakTest {
}, Resource.REALM, false, true);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.roles().get("nosuch").toRepresentation();
+ realm.roles().get("sample-role").toRepresentation();
}
}, Resource.REALM, false);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.roles().get("nosuch").update(new RoleRepresentation());
+ realm.roles().get("sample-role").update(newRole);
}
}, Resource.REALM, true);
invoke(new Invocation() {
@@ -976,39 +992,42 @@ public class PermissionsTest extends AbstractKeycloakTest {
}, Resource.REALM, true);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.roles().deleteRole("nosuch");
+ realm.roles().deleteRole("sample-role");
+ // need to recreate for other tests
+ realm.roles().create(newRole);
}
}, Resource.REALM, true);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.roles().get("nosuch").getRoleComposites();
+ realm.roles().get("sample-role").getRoleComposites();
}
}, Resource.REALM, false);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.roles().get("nosuch").addComposites(Collections.<RoleRepresentation>emptyList());
+ realm.roles().get("sample-role").addComposites(Collections.<RoleRepresentation>emptyList());
}
}, Resource.REALM, true);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.roles().get("nosuch").deleteComposites(Collections.<RoleRepresentation>emptyList());
+ realm.roles().get("sample-role").deleteComposites(Collections.<RoleRepresentation>emptyList());
}
}, Resource.REALM, true);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.roles().get("nosuch").getRoleComposites();
+ realm.roles().get("sample-role").getRoleComposites();
}
}, Resource.REALM, false);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.roles().get("nosuch").getRealmRoleComposites();
+ realm.roles().get("sample-role").getRealmRoleComposites();
}
}, Resource.REALM, false);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.roles().get("nosuch").getClientRoleComposites("nosuch");
+ realm.roles().get("sample-role").getClientRoleComposites("nosuch");
}
}, Resource.REALM, false);
+ adminClient.realms().realm(REALM_NAME).roles().deleteRole("sample-role");
}
@Test
@@ -1175,51 +1194,61 @@ public class PermissionsTest extends AbstractKeycloakTest {
@Test
public void rolesById() {
+ RoleRepresentation newRole = new RoleRepresentation();
+ newRole.setName("role-by-id");
+ adminClient.realm(REALM_NAME).roles().create(newRole);
+ RoleRepresentation role = adminClient.realm(REALM_NAME).roles().get("role-by-id").toRepresentation();
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.rolesById().getRole("nosuch");
+ realm.rolesById().getRole(role.getId());
}
}, Resource.REALM, false, true);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.rolesById().updateRole("nosuch", new RoleRepresentation());
+ realm.rolesById().updateRole(role.getId(), role);
}
}, Resource.REALM, true);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.rolesById().deleteRole("nosuch");
+ realm.rolesById().deleteRole(role.getId());
+ // need to recreate for other tests
+ realm.roles().create(newRole);
+ RoleRepresentation temp = realm.roles().get("role-by-id").toRepresentation();
+ role.setId(temp.getId());
}
}, Resource.REALM, true);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.rolesById().getRoleComposites("nosuch");
+ realm.rolesById().getRoleComposites(role.getId());
}
}, Resource.REALM, false, true);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.rolesById().addComposites("nosuch", Collections.<RoleRepresentation>emptyList());
+ realm.rolesById().addComposites(role.getId(), Collections.<RoleRepresentation>emptyList());
}
}, Resource.REALM, true);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.rolesById().deleteComposites("nosuch", Collections.<RoleRepresentation>emptyList());
+ realm.rolesById().deleteComposites(role.getId(), Collections.<RoleRepresentation>emptyList());
}
}, Resource.REALM, true);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.rolesById().getRoleComposites("nosuch");
+ realm.rolesById().getRoleComposites(role.getId());
}
}, Resource.REALM, false, true);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.rolesById().getRealmRoleComposites("nosuch");
+ realm.rolesById().getRealmRoleComposites(role.getId());
}
}, Resource.REALM, false, true);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.rolesById().getClientRoleComposites("nosuch", "nosuch");
+ realm.rolesById().getClientRoleComposites(role.getId(), "nosuch");
}
}, Resource.REALM, false, true);
+
+ adminClient.realm(REALM_NAME).roles().deleteRole("role-by-id");
}
@Test
@@ -1237,85 +1266,95 @@ public class PermissionsTest extends AbstractKeycloakTest {
}
}, Resource.USER, true);
+ GroupRepresentation group = adminClient.realms().realm(REALM_NAME).getGroupByPath("mygroup");
+ ClientRepresentation realmAccessClient = adminClient.realms().realm(REALM_NAME).clients().findByClientId(Constants.REALM_MANAGEMENT_CLIENT_ID).get(0);
+
+
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.groups().group("nosuch").toRepresentation();
+ realm.groups().group(group.getId()).toRepresentation();
}
}, Resource.USER, false);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.groups().group("nosuch").update(new GroupRepresentation());
- }
- }, Resource.USER, true);
- invoke(new Invocation() {
- public void invoke(RealmResource realm) {
- realm.groups().group("nosuch").remove();
+ realm.groups().group(group.getId()).update(group);
}
}, Resource.USER, true);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.groups().group("nosuch").members(0, 100);
+ realm.groups().group(group.getId()).members(0, 100);
}
}, Resource.USER, false);
invoke(new InvocationWithResponse() {
public void invoke(RealmResource realm, AtomicReference<Response> response) {
- response.set(realm.groups().group("nosuch").subGroup(new GroupRepresentation()));
+ GroupRepresentation subgroup = new GroupRepresentation();
+ subgroup.setName("sub");
+ response.set(realm.groups().group(group.getId()).subGroup(subgroup));
}
}, Resource.USER, true);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.groups().group("nosuch").roles().getAll();
+ realm.groups().group(group.getId()).roles().getAll();
}
}, Resource.USER, false);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.groups().group("nosuch").roles().realmLevel().listAll();
+ realm.groups().group(group.getId()).roles().realmLevel().listAll();
}
}, Resource.USER, false);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.groups().group("nosuch").roles().realmLevel().listEffective();
+ realm.groups().group(group.getId()).roles().realmLevel().listEffective();
}
}, Resource.USER, false);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.groups().group("nosuch").roles().realmLevel().listAvailable();
+ realm.groups().group(group.getId()).roles().realmLevel().listAvailable();
}
}, Resource.USER, false);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.groups().group("nosuch").roles().realmLevel().add(Collections.<RoleRepresentation>emptyList());
+ realm.groups().group(group.getId()).roles().realmLevel().add(Collections.<RoleRepresentation>emptyList());
}
}, Resource.USER, true);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.groups().group("nosuch").roles().realmLevel().remove(Collections.<RoleRepresentation>emptyList());
+ realm.groups().group(group.getId()).roles().realmLevel().remove(Collections.<RoleRepresentation>emptyList());
}
}, Resource.USER, true);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.groups().group("nosuch").roles().clientLevel("nosuch").listAll();
+ realm.groups().group(group.getId()).roles().clientLevel(realmAccessClient.getId()).listAll();
}
}, Resource.USER, false);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.groups().group("nosuch").roles().clientLevel("nosuch").listEffective();
+ realm.groups().group(group.getId()).roles().clientLevel(realmAccessClient.getId()).listEffective();
}
}, Resource.USER, false);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.groups().group("nosuch").roles().clientLevel("nosuch").listAvailable();
+ realm.groups().group(group.getId()).roles().clientLevel(realmAccessClient.getId()).listAvailable();
}
}, Resource.USER, false);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.groups().group("nosuch").roles().clientLevel("nosuch").add(Collections.<RoleRepresentation>emptyList());
+ realm.groups().group(group.getId()).roles().clientLevel(realmAccessClient.getId()).add(Collections.<RoleRepresentation>emptyList());
}
}, Resource.USER, true);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.groups().group("nosuch").roles().clientLevel("nosuch").remove(Collections.<RoleRepresentation>emptyList());
+ realm.groups().group(group.getId()).roles().clientLevel(realmAccessClient.getId()).remove(Collections.<RoleRepresentation>emptyList());
+ }
+ }, Resource.USER, true);
+ invoke(new Invocation() {
+ public void invoke(RealmResource realm) {
+ realm.groups().group(group.getId()).remove();
+ group.setId(null);
+ realm.groups().add(group);
+ GroupRepresentation temp = realm.getGroupByPath("mygroup");
+ group.setId(temp.getId());
}
}, Resource.USER, true);
}
@@ -1323,176 +1362,181 @@ public class PermissionsTest extends AbstractKeycloakTest {
// Permissions for impersonation tested in ImpersonationTest
@Test
public void users() {
- invoke(new Invocation() {
- public void invoke(RealmResource realm) {
- realm.users().get("nosuch").toRepresentation();
- }
- }, Resource.USER, false);
invoke(new InvocationWithResponse() {
public void invoke(RealmResource realm, AtomicReference<Response> response) {
response.set(realm.users().create(UserBuilder.create().username("testuser").build()));
}
}, Resource.USER, true);
+ UserRepresentation user = adminClient.realms().realm(REALM_NAME).users().search("testuser").get(0);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.users().get("nosuch").update(UserBuilder.create().enabled(true).build());
+ realm.users().get(user.getId()).remove();
+ realm.users().create(user);
+ UserRepresentation temp = realm.users().search("testuser").get(0);
+ user.setId(temp.getId());
}
}, Resource.USER, true);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.users().search("foo", 0, 1);
+ realm.users().get(user.getId()).toRepresentation();
}
}, Resource.USER, false);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
+ realm.users().get(user.getId()).update(user);
+ }
+ }, Resource.USER, true);
+ invoke(new Invocation() {
+ public void invoke(RealmResource realm) {
realm.users().count();
}
}, Resource.USER, false);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.users().get("nosuch").getUserSessions();
+ realm.users().get(user.getId()).getUserSessions();
}
}, Resource.USER, false);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.users().get("nosuch").getOfflineSessions("nosuch");
+ realm.users().get(user.getId()).getOfflineSessions("nosuch");
}
}, Resource.USER, false);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.users().get("nosuch").getFederatedIdentity();
+ realm.users().get(user.getId()).getFederatedIdentity();
}
}, Resource.USER, false);
invoke(new InvocationWithResponse() {
public void invoke(RealmResource realm, AtomicReference<Response> response) {
response.set(realm.users()
- .get("nosuch")
+ .get(user.getId())
.addFederatedIdentity("nosuch",
FederatedIdentityBuilder.create().identityProvider("nosuch").userId("nosuch").userName("nosuch").build()));
}
}, Resource.USER, true);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.users().get("nosuch").removeFederatedIdentity("nosuch");
+ realm.users().get(user.getId()).removeFederatedIdentity("nosuch");
}
}, Resource.USER, true);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.users().get("nosuch").getConsents();
+ realm.users().get(user.getId()).getConsents();
}
}, Resource.USER, false);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.users().get("nosuch").revokeConsent("testclient");
+ realm.users().get(user.getId()).revokeConsent("testclient");
}
}, Resource.USER, true);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.users().get("nosuch").logout();
+ realm.users().get(user.getId()).logout();
}
}, Resource.USER, true);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.users().get("nosuch").remove();
+ realm.users().get(user.getId()).resetPassword(CredentialBuilder.create().password("password").build());
}
}, Resource.USER, true);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.users().get("nosuch").resetPassword(CredentialBuilder.create().password("password").build());
+ realm.users().get(user.getId()).removeTotp();
}
}, Resource.USER, true);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.users().get("nosuch").removeTotp();
+ realm.users().get(user.getId()).resetPasswordEmail();
}
}, Resource.USER, true);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.users().get("nosuch").resetPasswordEmail();
+ realm.users().get(user.getId()).executeActionsEmail(Collections.<String>emptyList());
}
}, Resource.USER, true);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.users().get("nosuch").executeActionsEmail(Collections.<String>emptyList());
+ realm.users().get(user.getId()).sendVerifyEmail();
}
}, Resource.USER, true);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.users().get("nosuch").sendVerifyEmail();
- }
- }, Resource.USER, true);
- invoke(new Invocation() {
- public void invoke(RealmResource realm) {
- realm.users().get("nosuch").groups();
+ realm.users().get(user.getId()).groups();
}
}, Resource.USER, false);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.users().get("nosuch").leaveGroup("nosuch");
+ realm.users().get(user.getId()).leaveGroup("nosuch");
}
}, Resource.USER, true);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.users().get("nosuch").joinGroup("nosuch");
+ realm.users().get(user.getId()).joinGroup("nosuch");
}
}, Resource.USER, true);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.users().get("nosuch").roles().getAll();
+ realm.users().get(user.getId()).roles().getAll();
}
}, Resource.USER, false);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.users().get("nosuch").roles().realmLevel().listAll();
+ realm.users().get(user.getId()).roles().realmLevel().listAll();
}
}, Resource.USER, false);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.users().get("nosuch").roles().realmLevel().listAvailable();
+ realm.users().get(user.getId()).roles().realmLevel().listAvailable();
}
}, Resource.USER, false);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.users().get("nosuch").roles().realmLevel().listEffective();
+ realm.users().get(user.getId()).roles().realmLevel().listEffective();
}
}, Resource.USER, false);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.users().get("nosuch").roles().realmLevel().add(Collections.<RoleRepresentation>emptyList());
+ realm.users().get(user.getId()).roles().realmLevel().add(Collections.<RoleRepresentation>emptyList());
}
}, Resource.USER, true);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.users().get("nosuch").roles().realmLevel().remove(Collections.<RoleRepresentation>emptyList());
+ realm.users().get(user.getId()).roles().realmLevel().remove(Collections.<RoleRepresentation>emptyList());
}
}, Resource.USER, true);
+ ClientRepresentation realmAccessClient = adminClient.realms().realm(REALM_NAME).clients().findByClientId(Constants.REALM_MANAGEMENT_CLIENT_ID).get(0);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.users().get("nosuch").roles().clientLevel("nosuch").listAll();
+ realm.users().get(user.getId()).roles().clientLevel(realmAccessClient.getId()).listAll();
}
}, Resource.USER, false);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.users().get("nosuch").roles().clientLevel("nosuch").listAvailable();
+ realm.users().get(user.getId()).roles().clientLevel(realmAccessClient.getId()).listAvailable();
}
}, Resource.USER, false);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.users().get("nosuch").roles().clientLevel("nosuch").listEffective();
+ realm.users().get(user.getId()).roles().clientLevel(realmAccessClient.getId()).listEffective();
}
}, Resource.USER, false);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.users().get("nosuch").roles().clientLevel("nosuch").add(Collections.<RoleRepresentation>emptyList());
+ realm.users().get(user.getId()).roles().clientLevel(realmAccessClient.getId()).add(Collections.<RoleRepresentation>emptyList());
}
}, Resource.USER, true);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
- realm.users().get("nosuch").roles().clientLevel("nosuch").remove(Collections.<RoleRepresentation>emptyList());
+ realm.users().get(user.getId()).roles().clientLevel(realmAccessClient.getId()).remove(Collections.<RoleRepresentation>emptyList());
}
}, Resource.USER, true);
+ invoke(new Invocation() {
+ public void invoke(RealmResource realm) {
+ realm.users().search("foo", 0, 1);
+ }
+ }, Resource.USER, false);
}
@Test
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/PolicyEvaluationCompositeRoleTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/PolicyEvaluationCompositeRoleTest.java
index 80de8b0..e5f84e2 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/PolicyEvaluationCompositeRoleTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/PolicyEvaluationCompositeRoleTest.java
@@ -22,6 +22,7 @@ import org.junit.Assert;
import org.junit.Test;
import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.authorization.AuthorizationProvider;
+import org.keycloak.authorization.AuthorizationProviderFactory;
import org.keycloak.authorization.model.Policy;
import org.keycloak.authorization.model.Resource;
import org.keycloak.authorization.model.ResourceServer;
@@ -77,7 +78,8 @@ public class PolicyEvaluationCompositeRoleTest extends AbstractKeycloakTest {
RoleModel role1 = client.addRole("client-role1");
- AuthorizationProvider authz = session.getProvider(AuthorizationProvider.class);
+ AuthorizationProviderFactory factory = (AuthorizationProviderFactory)session.getKeycloakSessionFactory().getProviderFactory(AuthorizationProvider.class);
+ AuthorizationProvider authz = factory.create(session, realm);
ResourceServer resourceServer = authz.getStoreFactory().getResourceServerStore().create(client.getId());
Policy policy = createRolePolicy(authz, resourceServer, role1);
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/log4j.properties b/testsuite/integration-arquillian/tests/base/src/test/resources/log4j.properties
index 1c42519..167c611 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/resources/log4j.properties
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/log4j.properties
@@ -70,4 +70,4 @@ log4j.logger.org.apache.directory.server.core=warn
# log4j.logger.org.keycloak.keys.infinispan=trace
log4j.logger.org.keycloak.services.clientregistration.policy=debug
-#log4j.logger.org.keycloak.authentication=debug
+#log4j.logger.org.keycloak.authentication=debug
\ No newline at end of file
diff --git a/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties b/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties
index 1a0de6e..28a985c 100644
--- a/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties
+++ b/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties
@@ -1312,9 +1312,35 @@ credential-reset-actions-timeout=Expires In
credential-reset-actions-timeout.tooltip=Maximum time before the action permit expires.
ldap-mappers=LDAP Mappers
create-ldap-mapper=Create LDAP mapper
-
-
-
-
+map-role-mgmt-scope-description=Policies that decide if an admin can map this role to a user or group
+manage-authz-users-scope-description=Policies that decide if an admin can manage all users in the realm
+view-authz-users-scope-description=Policies that decide if an admin can view all users in realm
+permissions-enabled-role=Permissions Enabled
+permissions-enabled-role.tooltip=Whether or not to enable fine grain permissions for managing this role. Disabling will delete all current permissions that have been set up.
+manage-permissions-role.tooltip=Fine grain permissions for managing roles. For example, you can define different policies for who is allowed to map a role.
+lookup=Lookup
+manage-permissions-users.tooltip=Fine grain permssions for managing all users in realm. You can define different policies for who is allowed to manage users in the realm.
+permissions-enabled-users=Permissions Enabled
+permissions-enabled-users.tooltip=Whether or not to enable fine grain permissions for managing users. Disabling will delete all current permissions that have been set up.
+manage-permissions-client.tooltip=Fine grain permssions for admins that want to manage this client or apply roles defined by this client.
+manage-permissions-group.tooltip=Fine grain permssions for admins that want to manage this group or the members of this group.
+manage-authz-group-scope-description=Policies that decide if an admin can manage this group
+view-authz-group-scope-description=Policies that decide if an admin can view this group
+view-members-authz-group-scope-description=Policies that decide if an admin can manage the members of this group
+manage-authz-client-scope-description=Policies that decide if an admin can manage this client
+configure-authz-client-scope-description=Reduced management permissions for admin. Cannot set scope, template, or protocol mappers.
+view-authz-client-scope-description=Policies that decide if an admin can view this client
+map-roles-authz-client-scope-description=Policies that decide if an admin can map roles defined by this client
+map-roles-client-scope-authz-client-scope-description=Policies that decide if an admin can apply roles defined by this client to the client scope of another client
+map-roles-composite-authz-client-scope-description=Policies that decide if an admin can apply roles defined by this client as a composite to another role
+map-role-authz-role-scope-description=Policies that decide if an admin can map role this role to a user or group
+map-role-client-scope-authz-role-scope-description=Policies that decide if an admin can apply this role to the client scope of a client
+map-role-composite-authz-role-scope-description=Policies that decide if an admin can apply this role as a composite to another role
+manage-group-membership-authz-users-scope-description=Policies that decide if an admin can manage group membership for all users in the realm. This is used in conjunction with specific group policy
+impersonate-authz-users-scope-description=Policies that decide if admin can impersonate other users
+map-roles-authz-users-scope-description=Policies that decide if admin can map roles for all users
+user-impersonated-authz-users-scope-description=Policies that decide which users can be impersonated. These policies are applied to the user being impersonated.
+manage-membership-authz-group-scope-description=Policies that decide if admin can add or remove users from this group
+manage-members-authz-group-scope-description=Policies that decide if an admin can manage the members of this group
diff --git a/themes/src/main/resources/theme/base/admin/resources/js/app.js b/themes/src/main/resources/theme/base/admin/resources/js/app.js
index 4451535..c4e870f 100755
--- a/themes/src/main/resources/theme/base/admin/resources/js/app.js
+++ b/themes/src/main/resources/theme/base/admin/resources/js/app.js
@@ -1398,9 +1398,6 @@ module.config([ '$routeProvider', function($routeProvider) {
realm : function(RealmLoader) {
return RealmLoader();
},
- clients : function(ClientListLoader) {
- return ClientListLoader();
- },
serverInfo : function(ServerInfoLoader) {
return ServerInfoLoader();
}
@@ -2358,6 +2355,24 @@ module.directive('kcTabsAuthentication', function () {
}
});
+module.directive('kcTabsRole', function () {
+ return {
+ scope: true,
+ restrict: 'E',
+ replace: true,
+ templateUrl: resourceUrl + '/templates/kc-tabs-role.html'
+ }
+});
+
+module.directive('kcTabsClientRole', function () {
+ return {
+ scope: true,
+ restrict: 'E',
+ replace: true,
+ templateUrl: resourceUrl + '/templates/kc-tabs-client-role.html'
+ }
+});
+
module.directive('kcTabsUser', function () {
return {
scope: true,
@@ -2367,6 +2382,15 @@ module.directive('kcTabsUser', function () {
}
});
+module.directive('kcTabsUsers', function () {
+ return {
+ scope: true,
+ restrict: 'E',
+ replace: true,
+ templateUrl: resourceUrl + '/templates/kc-tabs-users.html'
+ }
+});
+
module.directive('kcTabsGroup', function () {
return {
scope: true,
diff --git a/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-app.js b/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-app.js
index 2b92bc5..a59ebf3 100644
--- a/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-app.js
+++ b/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-app.js
@@ -412,7 +412,65 @@ module.config(['$routeProvider', function ($routeProvider) {
}
},
controller: 'ResourceServerPolicyAggregateDetailCtrl'
- });
+ }).when('/realms/:realm/roles/:role/permissions', {
+ templateUrl : resourceUrl + '/partials/authz/mgmt/realm-role-permissions.html',
+ resolve : {
+ realm : function(RealmLoader) {
+ return RealmLoader();
+ },
+ role : function(RoleLoader) {
+ return RoleLoader();
+ }
+ },
+ controller : 'RealmRolePermissionsCtrl'
+ }).when('/realms/:realm/clients/:client/roles/:role/permissions', {
+ templateUrl : resourceUrl + '/partials/authz/mgmt/client-role-permissions.html',
+ resolve : {
+ realm : function(RealmLoader) {
+ return RealmLoader();
+ },
+ client : function(ClientLoader) {
+ return ClientLoader();
+ },
+ role : function(RoleLoader) {
+ return RoleLoader();
+ }
+ },
+ controller : 'ClientRolePermissionsCtrl'
+ }).when('/realms/:realm/users-permissions', {
+ templateUrl : resourceUrl + '/partials/authz/mgmt/users-permissions.html',
+ resolve : {
+ realm : function(RealmLoader) {
+ return RealmLoader();
+ }
+ },
+ controller : 'UsersPermissionsCtrl'
+ })
+ .when('/realms/:realm/clients/:client/permissions', {
+ templateUrl : resourceUrl + '/partials/authz/mgmt/client-permissions.html',
+ resolve : {
+ realm : function(RealmLoader) {
+ return RealmLoader();
+ },
+ client : function(ClientLoader) {
+ return ClientLoader();
+ }
+ },
+ controller : 'ClientPermissionsCtrl'
+ })
+ .when('/realms/:realm/groups/:group/permissions', {
+ templateUrl : resourceUrl + '/partials/authz/mgmt/group-permissions.html',
+ resolve : {
+ realm : function(RealmLoader) {
+ return RealmLoader();
+ },
+ group : function(GroupLoader) {
+ return GroupLoader();
+ }
+ },
+ controller : 'GroupPermissionsCtrl'
+ })
+ ;
}]);
module.directive('kcTabsResourceServer', function () {
diff --git a/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-controller.js b/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-controller.js
index 89d0a48..034d595 100644
--- a/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-controller.js
+++ b/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-controller.js
@@ -79,72 +79,7 @@ module.controller('ResourceServerDetailCtrl', function($scope, $http, $route, $l
});
});
-var Resources = {
- delete: function(ResourceServerResource, realm, client, $scope, AuthzDialog, $location, Notifications, $route) {
- ResourceServerResource.permissions({
- realm : realm,
- client : client.id,
- rsrid : $scope.resource._id
- }, function (permissions) {
- var msg = "";
-
- if (permissions.length > 0 && !$scope.deleteConsent) {
- msg = "<p>This resource is referenced in some permissions:</p>";
- msg += "<ul>";
- for (i = 0; i < permissions.length; i++) {
- msg+= "<li><strong>" + permissions[i].name + "</strong></li>";
- }
- msg += "</ul>";
- msg += "<p>If you remove this resource, the permissions above will be affected and will not be associated with this resource anymore.</p>";
- }
-
- AuthzDialog.confirmDeleteWithMsg($scope.resource.name, "Resource", msg, function() {
- ResourceServerResource.delete({realm : realm, client : $scope.client.id, rsrid : $scope.resource._id}, null, function() {
- $location.url("/realms/" + realm + "/clients/" + $scope.client.id + "/authz/resource-server/resource");
- $route.reload();
- Notifications.success("The resource has been deleted.");
- });
- });
- });
- }
-}
-
-var Policies = {
- delete: function(service, realm, client, $scope, AuthzDialog, $location, Notifications, $route, isPermission) {
- var msg = "";
-
- service.dependentPolicies({
- realm : realm,
- client : client.id,
- id : $scope.policy.id
- }, function (dependentPolicies) {
- if (dependentPolicies.length > 0 && !$scope.deleteConsent) {
- msg = "<p>This policy is being used by other policies:</p>";
- msg += "<ul>";
- for (i = 0; i < dependentPolicies.length; i++) {
- msg+= "<li><strong>" + dependentPolicies[i].name + "</strong></li>";
- }
- msg += "</ul>";
- msg += "<p>If you remove this policy, the policies above will be affected and will not be associated with this policy anymore.</p>";
- }
-
- AuthzDialog.confirmDeleteWithMsg($scope.policy.name, isPermission ? "Permission" : "Policy", msg, function() {
- service.delete({realm : realm, client : $scope.client.id, id : $scope.policy.id}, null, function() {
- if (isPermission) {
- $location.url("/realms/" + realm + "/clients/" + $scope.client.id + "/authz/resource-server/permission");
- Notifications.success("The permission has been deleted.");
- } else {
- $location.url("/realms/" + realm + "/clients/" + $scope.client.id + "/authz/resource-server/policy");
- Notifications.success("The policy has been deleted.");
- }
- $route.reload();
- });
- });
- });
- }
-}
-
-module.controller('ResourceServerResourceCtrl', function($scope, $http, $route, $location, realm, ResourceServer, ResourceServerResource, client, AuthzDialog, Notifications) {
+module.controller('ResourceServerResourceCtrl', function($scope, $http, $route, $location, realm, ResourceServer, ResourceServerResource, client) {
$scope.realm = realm;
$scope.client = client;
@@ -236,11 +171,6 @@ module.controller('ResourceServerResourceCtrl', function($scope, $http, $route,
}
}
};
-
- $scope.delete = function(resource) {
- $scope.resource = resource;
- Resources.delete(ResourceServerResource, $route.current.params.realm, client, $scope, AuthzDialog, $location, Notifications, $route);
- };
});
module.controller('ResourceServerResourceDetailCtrl', function($scope, $http, $route, $location, realm, ResourceServer, client, ResourceServerResource, ResourceServerScope, AuthzDialog, Notifications) {
@@ -352,7 +282,30 @@ module.controller('ResourceServerResourceDetailCtrl', function($scope, $http, $r
}
$scope.remove = function() {
- Resources.delete(ResourceServerResource, $route.current.params.realm, client, $scope, AuthzDialog, $location, Notifications, $route);
+ ResourceServerResource.permissions({
+ realm : $route.current.params.realm,
+ client : client.id,
+ rsrid : $scope.resource._id
+ }, function (permissions) {
+ var msg = "";
+
+ if (permissions.length > 0 && !$scope.deleteConsent) {
+ msg = "<p>This resource is referenced in some policies:</p>";
+ msg += "<ul>";
+ for (i = 0; i < permissions.length; i++) {
+ msg+= "<li><strong>" + permissions[i].name + "</strong></li>";
+ }
+ msg += "</ul>";
+ msg += "<p>If you remove this resource, the policies above will be affected and will not be associated with this resource anymore.</p>";
+ }
+
+ AuthzDialog.confirmDeleteWithMsg($scope.resource.name, "Resource", msg, function() {
+ ResourceServerResource.delete({realm : realm.realm, client : $scope.client.id, rsrid : $scope.resource._id}, null, function() {
+ $location.url("/realms/" + realm.realm + "/clients/" + $scope.client.id + "/authz/resource-server/resource");
+ Notifications.success("The resource has been deleted.");
+ });
+ });
+ });
}
$scope.reset = function() {
@@ -385,37 +338,7 @@ module.controller('ResourceServerResourceDetailCtrl', function($scope, $http, $r
}
});
-var Scopes = {
- delete: function(ResourceServerScope, realm, client, $scope, AuthzDialog, $location, Notifications, $route) {
- ResourceServerScope.permissions({
- realm : realm,
- client : client.id,
- id : $scope.scope.id
- }, function (permissions) {
- var msg = "";
-
- if (permissions.length > 0 && !$scope.deleteConsent) {
- msg = "<p>This scope is referenced in some permissions:</p>";
- msg += "<ul>";
- for (i = 0; i < permissions.length; i++) {
- msg+= "<li><strong>" + permissions[i].name + "</strong></li>";
- }
- msg += "</ul>";
- msg += "<p>If you remove this scope, the permissions above will be affected and will not be associated with this scope anymore.</p>";
- }
-
- AuthzDialog.confirmDeleteWithMsg($scope.scope.name, "Scope", msg, function() {
- ResourceServerScope.delete({realm : realm, client : $scope.client.id, id : $scope.scope.id}, null, function() {
- $location.url("/realms/" + realm + "/clients/" + $scope.client.id + "/authz/resource-server/scope");
- $route.reload();
- Notifications.success("The scope has been deleted.");
- });
- });
- });
- }
-}
-
-module.controller('ResourceServerScopeCtrl', function($scope, $http, $route, $location, realm, ResourceServer, ResourceServerScope,client, AuthzDialog, Notifications) {
+module.controller('ResourceServerScopeCtrl', function($scope, $http, $route, $location, realm, ResourceServer, ResourceServerScope, client) {
$scope.realm = realm;
$scope.client = client;
@@ -507,11 +430,6 @@ module.controller('ResourceServerScopeCtrl', function($scope, $http, $route, $lo
}
}
};
-
- $scope.delete = function(scope) {
- $scope.scope = scope;
- Scopes.delete(ResourceServerScope, $route.current.params.realm, client, $scope, AuthzDialog, $location, Notifications, $route);
- };
});
module.controller('ResourceServerScopeDetailCtrl', function($scope, $http, $route, $location, realm, ResourceServer, client, ResourceServerScope, AuthzDialog, Notifications) {
@@ -581,7 +499,30 @@ module.controller('ResourceServerScopeDetailCtrl', function($scope, $http, $rout
}
$scope.remove = function() {
- Scopes.delete(ResourceServerScope, $route.current.params.realm, client, $scope, AuthzDialog, $location, Notifications, $route);
+ ResourceServerScope.permissions({
+ realm : $route.current.params.realm,
+ client : client.id,
+ id : $scope.scope.id
+ }, function (permissions) {
+ var msg = "";
+
+ if (permissions.length > 0 && !$scope.deleteConsent) {
+ msg = "<p>This scope is referenced in some policies:</p>";
+ msg += "<ul>";
+ for (i = 0; i < permissions.length; i++) {
+ msg+= "<li><strong>" + permissions[i].name + "</strong></li>";
+ }
+ msg += "</ul>";
+ msg += "<p>If you remove this scope, the policies above will be affected and will not be associated with this scope anymore.</p>";
+ }
+
+ AuthzDialog.confirmDeleteWithMsg($scope.scope.name, "Scope", msg, function() {
+ ResourceServerScope.delete({realm : realm.realm, client : $scope.client.id, id : $scope.scope.id}, null, function() {
+ $location.url("/realms/" + realm.realm + "/clients/" + client.id + "/authz/resource-server/scope");
+ Notifications.success("The scope has been deleted.");
+ });
+ });
+ });
}
$scope.reset = function() {
@@ -613,7 +554,7 @@ module.controller('ResourceServerScopeDetailCtrl', function($scope, $http, $rout
}
});
-module.controller('ResourceServerPolicyCtrl', function($scope, $http, $route, $location, realm, ResourceServer, ResourceServerPolicy, PolicyProvider, client, AuthzDialog, Notifications) {
+module.controller('ResourceServerPolicyCtrl', function($scope, $http, $route, $location, realm, ResourceServer, ResourceServerPolicy, PolicyProvider, client) {
$scope.realm = realm;
$scope.client = client;
$scope.policyProviders = [];
@@ -709,14 +650,9 @@ module.controller('ResourceServerPolicyCtrl', function($scope, $http, $route, $l
}
}
};
-
- $scope.delete = function(policy) {
- $scope.policy = policy;
- Policies.delete(ResourceServerPolicy, $route.current.params.realm, client, $scope, AuthzDialog, $location, Notifications, $route, false);
- };
});
-module.controller('ResourceServerPermissionCtrl', function($scope, $http, $route, $location, realm, ResourceServer, ResourceServerPermission, PolicyProvider, client, AuthzDialog, Notifications) {
+module.controller('ResourceServerPermissionCtrl', function($scope, $http, $route, $location, realm, ResourceServer, ResourceServerPermission, PolicyProvider, client) {
$scope.realm = realm;
$scope.client = client;
$scope.policyProviders = [];
@@ -811,11 +747,6 @@ module.controller('ResourceServerPermissionCtrl', function($scope, $http, $route
}
}
};
-
- $scope.delete = function(policy) {
- $scope.policy = policy;
- Policies.delete(ResourceServerPermission, $route.current.params.realm, client, $scope, AuthzDialog, $location, Notifications, $route, true);
- };
});
module.controller('ResourceServerPolicyDroolsDetailCtrl', function($scope, $http, $route, realm, client, PolicyController) {
@@ -1260,19 +1191,17 @@ module.controller('ResourceServerPolicyScopeDetailCtrl', function($scope, $route
client : client.id,
id : policy.id
}, function(policies) {
- if (policies.length > 0) {
- $scope.selectedPolicies = [];
- for (i = 0; i < policies.length; i++) {
- policies[i].text = policies[i].name;
- $scope.selectedPolicies.push(policies[i]);
- }
- var copy = angular.copy($scope.selectedPolicies);
- $scope.$watch('selectedPolicies', function() {
- if (!angular.equals($scope.selectedPolicies, copy)) {
- $scope.changed = true;
- }
- }, true);
+ $scope.selectedPolicies = [];
+ for (i = 0; i < policies.length; i++) {
+ policies[i].text = policies[i].name;
+ $scope.selectedPolicies.push(policies[i]);
}
+ var copy = angular.copy($scope.selectedPolicies);
+ $scope.$watch('selectedPolicies', function() {
+ if (!angular.equals($scope.selectedPolicies, copy)) {
+ $scope.changed = true;
+ }
+ }, true);
});
},
@@ -2169,7 +2098,35 @@ module.service("PolicyController", function($http, $route, $location, ResourceSe
});
$scope.remove = function() {
- Policies.delete(ResourceServerPolicy, $route.current.params.realm, client, $scope, AuthzDialog, $location, Notifications, $route, delegate.isPermission());
+ var msg = "";
+
+ service.dependentPolicies({
+ realm : $route.current.params.realm,
+ client : client.id,
+ id : $scope.policy.id
+ }, function (dependentPolicies) {
+ if (dependentPolicies.length > 0 && !$scope.deleteConsent) {
+ msg = "<p>This policy is being used by other policies:</p>";
+ msg += "<ul>";
+ for (i = 0; i < dependentPolicies.length; i++) {
+ msg+= "<li><strong>" + dependentPolicies[i].name + "</strong></li>";
+ }
+ msg += "</ul>";
+ msg += "<p>If you remove this policy, the policies above will be affected and will not be associated with this policy anymore.</p>";
+ }
+
+ AuthzDialog.confirmDeleteWithMsg($scope.policy.name, "Policy", msg, function() {
+ service.delete({realm : $scope.realm.realm, client : $scope.client.id, id : $scope.policy.id}, null, function() {
+ if (delegate.isPermission()) {
+ $location.url("/realms/" + realm.realm + "/clients/" + client.id + "/authz/resource-server/permission");
+ Notifications.success("The permission has been deleted.");
+ } else {
+ $location.url("/realms/" + realm.realm + "/clients/" + client.id + "/authz/resource-server/policy");
+ Notifications.success("The policy has been deleted.");
+ }
+ });
+ });
+ });
}
}
});
@@ -2204,21 +2161,13 @@ module.controller('PolicyEvaluateCtrl', function($scope, $http, $route, $locatio
$scope.client = client;
$scope.clients = clients;
$scope.roles = roles;
- authzRequest = {};
- authzRequest.resources = [];
- authzRequest.context = {};
- authzRequest.context.attributes = {};
- authzRequest.roleIds = [];
+ $scope.authzRequest = {};
+ $scope.authzRequest.resources = [];
+ $scope.authzRequest.context = {};
+ $scope.authzRequest.context.attributes = {};
+ $scope.authzRequest.roleIds = [];
$scope.resultUrl = resourceUrl + '/partials/authz/policy/resource-server-policy-evaluate-result.html';
- $scope.authzRequest = angular.copy(authzRequest);
-
- $scope.$watch('authzRequest', function() {
- if (!angular.equals($scope.authzRequest, authzRequest)) {
- $scope.changed = true;
- }
- }, true);
-
$scope.addContextAttribute = function() {
if (!$scope.newContextAttribute.value || $scope.newContextAttribute.value == '') {
Notifications.error("You must provide a value to a context attribute.");
@@ -2531,4 +2480,103 @@ module.controller('PolicyEvaluateCtrl', function($scope, $http, $route, $locatio
$scope.authzRequest = angular.copy(authzRequest);
$scope.changed = false;
}
-});
\ No newline at end of file
+});
+
+getManageClientId = function(realm) {
+ if (realm.realm == masterRealm) {
+ return 'master-realm';
+ } else {
+ return 'realm-management';
+ }
+}
+
+module.controller('RealmRolePermissionsCtrl', function($scope, $http, $route, $location, realm, role, RoleManagementPermissions, Client, Notifications) {
+ console.log('RealmRolePermissionsCtrl');
+ $scope.role = role;
+ $scope.realm = realm;
+ RoleManagementPermissions.get({realm: realm.realm, role: role.id}, function(data) {
+ $scope.permissions = data;
+ });
+ Client.query({realm: realm.realm, clientId: getManageClientId(realm)}, function(data) {
+ $scope.realmManagementClientId = data[0].id;
+ });
+ $scope.setEnabled = function() {
+ var param = { enabled: $scope.permissions.enabled};
+ $scope.permissions= RoleManagementPermissions.update({realm: realm.realm, role:role.id}, param);
+ };
+
+
+});
+module.controller('ClientRolePermissionsCtrl', function($scope, $http, $route, $location, realm, client, role, Client, RoleManagementPermissions, Client, Notifications) {
+ console.log('RealmRolePermissionsCtrl');
+ $scope.client = client;
+ $scope.role = role;
+ $scope.realm = realm;
+ RoleManagementPermissions.get({realm: realm.realm, role: role.id}, function(data) {
+ $scope.permissions = data;
+ });
+ Client.query({realm: realm.realm, clientId: getManageClientId(realm)}, function(data) {
+ $scope.realmManagementClientId = data[0].id;
+ });
+ $scope.setEnabled = function() {
+ console.log('perssions enabled: ' + $scope.permissions.enabled);
+ var param = { enabled: $scope.permissions.enabled};
+ $scope.permissions = RoleManagementPermissions.update({realm: realm.realm, role:role.id}, param);
+ };
+
+
+});
+
+module.controller('UsersPermissionsCtrl', function($scope, $http, $route, $location, realm, UsersManagementPermissions, Client, Notifications) {
+ console.log('UsersPermissionsCtrl');
+ $scope.realm = realm;
+ UsersManagementPermissions.get({realm: realm.realm}, function(data) {
+ $scope.permissions = data;
+ });
+ Client.query({realm: realm.realm, clientId: getManageClientId(realm)}, function(data) {
+ $scope.realmManagementClientId = data[0].id;
+ });
+ $scope.changeIt = function() {
+ console.log('before permissions.enabled=' + $scope.permissions.enabled);
+ var param = { enabled: $scope.permissions.enabled};
+ $scope.permissions = UsersManagementPermissions.update({realm: realm.realm}, param);
+ };
+
+
+});
+
+module.controller('ClientPermissionsCtrl', function($scope, $http, $route, $location, realm, client, Client, ClientManagementPermissions, Notifications) {
+ $scope.client = client;
+ $scope.realm = realm;
+ ClientManagementPermissions.get({realm: realm.realm, client: client.id}, function(data) {
+ $scope.permissions = data;
+ });
+ Client.query({realm: realm.realm, clientId: getManageClientId(realm)}, function(data) {
+ $scope.realmManagementClientId = data[0].id;
+ });
+ $scope.setEnabled = function() {
+ var param = { enabled: $scope.permissions.enabled};
+ $scope.permissions = ClientManagementPermissions.update({realm: realm.realm, client: client.id}, param);
+ };
+
+
+});
+
+module.controller('GroupPermissionsCtrl', function($scope, $http, $route, $location, realm, group, GroupManagementPermissions, Client, Notifications) {
+ $scope.group = group;
+ $scope.realm = realm;
+ Client.query({realm: realm.realm, clientId: getManageClientId(realm)}, function(data) {
+ $scope.realmManagementClientId = data[0].id;
+ });
+ GroupManagementPermissions.get({realm: realm.realm, group: group.id}, function(data) {
+ $scope.permissions = data;
+ });
+ $scope.setEnabled = function() {
+ var param = { enabled: $scope.permissions.enabled};
+ $scope.permissions = GroupManagementPermissions.update({realm: realm.realm, group: group.id}, param);
+ };
+
+
+});
+
+
diff --git a/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-services.js b/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-services.js
index 92219bb..f56ca8f 100644
--- a/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-services.js
+++ b/themes/src/main/resources/theme/base/admin/resources/js/authz/authz-services.js
@@ -144,4 +144,50 @@ module.service('AuthzDialog', function($modal) {
}
return dialog;
-});
\ No newline at end of file
+});
+
+module.factory('RoleManagementPermissions', function($resource) {
+ return $resource(authUrl + '/admin/realms/:realm/roles-by-id/:role/management/permissions', {
+ realm : '@realm',
+ role : '@role'
+ }, {
+ update: {
+ method: 'PUT'
+ }
+ });
+});
+
+module.factory('UsersManagementPermissions', function($resource) {
+ return $resource(authUrl + '/admin/realms/:realm/users-management-permissions', {
+ realm : '@realm'
+ }, {
+ update: {
+ method: 'PUT'
+ }
+ });
+});
+
+module.factory('ClientManagementPermissions', function($resource) {
+ return $resource(authUrl + '/admin/realms/:realm/clients/:client/management/permissions', {
+ realm : '@realm',
+ client : '@client'
+ }, {
+ update: {
+ method: 'PUT'
+ }
+ });
+});
+
+module.factory('GroupManagementPermissions', function($resource) {
+ return $resource(authUrl + '/admin/realms/:realm/groups/:group/management/permissions', {
+ realm : '@realm',
+ group : '@group'
+ }, {
+ update: {
+ method: 'PUT'
+ }
+ });
+});
+
+
+
diff --git a/themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js b/themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js
index 315b8a5..515eb99 100755
--- a/themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js
+++ b/themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js
@@ -728,9 +728,9 @@ module.controller('ClientImportCtrl', function($scope, $location, $upload, realm
});
-module.controller('ClientListCtrl', function($scope, realm, clients, Client, serverInfo, $route, Dialog, Notifications, filterFilter) {
+module.controller('ClientListCtrl', function($scope, realm, Client, serverInfo, $route, Dialog, Notifications, filterFilter) {
$scope.realm = realm;
- $scope.clients = clients;
+ $scope.clients = Client.query({realm: realm.realm, viewableOnly: true});
$scope.currentPage = 1;
$scope.currentPageInput = 1;
$scope.pageSize = 20;
@@ -1387,6 +1387,7 @@ module.controller('ClientScopeMappingCtrl', function($scope, $http, realm, clien
}
$scope.changeFlag = function() {
+ console.log('changeFlag');
Client.update({
realm : realm.realm,
client : client.id
diff --git a/themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js b/themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js
index 23f4ad8..ab4e72a 100644
--- a/themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js
+++ b/themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js
@@ -1,81 +1,95 @@
-module.controller('GlobalCtrl', function($scope, $http, Auth, Current, $location, Notifications, ServerInfo) {
- $scope.authUrl = authUrl;
- $scope.resourceUrl = resourceUrl;
- $scope.auth = Auth;
- $scope.serverInfo = ServerInfo.get();
-
- function getAccess(role) {
- if (!Current.realm) {
- return false;
- }
-
- var realmAccess = Auth.user && Auth.user['realm_access'];
+function getAccess(Auth, Current, role) {
+ if (!Current.realm)return false;
+ var realmAccess = Auth.user && Auth.user['realm_access'];
+ if (realmAccess) {
+ realmAccess = realmAccess[Current.realm.realm];
if (realmAccess) {
- realmAccess = realmAccess[Current.realm.realm];
- if (realmAccess) {
- return realmAccess.indexOf(role) >= 0;
- }
+ return realmAccess.indexOf(role) >= 0;
}
- return false;
}
+ return false;
+}
- $scope.access = {
+function getAccessObject(Auth, Current) {
+ return {
get createRealm() {
return Auth.user && Auth.user.createRealm;
},
+ get queryUsers() {
+ return getAccess(Auth, Current, 'query-users') || this.viewUsers;
+ },
+
+ get queryGroups() {
+ return getAccess(Auth, Current, 'query-groups') || this.viewUsers;
+ },
+
+ get queryClients() {
+ return getAccess(Auth, Current, 'query-clients') || this.viewClients;
+ },
+
get viewRealm() {
- return getAccess('view-realm') || getAccess('manage-realm') || this.manageRealm;
+ return getAccess(Auth, Current, 'view-realm') || getAccess(Auth, Current, 'manage-realm') || this.manageRealm;
},
get viewClients() {
- return getAccess('view-clients') || getAccess('manage-clients') || this.manageClients;
+ return getAccess(Auth, Current, 'view-clients') || getAccess(Auth, Current, 'manage-clients') || this.manageClients;
},
get viewUsers() {
- return getAccess('view-users') || getAccess('manage-users') || this.manageClients;
+ return getAccess(Auth, Current, 'view-users') || getAccess(Auth, Current, 'manage-users') || this.manageClients;
},
get viewEvents() {
- return getAccess('view-events') || getAccess('manage-events') || this.manageClients;
+ return getAccess(Auth, Current, 'view-events') || getAccess(Auth, Current, 'manage-events') || this.manageClients;
},
get viewIdentityProviders() {
- return getAccess('view-identity-providers') || getAccess('manage-identity-providers') || this.manageIdentityProviders;
+ return getAccess(Auth, Current, 'view-identity-providers') || getAccess(Auth, Current, 'manage-identity-providers') || this.manageIdentityProviders;
},
get viewAuthorization() {
- return getAccess('view-authorization') || this.manageAuthorization;
+ return getAccess(Auth, Current, 'view-authorization') || this.manageAuthorization;
},
get manageRealm() {
- return getAccess('manage-realm');
+ return getAccess(Auth, Current, 'manage-realm');
},
get manageClients() {
- return getAccess('manage-clients');
+ return getAccess(Auth, Current, 'manage-clients');
},
get manageUsers() {
- return getAccess('manage-users');
+ return getAccess(Auth, Current, 'manage-users');
},
get manageEvents() {
- return getAccess('manage-events');
+ return getAccess(Auth, Current, 'manage-events');
},
get manageIdentityProviders() {
- return getAccess('manage-identity-providers');
+ return getAccess(Auth, Current, 'manage-identity-providers');
},
get manageAuthorization() {
- return getAccess('manage-authorization');
+ return getAccess(Auth, Current, 'manage-authorization');
},
get impersonation() {
- return getAccess('impersonation');
+ return getAccess(Auth, Current, 'impersonation');
}
};
+}
+
+
+module.controller('GlobalCtrl', function($scope, $http, Auth, Current, $location, Notifications, ServerInfo) {
+ $scope.authUrl = authUrl;
+ $scope.resourceUrl = resourceUrl;
+ $scope.auth = Auth;
+ $scope.serverInfo = ServerInfo.get();
+
+ $scope.access = getAccessObject(Auth, Current);
$scope.$watch(function() {
return $location.path();
@@ -85,20 +99,36 @@ module.controller('GlobalCtrl', function($scope, $http, Auth, Current, $location
});
});
-module.controller('HomeCtrl', function(Realm, Auth, $location) {
+module.controller('HomeCtrl', function(Realm, Auth, Current, $location) {
+
Realm.query(null, function(realms) {
var realm;
if (realms.length == 1) {
- realm = realms[0].realm;
+ realm = realms[0];
} else if (realms.length == 2) {
if (realms[0].realm == Auth.user.realm) {
- realm = realms[1].realm;
+ realm = realms[1];
} else if (realms[1].realm == Auth.user.realm) {
- realm = realms[0].realm;
+ realm = realms[0];
}
}
if (realm) {
- $location.url('/realms/' + realm);
+ Current.realms = realms;
+ Current.realm = realm;
+ var access = getAccessObject(Auth, Current);
+ if (access.viewRealm || access.manageRealm) {
+ $location.url('/realms/' + realm.realm );
+ } else if (access.queryClients) {
+ $location.url('/realms/' + realm.realm + "/clients");
+ } else if (access.viewIdentityProviders) {
+ $location.url('/realms/' + realm.realm + "/identity-provider-settings");
+ } else if (access.queryUsers) {
+ $location.url('/realms/' + realm.realm + "/users");
+ } else if (access.queryGroups) {
+ $location.url('/realms/' + realm.realm + "/groups");
+ } else if (access.viewEvents) {
+ $location.url('/realms/' + realm.realm + "/events");
+ }
} else {
$location.url('/realms');
}
@@ -1322,6 +1352,21 @@ module.controller('RealmRevocationCtrl', function($scope, Realm, RealmPushRevoca
});
+module.controller('RoleTabCtrl', function(Dialog, $scope, Current, Notifications, $location) {
+ $scope.removeRole = function() {
+ Dialog.confirmDelete($scope.role.name, 'role', function() {
+ RoleById.remove({
+ realm: realm.realm,
+ role: $scope.role.id
+ }, function () {
+ $route.reload();
+ Notifications.success("The role has been deleted.");
+ });
+ });
+ };
+});
+
+
module.controller('RoleListCtrl', function($scope, $route, Dialog, Notifications, realm, roles, RoleById, filterFilter) {
$scope.realm = realm;
$scope.roles = roles;
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/authz/mgmt/client-permissions.html b/themes/src/main/resources/theme/base/admin/resources/partials/authz/mgmt/client-permissions.html
new file mode 100644
index 0000000..abc21a4
--- /dev/null
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/authz/mgmt/client-permissions.html
@@ -0,0 +1,39 @@
+<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
+ <ol class="breadcrumb">
+ <li><a href="#/realms/{{realm.realm}}/clients">{{:: 'clients' | translate}}</a></li>
+ <li>{{client.clientId}}</li>
+ </ol>
+
+ <kc-tabs-client></kc-tabs-client>
+
+ <form class=form-horizontal" name="enableForm" novalidate kc-read-only="!client.access.manage || !access.manageAuthorization">
+ <fieldset class="border-top">
+ <div class="form-group">
+ <label class="col-md-2 control-label" for="permissionsEnabled">{{:: 'permissions-enabled-role' | translate}}</label>
+ <div class="col-md-6">
+ <input ng-model="permissions.enabled" ng-click="setEnabled()" name="permissionsEnabled" id="permissionsEnabled" ng-disabled="!access.manageAuthorization" onoffswitch on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}"/>
+ </div>
+ <kc-tooltip>{{:: 'permissions-enabled-role.tooltip' | translate}}</kc-tooltip>
+ </div>
+ </fieldset>
+ </form>
+ <table class="datatable table table-striped table-bordered dataTable no-footer" data-ng-show="permissions.enabled">
+ <thead>
+ <tr>
+ <th>{{:: 'scope-name' | translate}}</th>
+ <th>{{:: 'description' | translate}}</th>
+ <th colspan="2">{{:: 'actions' | translate}}</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr ng-repeat="(scopeName, scopeId) in permissions.scopePermissions">
+ <td><a href="#/realms/{{realm.realm}}/clients/{{realmManagementClientId}}/authz/resource-server/permission/scope/{{scopeId}}">{{scopeName}}</a></td>
+ <td translate="{{scopeName}}-authz-client-scope-description"></td>
+ <td class="kc-action-cell" kc-open="/realms/{{realm.realm}}/clients/{{realmManagementClientId}}/authz/resource-server/permission/scope/{{scopeId}}">{{:: 'edit' | translate}}</td>
+ </tr>
+ </tbody>
+ </table>
+
+</div>
+
+<kc-menu></kc-menu>
\ No newline at end of file
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/authz/mgmt/client-role-permissions.html b/themes/src/main/resources/theme/base/admin/resources/partials/authz/mgmt/client-role-permissions.html
new file mode 100644
index 0000000..c5f37ea
--- /dev/null
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/authz/mgmt/client-role-permissions.html
@@ -0,0 +1,40 @@
+<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
+ <ol class="breadcrumb">
+ <li><a href="#/realms/{{realm.realm}}/clients">{{:: 'clients' | translate}}</a></li>
+ <li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}">{{client.clientId}}</a></li>
+ <li>{{role.name}}</li>
+ </ol>
+
+ <kc-tabs-client-role></kc-tabs-client-role>
+
+ <form class=form-horizontal" name="enableForm" novalidate kc-read-only="!client.access.manage || !access.manageAuthorization">
+ <fieldset class="border-top">
+ <div class="form-group">
+ <label class="col-md-2 control-label" for="permissionsEnabled">{{:: 'permissions-enabled-role' | translate}}</label>
+ <div class="col-md-6">
+ <input ng-model="permissions.enabled" ng-click="setEnabled()" name="permissionsEnabled" id="permissionsEnabled" ng-disabled="!access.manageAuthorization" onoffswitch on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}"/>
+ </div>
+ <kc-tooltip>{{:: 'permissions-enabled-role.tooltip' | translate}}</kc-tooltip>
+ </div>
+ </fieldset>
+ </form>
+ <table class="datatable table table-striped table-bordered dataTable no-footer" data-ng-show="permissions.enabled">
+ <thead>
+ <tr>
+ <th>{{:: 'scope-name' | translate}}</th>
+ <th>{{:: 'description' | translate}}</th>
+ <th colspan="2">{{:: 'actions' | translate}}</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr ng-repeat="(scopeName, scopeId) in permissions.scopePermissions">
+ <td><a href="#/realms/{{realm.realm}}/clients/{{realmManagementClientId}}/authz/resource-server/permission/scope/{{scopeId}}">{{scopeName}}</a></td>
+ <td translate="{{scopeName}}-authz-role-scope-description"></td>
+ <td class="kc-action-cell" kc-open="/realms/{{realm.realm}}/clients/{{realmManagementClientId}}/authz/resource-server/permission/scope/{{scopeId}}">{{:: 'edit' | translate}}</td>
+ </tr>
+ </tbody>
+ </table>
+
+</div>
+
+<kc-menu></kc-menu>
\ No newline at end of file
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/authz/mgmt/group-permissions.html b/themes/src/main/resources/theme/base/admin/resources/partials/authz/mgmt/group-permissions.html
new file mode 100644
index 0000000..897a0ed
--- /dev/null
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/authz/mgmt/group-permissions.html
@@ -0,0 +1,39 @@
+<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
+ <ol class="breadcrumb">
+ <li><a href="#/realms/{{realm.realm}}/groups">{{:: 'groups' | translate}}</a></li>
+ <li>{{group.name}}</li>
+ </ol>
+
+ <kc-tabs-group></kc-tabs-group>
+
+ <form class=form-horizontal" name="enableForm" novalidate kc-read-only="!group.access.manage || !access.manageAuthorization">
+ <fieldset class="border-top">
+ <div class="form-group">
+ <label class="col-md-2 control-label" for="permissionsEnabled">{{:: 'permissions-enabled-role' | translate}}</label>
+ <div class="col-md-6">
+ <input ng-model="permissions.enabled" ng-click="setEnabled()" name="permissionsEnabled" id="permissionsEnabled" ng-disabled="!access.manageAuthorization" onoffswitch on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}"/>
+ </div>
+ <kc-tooltip>{{:: 'permissions-enabled-role.tooltip' | translate}}</kc-tooltip>
+ </div>
+ </fieldset>
+ </form>
+ <table class="datatable table table-striped table-bordered dataTable no-footer" data-ng-show="permissions.enabled">
+ <thead>
+ <tr>
+ <th>{{:: 'scope-name' | translate}}</th>
+ <th>{{:: 'description' | translate}}</th>
+ <th colspan="2">{{:: 'actions' | translate}}</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr ng-repeat="(scopeName, scopeId) in permissions.scopePermissions">
+ <td><a href="#/realms/{{realm.realm}}/clients/{{realmManagementClientId}}/authz/resource-server/permission/scope/{{scopeId}}">{{scopeName}}</a></td>
+ <td translate="{{scopeName}}-authz-group-scope-description"></td>
+ <td class="kc-action-cell" kc-open="/realms/{{realm.realm}}/clients/{{realmManagementClientId}}/authz/resource-server/permission/scope/{{scopeId}}">{{:: 'edit' | translate}}</td>
+ </tr>
+ </tbody>
+ </table>
+
+</div>
+
+<kc-menu></kc-menu>
\ No newline at end of file
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/authz/mgmt/realm-role-permissions.html b/themes/src/main/resources/theme/base/admin/resources/partials/authz/mgmt/realm-role-permissions.html
new file mode 100644
index 0000000..9c03333
--- /dev/null
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/authz/mgmt/realm-role-permissions.html
@@ -0,0 +1,39 @@
+<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
+ <ol class="breadcrumb">
+ <li><a href="#/realms/{{realm.realm}}/roles">{{:: 'roles' | translate}}</a></li>
+ <li>{{role.name}}</li>
+ </ol>
+
+ <kc-tabs-role></kc-tabs-role>
+
+ <form class=form-horizontal" name="enableForm" novalidate kc-read-only="!access.manageAuthorization">
+ <fieldset class="border-top">
+ <div class="form-group">
+ <label class="col-md-2 control-label" for="permissionsEnabled">{{:: 'permissions-enabled-role' | translate}}</label>
+ <div class="col-md-6">
+ <input ng-model="permissions.enabled" ng-click="setEnabled()" name="permissionsEnabled" id="permissionsEnabled" ng-disabled="!access.manageAuthorization" onoffswitch on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}"/>
+ </div>
+ <kc-tooltip>{{:: 'permissions-enabled-role.tooltip' | translate}}</kc-tooltip>
+ </div>
+ </fieldset>
+ </form>
+ <table class="datatable table table-striped table-bordered dataTable no-footer" data-ng-show="permissions.enabled">
+ <thead>
+ <tr>
+ <th>{{:: 'scope-name' | translate}}</th>
+ <th>{{:: 'description' | translate}}</th>
+ <th colspan="2">{{:: 'actions' | translate}}</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr ng-repeat="(scopeName, scopeId) in permissions.scopePermissions">
+ <td><a href="#/realms/{{realm.realm}}/clients/{{realmManagementClientId}}/authz/resource-server/permission/scope/{{scopeId}}">{{scopeName}}</a></td>
+ <td translate="{{scopeName}}-authz-role-scope-description"></td>
+ <td class="kc-action-cell" kc-open="/realms/{{realm.realm}}/clients/{{realmManagementClientId}}/authz/resource-server/permission/scope/{{scopeId}}">{{:: 'edit' | translate}}</td>
+ </tr>
+ </tbody>
+ </table>
+
+</div>
+
+<kc-menu></kc-menu>
\ No newline at end of file
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/authz/mgmt/users-permissions.html b/themes/src/main/resources/theme/base/admin/resources/partials/authz/mgmt/users-permissions.html
new file mode 100644
index 0000000..4a5661f
--- /dev/null
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/authz/mgmt/users-permissions.html
@@ -0,0 +1,35 @@
+<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
+
+ <kc-tabs-users></kc-tabs-users>
+
+ <form class=form-horizontal" name="enableForm" novalidate kc-read-only="!access.manageAuthorization">
+ <fieldset class="border-top">
+ <div class="form-group">
+ <label class="col-md-2 control-label" for="permissionsEnabled">{{:: 'permissions-enabled-users' | translate}}</label>
+ <div class="col-md-6">
+ <input ng-model="permissions.enabled" ng-click="changeIt()" name="permissionsEnabled" id="permissionsEnabled" ng-disabled="!access.manageAuthorization" onoffswitch on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}"/>
+ </div>
+ <kc-tooltip>{{:: 'permissions-enabled-users.tooltip' | translate}}</kc-tooltip>
+ </div>
+ </fieldset>
+ </form>
+ <table class="datatable table table-striped table-bordered dataTable no-footer" data-ng-show="permissions.enabled">
+ <thead>
+ <tr>
+ <th>{{:: 'scope-name' | translate}}</th>
+ <th>{{:: 'description' | translate}}</th>
+ <th colspan="2">{{:: 'actions' | translate}}</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr ng-repeat="(scopeName, scopeId) in permissions.scopePermissions">
+ <td><a href="#/realms/{{realm.realm}}/clients/{{realmManagementClientId}}/authz/resource-server/permission/scope/{{scopeId}}">{{scopeName}}</a></td>
+ <td translate="{{scopeName}}-authz-users-scope-description"></td>
+ <td class="kc-action-cell" kc-open="/realms/{{realm.realm}}/clients/{{realmManagementClientId}}/authz/resource-server/permission/scope/{{scopeId}}">{{:: 'edit' | translate}}</td>
+ </tr>
+ </tbody>
+ </table>
+
+</div>
+
+<kc-menu></kc-menu>
\ No newline at end of file
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-clustering.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-clustering.html
index 4a6312b..72ff437 100644
--- a/themes/src/main/resources/theme/base/admin/resources/partials/client-clustering.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-clustering.html
@@ -7,7 +7,7 @@
<kc-tabs-client></kc-tabs-client>
- <form class="form-horizontal" name="clusteringForm" novalidate kc-read-only="!access.manageClients">
+ <form class="form-horizontal" name="clusteringForm" novalidate kc-read-only="!client.access.configure">
<legend><span class="text">{{:: 'basic-configuration' | translate}}</span></legend>
<fieldset >
<div class="form-group clearfix">
@@ -31,7 +31,7 @@
</div>
<div class="form-group">
- <div class="col-md-10 col-md-offset-2" data-ng-show="access.manageRealm">
+ <div class="col-md-10 col-md-offset-2" data-ng-show="client.access.configure">
<button data-kc-save data-ng-disabled="!changed">{{:: 'save' | translate}}</button>
<button data-kc-reset data-ng-disabled="!changed">{{:: 'cancel' | translate}}</button>
</div>
@@ -43,7 +43,7 @@
<table class="table table-striped table-bordered">
<thead>
<tr>
- <th class="kc-table-actions" colspan="5" data-ng-show="access.manageClients">
+ <th class="kc-table-actions" colspan="5" data-ng-show="client.access.configure">
<div class="pull-right">
<a class="btn btn-default" tooltip="Manually register cluster node. This is usually not needed as cluster node should be registered automatically by adapter"
tooltip-trigger="mouseover mouseout" tooltip-placement="bottom" href="#/register-node/realms/{{realm.realm}}/clients/{{client.id}}/clustering">{{:: 'register-node-manually' | translate}}</a>
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-clustering-node.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-clustering-node.html
index 440994f..7df3a42 100644
--- a/themes/src/main/resources/theme/base/admin/resources/partials/client-clustering-node.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-clustering-node.html
@@ -10,10 +10,10 @@
<h1 data-ng-show="create">{{:: 'add-node' | translate}}</h1>
<h1 data-ng-hide="create">
{{node.host|capitalize}}
- <i id="removeClient" class="pficon pficon-delete clickable" data-ng-show="access.manageClients" data-ng-click="unregisterNode()"></i>
+ <i id="removeClient" class="pficon pficon-delete clickable" data-ng-show="client.access.configure" data-ng-click="unregisterNode()"></i>
</h1>
- <form class="form-horizontal" name="clusteringForm" novalidate kc-read-only="!access.manageClients" data-ng-show="create || registered">
+ <form class="form-horizontal" name="clusteringForm" novalidate kc-read-only="!client.access.configure" data-ng-show="create || registered">
<div class="form-group">
<label class="col-md-2 control-label" for="host">{{:: 'host' | translate}}</label>
<div class="col-sm-6">
@@ -27,7 +27,7 @@
</div>
</div>
<div class="form-group">
- <div class="col-md-10 col-md-offset-2" data-ng-show="access.manageRealm">
+ <div class="col-md-10 col-md-offset-2" data-ng-show="client.access.configure">
<button data-kc-save data-ng-show="create">{{:: 'save' | translate}}</button>
</div>
</div>
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-credentials.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-credentials.html
index b1b1062..e6865a3 100755
--- a/themes/src/main/resources/theme/base/admin/resources/partials/client-credentials.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-credentials.html
@@ -7,7 +7,7 @@
<kc-tabs-client></kc-tabs-client>
- <form class="form-horizontal" name="clientForm" novalidate kc-read-only="!access.manageClients">
+ <form class="form-horizontal" name="clientForm" novalidate kc-read-only="!client.access.configure">
<fieldset class="border-top">
<div class="form-group clearfix">
<label class="col-md-2 control-label" for="clientAuthenticatorType"> {{:: 'client-authenticator' | translate}}</label>
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-generic.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-generic.html
index 1d59078..39e9ebc 100644
--- a/themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-generic.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-generic.html
@@ -1,11 +1,11 @@
<div>
- <form class="form-horizontal no-margin-top" name="credentialForm" novalidate kc-read-only="!access.manageClients" data-ng-show="currentAuthenticatorConfigProperties.length > 0" data-ng-controller="ClientGenericCredentialsCtrl">
+ <form class="form-horizontal no-margin-top" name="credentialForm" novalidate kc-read-only="!client.access.configure" data-ng-show="currentAuthenticatorConfigProperties.length > 0" data-ng-controller="ClientGenericCredentialsCtrl">
<fieldset>
<kc-provider-config realm="realm" config="client.attributes" properties="currentAuthenticatorConfigProperties"></kc-provider-config>
</fieldset>
<div class="form-group">
- <div class="col-md-10 col-md-offset-2" data-ng-show="access.manageClients">
+ <div class="col-md-10 col-md-offset-2" data-ng-show="client.access.configure">
<button kc-save data-ng-disabled="!changed">{{:: 'save' | translate}}</button>
<button kc-reset data-ng-disabled="!changed">{{:: 'cancel' | translate}}</button>
</div>
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-jwt.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-jwt.html
index 29f6524..b2a5bf4 100644
--- a/themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-jwt.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-jwt.html
@@ -1,4 +1,4 @@
- <div class="form-horizontal no-margin-top" name="keyForm" novalidate kc-read-only="!access.manageClients" data-ng-controller="ClientSignedJWTCtrl">
+ <div class="form-horizontal no-margin-top" name="keyForm" novalidate kc-read-only="!client.access.configure" data-ng-controller="ClientSignedJWTCtrl">
<div class="form-group">
<label class="col-md-2 control-label" for="useJwksUrl">{{:: 'use-jwks-url' | translate}}</label>
@@ -63,7 +63,7 @@
</div>
<div class="form-group">
- <div class="col-md-10 col-md-offset-2" data-ng-show="access.manageClients">
+ <div class="col-md-10 col-md-offset-2" data-ng-show="client.access.configure">
<button class="btn btn-default" type="submit" data-ng-click="generateSigningKey()">{{:: 'gen-new-keys-and-cert' | translate}}</button>
<button data-ng-disabled="useJwksUrl" class="btn btn-default" type="submit" data-ng-click="importCertificate()">{{:: 'import-certificate' | translate}}</button>
<button kc-save data-ng-disabled="!changed" data-ng-click="save()">{{:: 'save' | translate}}</button>
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-jwt-key-export.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-jwt-key-export.html
index 382f612..82e50b7 100644
--- a/themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-jwt-key-export.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-jwt-key-export.html
@@ -9,7 +9,7 @@
<h1>{{:: 'generate-private-key' | translate}}</h1>
- <form class="form-horizontal" name="keyForm" novalidate kc-read-only="!access.manageRealm">
+ <form class="form-horizontal" name="keyForm" novalidate kc-read-only="!client.access.configure">
<input type="text" readonly value="this is not a login form" style="display: none;">
<input type="password" readonly value="this is not a login form" style="display: none;">
@@ -48,7 +48,7 @@
<kc-tooltip>{{:: 'store-password.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group">
- <div class="col-md-10 col-md-offset-2" data-ng-show="access.manageRealm">
+ <div class="col-md-10 col-md-offset-2" data-ng-show="client.access.configure">
<button class="btn btn-primary" type="submit" data-ng-click="download()">{{:: 'generate-and-download' | translate}}</button>
<button class="btn btn-default" type="submit" data-ng-click="cancel()">{{:: 'cancel' | translate}}</button>
</div>
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-jwt-key-import.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-jwt-key-import.html
index 9968a31..ec88cd6 100644
--- a/themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-jwt-key-import.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-jwt-key-import.html
@@ -9,7 +9,7 @@
<h1>{{:: 'import-client-certificate' | translate}}</h1>
- <form class="form-horizontal" name="keyForm" novalidate kc-read-only="!access.manageRealm">
+ <form class="form-horizontal" name="keyForm" novalidate kc-read-only="!client.access.configure">
<input type="text" readonly value="this is not a login form" style="display: none;">
<input type="password" readonly value="this is not a login form" style="display: none;">
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-secret.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-secret.html
index 744ea80..7cf5bf1 100644
--- a/themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-secret.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-secret.html
@@ -1,5 +1,5 @@
<div>
- <form class="form-horizontal no-margin-top" name="credentialForm" novalidate kc-read-only="!access.manageClients" data-ng-controller="ClientSecretCtrl">
+ <form class="form-horizontal no-margin-top" name="credentialForm" novalidate kc-read-only="!client.access.configure" data-ng-controller="ClientSecretCtrl">
<div class="form-group">
<label class="col-md-2 control-label" for="secret">{{:: 'secret' | translate}}</label>
<div class="col-sm-6">
@@ -7,7 +7,7 @@
<div class="col-sm-6">
<input readonly kc-select-action="click" class="form-control" type="text" id="secret" name="secret" data-ng-model="secret">
</div>
- <div class="col-sm-6" data-ng-show="access.manageClients">
+ <div class="col-sm-6" data-ng-show="client.access.configure">
<button type="submit" data-ng-click="changePassword()" class="btn btn-default">{{:: 'regenerate-secret' | translate}}</button>
</div>
</div>
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-detail.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-detail.html
index 3eb084f..cd6e271 100755
--- a/themes/src/main/resources/theme/base/admin/resources/partials/client-detail.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-detail.html
@@ -7,7 +7,7 @@
<kc-tabs-client></kc-tabs-client>
- <form class="form-horizontal" name="clientForm" novalidate kc-read-only="!access.manageClients">
+ <form class="form-horizontal" name="clientForm" novalidate kc-read-only="!client.access.configure">
<fieldset class="border-top">
<div class="form-group">
<label class="col-md-2 control-label" for="clientId">{{:: 'client-id' | translate}}</label>
@@ -57,7 +57,7 @@
</div>
<kc-tooltip>{{:: 'client-protocol.tooltip' | translate}}</kc-tooltip>
</div>
- <div class="form-group">
+ <div class="form-group" kc-read-only="!client.access.manage">
<label class="col-md-2 control-label" for="protocol">{{:: 'client-template' | translate}}</label>
<div class="col-sm-6">
<div>
@@ -391,7 +391,7 @@
</fieldset>
<div class="form-group">
- <div class="col-md-10 col-md-offset-2" data-ng-show="access.manageClients">
+ <div class="col-md-10 col-md-offset-2" data-ng-show="client.access.configure">
<button kc-save data-ng-disabled="!changed">{{:: 'save' | translate}}</button>
<button kc-reset data-ng-disabled="!changed">{{:: 'cancel' | translate}}</button>
</div>
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-keys.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-keys.html
index e2bc22e..c74f54f 100755
--- a/themes/src/main/resources/theme/base/admin/resources/partials/client-keys.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-keys.html
@@ -7,7 +7,7 @@
<kc-tabs-client></kc-tabs-client>
- <form class="form-horizontal" name="keyForm" novalidate kc-read-only="!access.manageRealm">
+ <form class="form-horizontal" name="keyForm" novalidate kc-read-only="!client.access.configure">
<input type="text" readonly value="this is not a login form" style="display: none;">
<input type="password" readonly value="this is not a login form" style="display: none;">
@@ -137,7 +137,7 @@
kc-select-action="click" readonly>{{keyInfo.certificate}}</textarea>
</div>
</div>
- <div class="form-group" data-ng-show="access.manageRealm">
+ <div class="form-group" data-ng-show="client.access.configure">
<div class="pull-right">
<button class="btn btn-primary" type="submit" data-ng-click="generate()">Generate new keys</button>
</div>
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-list.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-list.html
index 51aaa20..03ebf5c 100755
--- a/themes/src/main/resources/theme/base/admin/resources/partials/client-list.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-list.html
@@ -42,7 +42,7 @@
</td>
<td class="kc-action-cell" kc-open="/realms/{{realm.realm}}/clients/{{client.id}}">{{:: 'edit' | translate}}</td>
<td class="kc-action-cell" data-ng-click="exportClient(client)">{{:: 'export' | translate}}</td>
- <td class="kc-action-cell" data-ng-show="access.manageClients" data-ng-click="removeClient(client)">{{:: 'delete' | translate}}</td>
+ <td class="kc-action-cell" data-ng-show="client.access.manage" data-ng-click="removeClient(client)">{{:: 'delete' | translate}}</td>
</tr>
<tr data-ng-show="(clients | filter:search).length == 0">
<td class="text-muted" colspan="4" data-ng-show="search.clientId">{{:: 'no-results' | translate}}</td>
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-mappers.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-mappers.html
index ba9b9bf..5648c19 100755
--- a/themes/src/main/resources/theme/base/admin/resources/partials/client-mappers.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-mappers.html
@@ -7,7 +7,7 @@
<kc-tabs-client></kc-tabs-client>
- <form class="form-horizontal" name="allowScope" novalidate kc-read-only="!access.manageClients">
+ <form class="form-horizontal" name="allowScope" novalidate kc-read-only="!client.access.manage">
<fieldset class="border-top">
<div class="form-group" ng-show="client.clientTemplate">
<label class="col-md-2 control-label" for="useTemplateScope">Inherit Template Mappers</label>
@@ -35,7 +35,7 @@
</div>
</div>
- <div class="pull-right" data-ng-show="access.manageClients">
+ <div class="pull-right" data-ng-show="client.access.manage">
<a class="btn btn-default" href="#/create/client/{{realm.realm}}/{{client.id}}/mappers">{{:: 'create' | translate}}</a>
<a class="btn btn-default" href="#/realms/{{realm.realm}}/clients/{{client.id}}/add-mappers">{{:: 'add-builtin' | translate}}</a>
</div>
@@ -55,7 +55,7 @@
<td>{{mapperTypes[mapper.protocolMapper].category}}</td>
<td>{{mapperTypes[mapper.protocolMapper].name}}</td>
<td class="kc-action-cell" kc-open="/realms/{{realm.realm}}/clients/{{client.id}}/mappers/{{mapper.id}}">{{:: 'edit' | translate}}</td>
- <td class="kc-action-cell" data-ng-show="access.manageClients" data-ng-click="removeMapper(mapper)">{{:: 'delete' | translate}}</td>
+ <td class="kc-action-cell" data-ng-show="client.access.manage" data-ng-click="removeMapper(mapper)">{{:: 'delete' | translate}}</td>
</tr>
<tr data-ng-show="mappers.length == 0">
<td>{{:: 'no-mappers-available' | translate}}</td>
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-mappers-add.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-mappers-add.html
index 04c2339..6537779 100755
--- a/themes/src/main/resources/theme/base/admin/resources/partials/client-mappers-add.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-mappers-add.html
@@ -45,7 +45,7 @@
</tbody>
</table>
- <div data-ng-show="access.manageRealm">
+ <div data-ng-show="client.access.manage">
<button class="btn btn-primary" data-ng-click="add()">{{:: 'add-selected' | translate}}</button>
</div>
</div>
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-registration-access-token.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-registration-access-token.html
index 55a3546..327b70d 100644
--- a/themes/src/main/resources/theme/base/admin/resources/partials/client-registration-access-token.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-registration-access-token.html
@@ -1,5 +1,5 @@
<div>
- <form class="form-horizontal" name="registrationAccessTokenForm" novalidate kc-read-only="!access.manageClients">
+ <form class="form-horizontal" name="registrationAccessTokenForm" novalidate kc-read-only="!client.access.configure">
<div class="form-group">
<label class="col-md-2 control-label" for="registrationAccessToken">{{:: 'registrationAccessToken' | translate}}</label>
<div class="col-sm-6">
@@ -7,7 +7,7 @@
<div class="col-sm-6">
<input readonly kc-select-action="click" class="form-control" type="text" id="registrationAccessToken" name="registrationAccessToken" data-ng-model="client.registrationAccessToken">
</div>
- <div class="col-sm-6" data-ng-show="access.manageClients">
+ <div class="col-sm-6" data-ng-show="client.access.configure">
<button type="submit" data-ng-click="regenerateRegistrationAccessToken()" class="btn btn-default">{{:: 'registrationAccessToken.regenerate' | translate}}</button>
</div>
</div>
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-revocation.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-revocation.html
index 38560e8..95db767 100755
--- a/themes/src/main/resources/theme/base/admin/resources/partials/client-revocation.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-revocation.html
@@ -7,7 +7,7 @@
<kc-tabs-client></kc-tabs-client>
- <form class="form-horizontal" name="credentialForm" novalidate kc-read-only="!access.manageClients">
+ <form class="form-horizontal" name="credentialForm" novalidate kc-read-only="!client.access.configure">
<fieldset class="border-top">
<div class="form-group">
<label class="col-md-2 control-label" for="notBefore">{{:: 'not-before' | translate}}</label>
@@ -18,7 +18,7 @@
</div>
</fieldset>
<div class="form-group">
- <div class="col-md-10 col-md-offset-2" data-ng-show="access.manageClients">
+ <div class="col-md-10 col-md-offset-2" data-ng-show="client.access.configure">
<button type="submit" data-ng-click="clear()" class="btn btn-default">{{:: 'clear' | translate}}</button>
<button type="submit" data-ng-click="setNotBeforeNow()" class="btn btn-default">{{:: 'set-to-now' | translate}}</button>
<button type="submit" data-ng-click="pushRevocation()" class="btn btn-primary" tooltip-trigger="mouseover mouseout" tooltip="{{:: 'client-revoke.push.tooltip' | translate}}" tooltip-placement="bottom">{{:: 'push' | translate}}</button>
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-role-detail.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-role-detail.html
index de1a763..67c96fc 100755
--- a/themes/src/main/resources/theme/base/admin/resources/partials/client-role-detail.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-role-detail.html
@@ -8,11 +8,9 @@
<li data-ng-hide="create">{{role.name}}</li>
</ol>
- <h1 data-ng-show="create">{{:: 'add-role' | translate}}</h1>
- <h1 data-ng-hide="create">{{role.name|capitalize}}<i class="pficon pficon-delete clickable" data-ng-show="!create && access.manageClients"
- data-ng-hide="changed" data-ng-click="remove()"></i></h1>
+ <kc-tabs-client-role></kc-tabs-client-role>
- <form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageClients">
+ <form class="form-horizontal" name="realmForm" novalidate kc-read-only="!client.access.configure">
<fieldset class="border-top">
<div class="form-group">
@@ -49,13 +47,13 @@
</fieldset>
<div class="form-group">
- <div class="col-md-10 col-md-offset-2" data-ng-show="create && access.manageClients">
+ <div class="col-md-10 col-md-offset-2" data-ng-show="create && client.access.configure">
<button kc-save>{{:: 'save' | translate}}</button>
<button kc-cancel data-ng-click="cancel()">{{:: 'cancel' | translate}}</button>
</div>
</div>
<div class="form-group">
- <div class="col-md-10 col-md-offset-2" data-ng-show="!create && access.manageClients">
+ <div class="col-md-10 col-md-offset-2" data-ng-show="!create && client.access.configure">
<button kc-save data-ng-disabled="!changed">{{:: 'save' | translate}}</button>
<button kc-reset data-ng-disabled="!changed">{{:: 'cancel' | translate}}</button>
</div>
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-role-list.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-role-list.html
index da4d49d..9906074 100755
--- a/themes/src/main/resources/theme/base/admin/resources/partials/client-role-list.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-role-list.html
@@ -10,7 +10,7 @@
<table class="table table-striped table-bordered">
<thead>
<tr>
- <th class="kc-table-actions" colspan="5" data-ng-show="access.manageClients">
+ <th class="kc-table-actions" colspan="5" data-ng-show="client.access.configure">
<div class="pull-right">
<a class="btn btn-default" href="#/create/role/{{realm.realm}}/clients/{{client.id}}">{{:: 'add-role' | translate}}</a>
</div>
@@ -29,7 +29,7 @@
<td translate="{{role.composite}}"></td>
<td>{{role.description}}</td>
<td class="kc-action-cell" kc-open="/realms/{{realm.realm}}/clients/{{client.id}}/roles/{{role.id}}">{{:: 'edit' | translate}}</td>
- <td class="kc-action-cell" data-ng-show="access.manageClients" data-ng-click="removeRole(role)">{{:: 'delete' | translate}}</td>
+ <td class="kc-action-cell" data-ng-show="client.access.configure" data-ng-click="removeRole(role)">{{:: 'delete' | translate}}</td>
</tr>
<tr data-ng-show="!roles || roles.length == 0">
<td>{{:: 'no-client-roles-available' | translate}}</td>
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-saml-key-export.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-saml-key-export.html
index 2165165..ec51e96 100755
--- a/themes/src/main/resources/theme/base/admin/resources/partials/client-saml-key-export.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-saml-key-export.html
@@ -9,7 +9,7 @@
<h1>{{:: 'export-saml-key' | translate}} {{client.clientId|capitalize}}</h1>
- <form class="form-horizontal" name="keyForm" novalidate kc-read-only="!access.manageRealm">
+ <form class="form-horizontal" name="keyForm" novalidate kc-read-only="!client.access.configure">
<input type="text" readonly value="this is not a login form" style="display: none;">
<input type="password" readonly value="this is not a login form" style="display: none;">
@@ -55,7 +55,7 @@
<kc-tooltip>{{:: 'store-password.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group">
- <div class="col-md-10 col-md-offset-2" data-ng-show="access.manageRealm">
+ <div class="col-md-10 col-md-offset-2" data-ng-show="client.access.configure">
<button class="btn btn-primary" type="submit" data-ng-click="download()">{{:: 'download' | translate}}</button>
</div>
</div>
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-saml-key-import.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-saml-key-import.html
index dec13a8..a580a61 100755
--- a/themes/src/main/resources/theme/base/admin/resources/partials/client-saml-key-import.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-saml-key-import.html
@@ -9,7 +9,7 @@
<h1>{{:: 'import-saml-key' | translate}} {{client.clientId|capitalize}}</h1>
- <form class="form-horizontal" name="keyForm" novalidate kc-read-only="!access.manageRealm">
+ <form class="form-horizontal" name="keyForm" novalidate kc-read-only="!client.access.configure">
<input type="text" readonly value="this is not a login form" style="display: none;">
<input type="password" readonly value="this is not a login form" style="display: none;">
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-saml-keys.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-saml-keys.html
index 52e5468..776349d 100755
--- a/themes/src/main/resources/theme/base/admin/resources/partials/client-saml-keys.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-saml-keys.html
@@ -7,7 +7,7 @@
<kc-tabs-client></kc-tabs-client>
- <form class="form-horizontal" name="keyForm" novalidate kc-read-only="!access.manageRealm">
+ <form class="form-horizontal" name="keyForm" novalidate kc-read-only="!client.access.configure">
<fieldset class="form-group col-sm-10" data-ng-show="client.attributes['saml.client.signature'] == 'true'">
<legend uncollapsed><span class="text">{{:: 'signing-key' | translate}}</span> <kc-tooltip>{{:: 'saml-signing-key' | translate}}</kc-tooltip></legend>
<div class="form-group" data-ng-hide="!signingKeyInfo.privateKey">
@@ -27,7 +27,7 @@
</div>
</div>
<div class="form-group">
- <div class="col-md-10 col-md-offset-2" data-ng-show="access.manageRealm">
+ <div class="col-md-10 col-md-offset-2" data-ng-show="client.access.configure">
<button class="btn btn-default" type="submit" data-ng-click="generateSigningKey()">{{:: 'generate-new-keys' | translate}}</button>
<button class="btn btn-default" type="submit" data-ng-click="importSigningKey()">{{:: 'import' | translate}}</button>
<button class="btn btn-default" type="submit" data-ng-hide="!signingKeyInfo.certificate" data-ng-click="exportSigningKey()">{{:: 'export' | translate}}</button>
@@ -53,7 +53,7 @@
</div>
</div>
<div class="form-group">
- <div class="col-md-10 col-md-offset-2" data-ng-show="access.manageRealm">
+ <div class="col-md-10 col-md-offset-2" data-ng-show="client.access.configure">
<button class="btn btn-default" type="submit" data-ng-click="generateEncryptionKey()">{{:: 'generate-new-keys' | translate}}</button>
<button class="btn btn-default" type="submit" data-ng-click="importEncryptionKey()">{{:: 'import' | translate}}</button>
<button class="btn btn-default" type="submit" data-ng-hide="!encryptionKeyInfo.certificate" data-ng-click="exportEncryptionKey()">{{:: 'export' | translate}}</button>
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-scope-mappings.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-scope-mappings.html
index fc10e6b..6cb01f4 100755
--- a/themes/src/main/resources/theme/base/admin/resources/partials/client-scope-mappings.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-scope-mappings.html
@@ -9,7 +9,7 @@
<h2><span>{{client.clientId}}</span> {{:: 'scope-mappings' | translate}} </h2>
<p class="subtitle"></p>
- <form class="form-horizontal" name="allowScope" novalidate kc-read-only="!access.manageClients">
+ <form class="form-horizontal" name="allowScope" novalidate kc-read-only="!client.access.manage">
<fieldset class="border-top">
<div class="form-group" ng-show="client.clientTemplate">
<label class="col-md-2 control-label" for="useTemplateScope">Inherit Template Scope</label>
@@ -25,14 +25,14 @@
<label class="col-md-2 control-label" for="fullScopeAllowed">{{:: 'full-scope-allowed' | translate}}</label>
<kc-tooltip>{{:: 'full-scope-allowed.tooltip' | translate}}</kc-tooltip>
<div class="col-md-6">
- <input ng-model="client.fullScopeAllowed" ng-click="changeFlag()" name="fullScopeAllowed" id="fullScopeAllowed" onoffswitch on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}" />
+ <input kc-read-only="!client.access.manage" ng-model="client.fullScopeAllowed" ng-click="changeFlag()" name="fullScopeAllowed" id="fullScopeAllowed" onoffswitch on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}" />
</div>
</div>
<div class="form-group" ng-show="client.useTemplateScope && template && template.fullScopeAllowed">
<label class="col-md-2 control-label" for="fullScopeAllowed">{{:: 'full-scope-allowed' | translate}}</label>
<kc-tooltip>Client template has full scope allowed, which means this client will have the full scope of all roles.</kc-tooltip>
<div class="col-md-1">
- <input ng-model="template.fullScopeAllowed" name="fullScopeAllowed" id="fullScopeAllowed" ng-disabled="true" onoffswitch on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}"/>
+ <input kc-read-only="!client.access.manage" ng-model="template.fullScopeAllowed" name="fullScopeAllowed" id="fullScopeAllowed" ng-disabled="true" onoffswitch on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}"/>
</div>
<div class="col-md-1">
<i>inherited</i>
@@ -41,7 +41,7 @@
</fieldset>
</form>
- <form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageClients" data-ng-hide="hideRoleSelector()">
+ <form class="form-horizontal" name="realmForm" novalidate kc-read-only="!client.access.manage" data-ng-hide="hideRoleSelector()">
<div class="form-group">
<label class="col-md-2 control-label" class="control-label">{{:: 'realm-roles' | translate}}</label>
<div class="col-md-10">
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-service-account-roles.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-service-account-roles.html
index 27f967d..d57873f 100644
--- a/themes/src/main/resources/theme/base/admin/resources/partials/client-service-account-roles.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-service-account-roles.html
@@ -10,7 +10,7 @@
<h2><span>{{client.clientId}}</span> {{:: 'service-accounts' | translate}} </h2>
<p class="subtitle"></p>
- <form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageClients" data-ng-show="client.serviceAccountsEnabled">
+ <form class="form-horizontal" name="realmForm" novalidate kc-read-only="!client.access.configure" data-ng-show="client.serviceAccountsEnabled">
<div class="form-group">
<label class="col-md-2 control-label" class="control-label">{{:: 'realm-roles' | translate}}</label>
<div class="col-md-10">
@@ -102,7 +102,7 @@
</div>
</form>
- <form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageClients" data-ng-show="!client.serviceAccountsEnabled">
+ <form class="form-horizontal" name="realmForm" novalidate kc-read-only="!client.access.configure" data-ng-show="!client.serviceAccountsEnabled">
<legend><span class="text" translate="service-account-is-not-enabled-for" translate-values="{client: client.clientId}"></span></legend>
</form>
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/group-attributes.html b/themes/src/main/resources/theme/base/admin/resources/partials/group-attributes.html
index c3962c5..e12c553 100755
--- a/themes/src/main/resources/theme/base/admin/resources/partials/group-attributes.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/group-attributes.html
@@ -6,7 +6,7 @@
<kc-tabs-group></kc-tabs-group>
- <form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageUsers">
+ <form class="form-horizontal" name="realmForm" novalidate kc-read-only="!group.access.manage">
<table class="table table-striped table-bordered">
<thead>
<tr>
@@ -29,7 +29,7 @@
</tbody>
</table>
- <div class="form-group" data-ng-show="access.manageUsers">
+ <div class="form-group" data-ng-show="group.access.manage">
<div class="col-md-12">
<button kc-save data-ng-disabled="!changed">{{:: 'save' | translate}}</button>
<button kc-reset data-ng-disabled="!changed">{{:: 'cancel' | translate}}</button>
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/group-detail.html b/themes/src/main/resources/theme/base/admin/resources/partials/group-detail.html
index 677ea32..8fd6461 100755
--- a/themes/src/main/resources/theme/base/admin/resources/partials/group-detail.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/group-detail.html
@@ -16,7 +16,7 @@
</div>
</fieldset>
- <div class="form-group" data-ng-show="access.manageUsers">
+ <div class="form-group" data-ng-show="group.access.manage">
<div class="col-md-10 col-md-offset-2">
<button kc-save data-ng-disabled="!changed">{{:: 'save' | translate}}</button>
<button kc-reset data-ng-disabled="!changed" data-ng-click="cancel()">{{:: 'cancel' | translate}}</button>
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/group-role-mappings.html b/themes/src/main/resources/theme/base/admin/resources/partials/group-role-mappings.html
index 9a1c5b8..62aa7d4 100755
--- a/themes/src/main/resources/theme/base/admin/resources/partials/group-role-mappings.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/group-role-mappings.html
@@ -7,7 +7,7 @@
<kc-tabs-group></kc-tabs-group>
<form class="form-horizontal" name="realmForm" novalidate>
- <div class="form-group" kc-read-only="!access.manageUsers">
+ <div class="form-group" kc-read-only="!group.access.manage">
<label class="col-md-2 control-label" class="control-label">{{:: 'realm-roles' | translate}}</label>
<div class="col-md-10">
@@ -54,7 +54,7 @@
<span>{{:: 'client-roles' | translate}}</span>
<select class="form-control" id="clients" name="clients" ng-change="changeClient()" ng-model="targetClient" ng-options="a.clientId for a in clients | orderBy:'clientId'" ng-disabled="false"></select>
</label>
- <div class="col-md-10" kc-read-only="!access.manageUsers">
+ <div class="col-md-10" kc-read-only="!group.access.manage">
<div class="row" data-ng-hide="targetClient">
<div class="col-md-4"><span class="text-muted">{{:: 'select-client-to-view-roles' | translate}}</span></div>
</div>
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/role-detail.html b/themes/src/main/resources/theme/base/admin/resources/partials/role-detail.html
index 97acab8..3bcaa66 100755
--- a/themes/src/main/resources/theme/base/admin/resources/partials/role-detail.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/role-detail.html
@@ -6,9 +6,7 @@
<li data-ng-show="create">{{:: 'add-role' | translate}}</li>
</ol>
- <h1 data-ng-hide="create">{{role.name|capitalize}} <i id="removeRole" class="pficon pficon-delete clickable" data-ng-show="!create && access.manageRealm"
- data-ng-hide="changed" data-ng-click="remove()"></i></h1>
- <h1 data-ng-show="create">{{:: 'add-role' | translate}}</h1>
+ <kc-tabs-role></kc-tabs-role>
<form class="form-horizontal clearfix" name="realmForm" novalidate kc-read-only="!access.manageRealm">
<fieldset>
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/role-mappings.html b/themes/src/main/resources/theme/base/admin/resources/partials/role-mappings.html
index b57b1d2..013dae7 100755
--- a/themes/src/main/resources/theme/base/admin/resources/partials/role-mappings.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/role-mappings.html
@@ -7,7 +7,7 @@
<kc-tabs-user></kc-tabs-user>
<form class="form-horizontal" name="realmForm" novalidate>
- <div class="form-group" kc-read-only="!access.manageUsers">
+ <div class="form-group" kc-read-only="!user.access.mapRoles">
<label class="col-md-2 control-label" class="control-label">{{:: 'realm-roles' | translate}}</label>
<div class="col-md-10">
@@ -54,7 +54,7 @@
<span>{{:: 'client-roles' | translate}}</span>
<select class="form-control" id="clients" name="clients" ng-change="changeClient()" ng-model="targetClient" ng-options="a.clientId for a in clients | orderBy:'clientId'" ng-disabled="false"></select>
</label>
- <div class="col-md-10" kc-read-only="!access.manageUsers">
+ <div class="col-md-10" kc-read-only="!user.access.mapRoles">
<div class="row" data-ng-hide="targetClient">
<div class="col-md-4"><span class="text-muted">{{:: 'select-client-to-view-roles' | translate}}</span></div>
</div>
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/user-attributes.html b/themes/src/main/resources/theme/base/admin/resources/partials/user-attributes.html
index 26d30e1..10e3d8f 100755
--- a/themes/src/main/resources/theme/base/admin/resources/partials/user-attributes.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/user-attributes.html
@@ -6,7 +6,7 @@
<kc-tabs-user></kc-tabs-user>
- <form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageUsers">
+ <form class="form-horizontal" name="realmForm" novalidate kc-read-only="!user.access.manage">
<table class="table table-striped table-bordered">
<thead>
<tr>
@@ -29,7 +29,7 @@
</tbody>
</table>
- <div class="form-group" data-ng-show="access.manageUsers">
+ <div class="form-group" data-ng-show="user.access.manage">
<div class="col-md-12">
<button kc-save data-ng-disabled="!changed">{{:: 'save' | translate}}</button>
<button kc-reset data-ng-disabled="!changed">{{:: 'cancel' | translate}}</button>
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/user-detail.html b/themes/src/main/resources/theme/base/admin/resources/partials/user-detail.html
index f1998f8..88e66e7 100755
--- a/themes/src/main/resources/theme/base/admin/resources/partials/user-detail.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/user-detail.html
@@ -7,7 +7,7 @@
<kc-tabs-user></kc-tabs-user>
- <form class="form-horizontal" name="userForm" novalidate kc-read-only="!access.manageUsers">
+ <form class="form-horizontal" name="userForm" novalidate kc-read-only="!create && !user.access.manage">
<fieldset class="border-top">
<div class="form-group">
@@ -62,7 +62,7 @@
<div class="form-group clearfix block">
<label class="col-md-2 control-label" for="userEnabled">{{:: 'user-enabled' | translate}}</label>
<div class="col-md-6">
- <input ng-model="user.enabled" name="userEnabled" id="userEnabled" ng-disabled="!access.manageUsers" onoffswitch on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}"/>
+ <input ng-model="user.enabled" name="userEnabled" id="userEnabled" ng-disabled="!create && !user.access.manage" onoffswitch on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}"/>
</div>
<kc-tooltip>{{:: 'user-enabled.tooltip' | translate}}</kc-tooltip>
</div>
@@ -93,7 +93,7 @@
<div class="form-group clearfix block">
<label class="col-md-2 control-label" for="emailVerified">{{:: 'email-verified' | translate}}</label>
<div class="col-md-6">
- <input ng-model="user.emailVerified" name="emailVerified" id="emailVerified" ng-disabled="!access.manageUsers" onoffswitch on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}"/>
+ <input ng-model="user.emailVerified" name="emailVerified" id="emailVerified" ng-disabled="!create && !user.access.manage" onoffswitch on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}"/>
</div>
<kc-tooltip>{{:: 'email-verified.tooltip' | translate}}</kc-tooltip>
</div>
@@ -138,7 +138,7 @@
<button kc-cancel data-ng-click="cancel()">{{:: 'cancel' | translate}}</button>
</div>
- <div class="col-md-10 col-md-offset-2" data-ng-show="!create && access.manageUsers">
+ <div class="col-md-10 col-md-offset-2" data-ng-show="!create && user.access.manage">
<button kc-save data-ng-disabled="!changed">{{:: 'save' | translate}}</button>
<button kc-reset data-ng-disabled="!changed">{{:: 'cancel' | translate}}</button>
</div>
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/user-group-membership.html b/themes/src/main/resources/theme/base/admin/resources/partials/user-group-membership.html
index a9c0e6d..f7bf04e 100755
--- a/themes/src/main/resources/theme/base/admin/resources/partials/user-group-membership.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/user-group-membership.html
@@ -7,7 +7,7 @@
<kc-tabs-user></kc-tabs-user>
<form class="form-horizontal" name="realmForm" novalidate>
- <div class="form-group" kc-read-only="!access.manageUsers">
+ <div class="form-group" kc-read-only="!user.access.manageGroupMembership">
<label class="col-md-1 control-label" class="control-label"></label>
<div class="col-md-8" >
@@ -21,7 +21,7 @@
<label class="control-label">{{:: 'group-membership' | translate}}</label>
<kc-tooltip>{{:: 'group-membership.tooltip' | translate}}</kc-tooltip>
- <div class="pull-right" data-ng-show="access.manageUsers">
+ <div class="pull-right" data-ng-show="user.access.manageGroupMembership">
<button id="leaveGroups" class="btn btn-default" ng-click="leaveGroup()">{{:: 'leave' | translate}}</button>
</div>
</div>
@@ -53,7 +53,7 @@
<label class="control-label">{{:: 'available-groups' | translate}}</label>
<kc-tooltip>{{:: 'membership.available-groups.tooltip' | translate}}</kc-tooltip>
- <div class="pull-right" data-ng-show="access.manageUsers">
+ <div class="pull-right" data-ng-show="user.access.manageGroupMembership">
<button id="joinGroup" class="btn btn-default" ng-click="joinGroup()">{{:: 'join' | translate}}</button>
</div>
</div>
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/user-list.html b/themes/src/main/resources/theme/base/admin/resources/partials/user-list.html
index 4864acf..569e1fc 100755
--- a/themes/src/main/resources/theme/base/admin/resources/partials/user-list.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/user-list.html
@@ -1,5 +1,6 @@
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2" ng-init="init()">
- <h1>{{:: 'users' | translate}}</h1>
+
+ <kc-tabs-users></kc-tabs-users>
<table class="table table-striped table-bordered">
<caption data-ng-show="users" class="hidden">{{:: 'table-of-realm-users' | translate}}</caption>
@@ -54,7 +55,7 @@
<td class="clip">{{user.firstName}}</td>
<td class="kc-action-cell" kc-open="/realms/{{realm.realm}}/users/{{user.id}}">{{:: 'edit' | translate}}</td>
<td data-ng-show="serverInfo.profileInfo.disabledFeatures.indexOf('IMPERSONATION') == -1 && access.impersonation" class="kc-action-cell" data-ng-click="impersonate(user.id)">{{:: 'impersonate' | translate}}</td>
- <td data-ng-show="access.manageUsers" class="kc-action-cell" data-ng-click="removeUser(user)">{{:: 'delete' | translate}}</td>
+ <td data-ng-show="user.access.manage" class="kc-action-cell" data-ng-click="removeUser(user)">{{:: 'delete' | translate}}</td>
</tr>
<tr data-ng-show="!users || users.length == 0">
<td class="text-muted" data-ng-show="!users">{{:: 'users.instruction' | translate}}</td>
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/user-sessions.html b/themes/src/main/resources/theme/base/admin/resources/partials/user-sessions.html
index 1fe11e9..02c1959 100755
--- a/themes/src/main/resources/theme/base/admin/resources/partials/user-sessions.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/user-sessions.html
@@ -8,9 +8,9 @@
<table class="table table-striped table-bordered">
<thead>
- <tr data-ng-show="access.manageUsers">
+ <tr data-ng-show="user.access.manage">
<th class="kc-table-actions" colspan="6">
- <div class="pull-right" data-ng-show="access.manageUsers">
+ <div class="pull-right" data-ng-show="user.access.manage">
<a id="logoutAllSessions" class="btn btn-default" ng-click="logoutAll()">{{:: 'logout-all-sessions' | translate}}</a>
</div>
</th>
@@ -20,7 +20,7 @@
<th>{{:: 'started' | translate}}</th>
<th>{{:: 'last-access' | translate}}</th>
<th>{{:: 'clients' | translate}}</th>
- <th data-ng-show="access.manageUsers">{{:: 'action' | translate}}</th>
+ <th data-ng-show="user.access.manage">{{:: 'action' | translate}}</th>
</tr>
</thead>
<tbody>
@@ -34,7 +34,7 @@
</div>
</ul>
</td>
- <td class="kc-action-cell" data-ng-show="access.manageUsers" ng-click="logoutSession(session.id)">{{:: 'logout' | translate}}</td>
+ <td class="kc-action-cell" data-ng-show="user.access.manage" ng-click="logoutSession(session.id)">{{:: 'logout' | translate}}</td>
</tr>
</tbody>
</table>
diff --git a/themes/src/main/resources/theme/base/admin/resources/templates/kc-menu.html b/themes/src/main/resources/theme/base/admin/resources/templates/kc-menu.html
index 25d22d7..53b0a3d 100755
--- a/themes/src/main/resources/theme/base/admin/resources/templates/kc-menu.html
+++ b/themes/src/main/resources/theme/base/admin/resources/templates/kc-menu.html
@@ -16,7 +16,7 @@
</div>
</div>
- <div class="nav-category" data-ng-show="current.realm">
+ <div class="nav-category" data-ng-show="current.realm && (access.viewRealm || access.queryClients || access.viewIdentityProviders)">
<h2>{{:: 'configure' | translate}}</h2>
<ul class="nav nav-pills nav-stacked">
<li data-ng-show="access.viewRealm" data-ng-class="((!path[2]
@@ -32,7 +32,7 @@
|| path[2] == 'keys-settings' || path[2] == 'smtp-settings' || path[2] == 'ldap-settings' || path[2] == 'auth-settings') && path[3] != 'clients') && 'active'">
<a href="#/realms/{{realm.realm}}"><span class="pficon pficon-settings"></span> {{:: 'realm-settings' | translate}}</a>
</li>
- <li data-ng-show="access.viewClients" data-ng-class="(path[2] == 'clients' || path[3] == 'clients') && 'active'"><a href="#/realms/{{realm.realm}}/clients"><i class="fa fa-cube"></i> {{:: 'clients' | translate}}</a></li>
+ <li data-ng-show="access.queryClients" data-ng-class="(path[2] == 'clients' || path[3] == 'clients') && 'active'"><a href="#/realms/{{realm.realm}}/clients"><i class="fa fa-cube"></i> {{:: 'clients' | translate}}</a></li>
<li data-ng-show="access.viewClients" data-ng-class="(path[2] == 'client-templates' || path[3] == 'client-templates') && 'active'"><a href="#/realms/{{realm.realm}}/client-templates"><i class="fa fa-cubes"></i> {{:: 'client-templates' | translate}}</a></li>
<li data-ng-show="access.viewRealm" data-ng-class="(path[2] == 'roles' || path[2] == 'default-roles') && 'active'"><a href="#/realms/{{realm.realm}}/roles"><i class="fa fa-tasks"></i> {{:: 'roles' | translate}}</a></li>
<li data-ng-show="access.viewIdentityProviders" data-ng-class="(path[2] == 'identity-provider-settings' || path[2] == 'identity-provider-mappers') && 'active'"><a href="#/realms/{{realm.realm}}/identity-provider-settings"><i class="fa fa-exchange"></i> {{:: 'identity-providers' | translate}}</a></li>
@@ -45,12 +45,12 @@
</ul>
</div>
- <div class="nav-category" data-ng-show="current.realm">
+ <div class="nav-category" data-ng-show="current.realm && (access.viewRealm || access.queryGroups || access.queryUsers || access.viewEvents)">
<h2>{{:: 'manage' | translate}}</h2>
<ul class="nav nav-pills nav-stacked">
- <li data-ng-show="access.viewUsers" data-ng-class="(path[2] == 'groups'
+ <li data-ng-show="access.queryGroups" data-ng-class="(path[2] == 'groups'
|| path[2] == 'default-groups') && 'active'"><a href="#/realms/{{realm.realm}}/groups"><span class="pficon pficon-users"></span> {{:: 'groups' | translate}}</a></li>
- <li data-ng-show="access.viewUsers" data-ng-class="(path[2] == 'users') && 'active'"><a href="#/realms/{{realm.realm}}/users"><span class="pficon pficon-user"></span> {{:: 'users' | translate}}</a></li>
+ <li data-ng-show="access.queryUsers" data-ng-class="(path[2] == 'users') && 'active'"><a href="#/realms/{{realm.realm}}/users"><span class="pficon pficon-user"></span> {{:: 'users' | translate}}</a></li>
<li data-ng-show="access.viewRealm" data-ng-class="(path[2] == 'sessions') && 'active'"><a href="#/realms/{{realm.realm}}/sessions/realm"><i class="fa fa-clock-o"></i> {{:: 'sessions' | translate}}</a></li>
<li data-ng-show="access.viewEvents" data-ng-class="(path[2] == 'events'
|| path[2] == 'events-settings'
diff --git a/themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-client.html b/themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-client.html
index 2a3be32..e5b7c21 100755
--- a/themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-client.html
+++ b/themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-client.html
@@ -3,7 +3,7 @@
<h1 data-ng-show="create">{{:: 'add-client' | translate}}</h1>
<h1 data-ng-hide="create">
{{client.clientId|capitalize}}
- <i id="removeClient" class="pficon pficon-delete clickable" data-ng-show="access.manageClients" data-ng-click="removeClient()"></i>
+ <i id="removeClient" class="pficon pficon-delete clickable" data-ng-show="client.access.manage" data-ng-click="removeClient()"></i>
</h1>
<ul class="nav nav-tabs" data-ng-hide="create && !path[4]">
@@ -43,5 +43,9 @@
<a href="#/realms/{{realm.realm}}/clients/{{client.id}}/service-account-roles">{{:: 'service-account-roles' | translate}}</a>
<kc-tooltip>{{:: 'service-account-roles.tooltip' | translate}}</kc-tooltip>
</li>
+ <li ng-class="{active: path[4] == 'permissions'}" data-ng-show="client.access.manage && access.manageAuthorization">
+ <a href="#/realms/{{realm.realm}}/clients/{{client.id}}/permissions">{{:: 'authz-permissions' | translate}}</a>
+ <kc-tooltip>{{:: 'manage-permissions-client.tooltip' | translate}}</kc-tooltip>
+ </li>
</ul>
</div>
\ No newline at end of file
diff --git a/themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-client-role.html b/themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-client-role.html
new file mode 100755
index 0000000..9637186
--- /dev/null
+++ b/themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-client-role.html
@@ -0,0 +1,13 @@
+<div data-ng-controller="RoleTabCtrl">
+ <h1 data-ng-show="create">{{:: 'add-role' | translate}}</h1>
+ <h1 data-ng-hide="create">{{role.name|capitalize}}<i class="pficon pficon-delete clickable" data-ng-show="!create && access.manageClients"
+ data-ng-hide="changed" data-ng-click="remove()"></i></h1>
+
+ <ul class="nav nav-tabs" data-ng-show="!create">
+ <li ng-class="{active: !path[6]}"><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/roles/{{role.id}}">{{:: 'details' | translate}}</a></li>
+ <li ng-class="{active: path[6] && path[6] == 'permissions'}">
+ <a href="#/realms/{{realm.realm}}/clients/{{client.id}}/roles/{{role.id}}/permissions">{{:: 'authz-permissions' | translate}}</a>
+ <kc-tooltip>{{:: 'manage-permissions-role.tooltip' | translate}}</kc-tooltip>
+ </li>
+ </ul>
+</div>
\ No newline at end of file
diff --git a/themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-group.html b/themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-group.html
index c242336..e73d998 100755
--- a/themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-group.html
+++ b/themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-group.html
@@ -1,7 +1,7 @@
<div data-ng-controller="GroupTabCtrl">
<h1>
{{group.name|capitalize}}
- <i id="removeGroup" class="pficon pficon-delete clickable" data-ng-show="access.manageUsers" data-ng-click="removeGroup()"></i>
+ <i id="removeGroup" class="pficon pficon-delete clickable" data-ng-show="group.access.manage" data-ng-click="removeGroup()"></i>
</h1>
<ul class="nav nav-tabs">
@@ -9,5 +9,9 @@
<li ng-class="{active: path[4] == 'attributes'}"><a href="#/realms/{{realm.realm}}/groups/{{group.id}}/attributes">{{:: 'attributes' | translate}}</a></li>
<li ng-class="{active: path[4] == 'role-mappings'}" ><a href="#/realms/{{realm.realm}}/groups/{{group.id}}/role-mappings">{{:: 'role-mappings' | translate}}</a></li>
<li ng-class="{active: path[4] == 'members'}"><a href="#/realms/{{realm.realm}}/groups/{{group.id}}/members">{{:: 'members' | translate}}</a></li>
+ <li ng-class="{active: path[4] == 'permissions'}" data-ng-show="group.access.manage && access.manageAuthorization">
+ <a href="#/realms/{{realm.realm}}/groups/{{group.id}}/permissions">{{:: 'authz-permissions' | translate}}</a>
+ <kc-tooltip>{{:: 'manage-permissions-group.tooltip' | translate}}</kc-tooltip>
+ </li>
</ul>
</div>
\ No newline at end of file
diff --git a/themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-role.html b/themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-role.html
new file mode 100755
index 0000000..785c7e9
--- /dev/null
+++ b/themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-role.html
@@ -0,0 +1,13 @@
+<div data-ng-controller="RoleTabCtrl">
+ <h1 data-ng-hide="create">{{role.name|capitalize}} <i id="removeRole" class="pficon pficon-delete clickable" data-ng-show="!create && access.manageRealm"
+ data-ng-hide="changed" data-ng-click="remove()"></i></h1>
+ <h1 data-ng-show="create">{{:: 'add-role' | translate}}</h1>
+
+ <ul class="nav nav-tabs" data-ng-show="!create">
+ <li ng-class="{active: !path[4]}"><a href="#/realms/{{realm.realm}}/roles/{{role.id}}">{{:: 'details' | translate}}</a></li>
+ <li ng-class="{active: path[4] == 'permissions'}" data-ng-show="access.manageRealm && access.manageAuthorization">
+ <a href="#/realms/{{realm.realm}}/roles/{{role.id}}/permissions">{{:: 'authz-permissions' | translate}}</a>
+ <kc-tooltip>{{:: 'manage-permissions-role.tooltip' | translate}}</kc-tooltip>
+ </li>
+ </ul>
+</div>
\ No newline at end of file
diff --git a/themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-user.html b/themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-user.html
index 66dad39..9c372e3 100755
--- a/themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-user.html
+++ b/themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-user.html
@@ -8,7 +8,7 @@
<ul class="nav nav-tabs" data-ng-show="!create">
<li ng-class="{active: !path[4] && path[0] != 'create'}"><a href="#/realms/{{realm.realm}}/users/{{user.id}}">{{:: 'details' | translate}}</a></li>
<li ng-class="{active: path[4] == 'user-attributes'}"><a href="#/realms/{{realm.realm}}/users/{{user.id}}/user-attributes">{{:: 'attributes' | translate}}</a></li>
- <li ng-class="{active: path[4] == 'user-credentials'}" data-ng-show="access.manageUsers"><a href="#/realms/{{realm.realm}}/users/{{user.id}}/user-credentials">{{:: 'credentials' | translate}}</a></li>
+ <li ng-class="{active: path[4] == 'user-credentials'}" data-ng-show="user.access.manage"><a href="#/realms/{{realm.realm}}/users/{{user.id}}/user-credentials">{{:: 'credentials' | translate}}</a></li>
<li ng-class="{active: path[4] == 'role-mappings'}" ><a href="#/realms/{{realm.realm}}/users/{{user.id}}/role-mappings">{{:: 'role-mappings' | translate}}</a></li>
<li ng-class="{active: path[4] == 'groups'}" ><a href="#/realms/{{realm.realm}}/users/{{user.id}}/groups">{{:: 'groups' | translate}}</a></li>
<li ng-class="{active: path[4] == 'consents'}"><a href="#/realms/{{realm.realm}}/users/{{user.id}}/consents">{{:: 'consents' | translate}}</a></li>
diff --git a/themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-users.html b/themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-users.html
new file mode 100755
index 0000000..f641dc0
--- /dev/null
+++ b/themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-users.html
@@ -0,0 +1,11 @@
+<div >
+ <h1>{{:: 'users' | translate}}</h1>
+
+ <ul class="nav nav-tabs">
+ <li ng-class="{active: path[2] == 'users'}"><a href="#/realms/{{realm.realm}}/users">{{:: 'lookup' | translate}}</a></li>
+ <li ng-class="{active: path[2] == 'users-permissions'}" data-ng-show="access.manageUsers && access.manageAuthorization">
+ <a href="#/realms/{{realm.realm}}/users-permissions">{{:: 'authz-permissions' | translate}}</a>
+ <kc-tooltip>{{:: 'manage-permissions-users.tooltip' | translate}}</kc-tooltip>
+ </li>
+ </ul>
+</div>
\ No newline at end of file