Merge pull request #4256 from patriot1burke/master

KEYCLOAK-3444 KEYCLOAK-528
This commit is contained in:
Bill Burke 2017-06-26 13:27:08 -04:00 committed by GitHub
commit d77824ff98
145 changed files with 8178 additions and 2295 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -46,6 +46,10 @@ public interface ClientsResource {
@Produces(MediaType.APPLICATION_JSON)
public List<ClientRepresentation> findAll();
@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);

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -35,12 +35,19 @@ import java.util.Map;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
public abstract class AbstractEvaluationContext implements EvaluationContext {
public class DefaultEvaluationContext implements EvaluationContext {
private final KeycloakSession keycloakSession;
protected final KeycloakSession keycloakSession;
protected final Identity identity;
public AbstractEvaluationContext(KeycloakSession keycloakSession) {
public DefaultEvaluationContext(Identity identity, KeycloakSession keycloakSession) {
this.keycloakSession = keycloakSession;
this.identity = identity;
}
@Override
public Identity getIdentity() {
return identity;
}
public Map<String, Collection<String>> getBaseAttributes() {
@ -60,4 +67,9 @@ public abstract class AbstractEvaluationContext implements EvaluationContext {
return attributes;
}
@Override
public Attributes getAttributes() {
return Attributes.from(getBaseAttributes());
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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,44 +234,72 @@ 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;
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);
}
if (map != null) {
roles = map.get("roles");
}
if (roles == null) {
return false;
}
for (String r : role) {
if (roles.contains(r)) {
return true;
}
}
} else {
return hasRoleInToken(roles);
}
return false;
} catch (Throwable t) {
return false;
}
}
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;
}
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;
}
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);
}
if (map != null) {
roles = map.get("roles");
}
if (roles == null) {
return false;
}
for (String r : role) {
if (roles.contains(r)) {
return true;
}
}
}
return false;
}
private boolean authenticateClient(ClientModel client) {
if (client == null) {
return false;

View file

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

View file

@ -98,4 +98,7 @@ public class AdminAuth {
return false;
}
public enum Resource {
CLIENT, USER, REALM, EVENTS, IDENTITY_PROVIDER, IMPERSONATION, AUTHORIZATION
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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);
return ModelToRepresentation.toGroupHierarchy(group, true);
rep.setAccess(auth.groups().getAccess(group));
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();
}
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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,12 +311,8 @@ 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");
@ -355,4 +320,57 @@ public class RoleContainerResource extends RoleResource {
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");
}
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();
}
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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();
public UserResource user(final @PathParam("id") String id) {
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();
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();
}
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -15,24 +15,21 @@
* limitations under the License.
*/
package org.keycloak.services.resources.admin;
package org.keycloak.services.resources.admin.permissions;
import org.keycloak.models.AdminRoles;
import org.keycloak.models.ClientModel;
import org.keycloak.models.ImpersonationConstants;
import org.keycloak.services.ForbiddenException;
import org.keycloak.services.resources.admin.AdminAuth;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class RealmAuth {
class RealmAuth {
private Resource resource;
public enum Resource {
CLIENT, USER, REALM, EVENTS, IDENTITY_PROVIDER, IMPERSONATION, AUTHORIZATION
}
private AdminAuth.Resource resource;
private AdminAuth auth;
private ClientModel realmAdminApp;
@ -42,7 +39,7 @@ public class RealmAuth {
this.realmAdminApp = realmAdminApp;
}
public RealmAuth init(Resource resource) {
public RealmAuth init(AdminAuth.Resource resource) {
this.resource = resource;
return this;
}
@ -52,11 +49,15 @@ public class RealmAuth {
}
public void requireAny() {
if (!auth.hasOneOfAppRole(realmAdminApp, AdminRoles.ALL_REALM_ROLES)) {
if (!hasAny()) {
throw new ForbiddenException();
}
}
public boolean hasAny() {
return auth.hasOneOfAppRole(realmAdminApp, AdminRoles.ALL_REALM_ROLES);
}
public boolean hasView() {
return auth.hasOneOfAppRole(realmAdminApp, getViewRole(resource), getManageRole(resource));
}
@ -77,7 +78,7 @@ public class RealmAuth {
}
}
private String getViewRole(Resource resource) {
private String getViewRole(AdminAuth.Resource resource) {
switch (resource) {
case CLIENT:
return AdminRoles.VIEW_CLIENTS;
@ -96,7 +97,7 @@ public class RealmAuth {
}
}
private String getManageRole(Resource resource) {
private String getManageRole(AdminAuth.Resource resource) {
switch (resource) {
case CLIENT:
return AdminRoles.MANAGE_CLIENTS;

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,215 +0,0 @@
/*
* 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.Ignore;
import org.junit.Test;
import org.keycloak.admin.client.resource.RealmResource;
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.Constants;
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.RealmRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.representations.idm.authorization.DecisionEffect;
import org.keycloak.representations.idm.authorization.DecisionStrategy;
import org.keycloak.representations.idm.authorization.Logic;
import org.keycloak.representations.idm.authorization.PolicyEvaluationRequest;
import org.keycloak.representations.idm.authorization.PolicyEvaluationResponse;
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
import org.keycloak.representations.idm.authorization.ScopePermissionRepresentation;
import org.keycloak.testsuite.AbstractKeycloakTest;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
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 $
*/
@Ignore
public class FineGrainAdminLocalTest extends AbstractKeycloakTest {
@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 setupDefaults(KeycloakSession session) {
RealmModel realm = session.realms().getRealmByName(TEST);
ClientModel client = realm.getClientByClientId("realm-management");
AuthorizationProvider authz = session.getProvider(AuthorizationProvider.class);
ResourceServer resourceServer = authz.getStoreFactory().getResourceServerStore().create(client.getId());
Scope mapRoleScope = authz.getStoreFactory().getScopeStore().create("map-role", resourceServer);
Scope manageScope = authz.getStoreFactory().getScopeStore().create("manage", resourceServer);
Policy manageUsersPolicy = null;
Policy manageClientsPolicy = null;
for (RoleModel role : client.getRoles()) {
Policy policy = createRolePolicy(authz, resourceServer, role);
if (role.getName().equals(AdminRoles.MANAGE_USERS)) {
manageUsersPolicy = policy;
} else if (role.getName().equals(AdminRoles.MANAGE_CLIENTS)) {
manageClientsPolicy = policy;
}
Resource resource = createRoleResource(authz, resourceServer, role);
Set<Scope> scopeset = new HashSet<>();
scopeset.add(mapRoleScope);
resource.updateScopes(scopeset);
String name = "map.role.permission." + client.getClientId() + "." + role.getName();
Policy permission = addScopePermission(authz, resourceServer, name, resource, mapRoleScope, policy);
}
Resource usersResource = authz.getStoreFactory().getResourceStore().create("Users", resourceServer, resourceServer.getClientId());
Set<Scope> scopeset = new HashSet<>();
scopeset.add(manageScope);
usersResource.updateScopes(scopeset);
addScopePermission(authz, resourceServer, "Users.manage.permission", usersResource, manageScope, manageUsersPolicy);
}
private 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);
}
private static Resource createRoleResource(AuthorizationProvider authz, ResourceServer resourceServer, RoleModel role) {
String roleName = getRoleResourceName(role);
Resource resource = authz.getStoreFactory().getResourceStore().create(roleName, resourceServer, resourceServer.getClientId());
resource.setType("Role");
return resource;
}
private static String getRoleResourceName(RoleModel role) {
String roleName = "realm";
if (role.getContainer() instanceof ClientModel) {
ClientModel client = (ClientModel)role.getContainer();
roleName = client.getClientId();
}
roleName = "role.resource." + roleName + "." + role.getName();
return roleName;
}
private static Policy createRolePolicy(AuthorizationProvider authz, ResourceServer resourceServer, RoleModel role) {
String roleName = "realm";
if (role.getContainer() instanceof ClientModel) {
ClientModel client = (ClientModel) role.getContainer();
roleName = client.getClientId() ;
}
roleName = "role.policy." + roleName + "." + role.getName();
PolicyRepresentation representation = new PolicyRepresentation();
representation.setName(roleName);
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 void setupUsers(KeycloakSession session) {
RealmModel realm = session.realms().getRealmByName(TEST);
ClientModel client = realm.getClientByClientId(Constants.REALM_MANAGEMENT_CLIENT_ID);
UserModel admin = session.users().addUser(realm, "admin");
admin.grantRole(client.getRole(AdminRoles.REALM_ADMIN));
UserModel manageUserOnlyUser = session.users().addUser(realm, "manage-user");
RoleModel manageUsersRole = client.getRole(AdminRoles.MANAGE_USERS);
manageUserOnlyUser.grantRole(manageUsersRole);
UserModel manageRealmUser = session.users().addUser(realm, "manage-realm");
manageRealmUser.grantRole(manageUsersRole);
RoleModel manageRealmRole = client.getRole(AdminRoles.MANAGE_REALM);
manageRealmUser.grantRole(manageRealmRole);
}
@Test
public void testUI() throws Exception {
testingClient.server().run(FineGrainAdminLocalTest::setupDefaults);
testingClient.server().run(FineGrainAdminLocalTest::setupUsers);
//Thread.sleep(1000000000);
}
public static void evaluateAdminHasManageRealmPermissions(KeycloakSession session) {
RealmModel realm = session.realms().getRealmByName(TEST);
UserModel admin = session.users().getUserByUsername("admin", realm);
AuthorizationProvider authz = session.getProvider(AuthorizationProvider.class);
ClientModel client = realm.getClientByClientId(Constants.REALM_MANAGEMENT_CLIENT_ID);
ResourceServer resourceServer = authz.getStoreFactory().getResourceServerStore().findByClient(client.getId());
RoleModel manageRealmRole = client.getRole(AdminRoles.MANAGE_REALM);
Resource roleResource = authz.getStoreFactory().getResourceStore().findByName(getRoleResourceName(manageRealmRole), resourceServer.getId());
}
@Test
public void testEvaluation() throws Exception {
testingClient.server().run(FineGrainAdminLocalTest::setupDefaults);
testingClient.server().run(FineGrainAdminLocalTest::setupUsers);
RealmResource realm = adminClient.realm(TEST);
String resourceServerId = realm.clients().findByClientId(Constants.REALM_MANAGEMENT_CLIENT_ID).get(0).getId();
UserRepresentation admin = realm.users().search("admin").get(0);
UserRepresentation manageUser = realm.users().search("manage-user").get(0);
UserRepresentation manageRealm = realm.users().search("manage-realm").get(0);
PolicyEvaluationRequest request = new PolicyEvaluationRequest();
request.setUserId(admin.getId());
request.setClientId(resourceServerId);
request.addResource("role.resource." + Constants.REALM_MANAGEMENT_CLIENT_ID + "." + AdminRoles.MANAGE_REALM,
"map-role");
PolicyEvaluationResponse result = realm.clients().get(resourceServerId).authorization().policies().evaluate(request);
Assert.assertEquals(result.getStatus(), DecisionEffect.PERMIT);
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -144,4 +144,50 @@ module.service('AuthzDialog', function($modal) {
}
return dialog;
});
});
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'
}
});
});

View file

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

Some files were not shown because too many files have changed in this diff Show more