/*
* 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.authorization.store.ResourceStore;
import org.keycloak.models.AdminRoles;
import org.keycloak.models.ClientModel;
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.Map;
/**
* @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) {
ResourceServer server = getResourceServer(role);
if (authz.getStoreFactory().getResourceStore().findByName(getRoleResourceName(role), server.getId()) != null) {
return;
}
createResource(role);
} else {
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;
Resource resource = authz.getStoreFactory().getResourceStore().findByName(getRoleResourceName(role), server.getId());
if (resource == 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;
Resource resource = authz.getStoreFactory().getResourceStore().findByName(getRoleResourceName(role), server.getId());
if (resource == 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;
Resource resource = authz.getStoreFactory().getResourceStore().findByName(getRoleResourceName(role), server.getId());
if (resource == 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 authz.getStoreFactory().getResourceServerStore().findByClient(client.getId());
}
/**
* Is admin allowed to map this role?
*
* @param role
* @return
*/
@Override
public boolean canMapRole(RoleModel role) {
if (root.users().canManageDefault()) return true;
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 = getResourceServer(role);
Policy policy = authz.getStoreFactory().getPolicyStore().findByName(getMapRolePermissionName(role), resourceServer.getId());
if (policy.getAssociatedPolicies().isEmpty()) {
return false;
}
Resource roleResource = resource(role);
Scope mapRoleScope = getMapRoleScope(resourceServer);
return root.evaluatePermission(roleResource, mapRoleScope, resourceServer);
}
@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().canManage((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 true;
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 = getResourceServer(role);
Policy policy = authz.getStoreFactory().getPolicyStore().findByName(getMapCompositePermissionName(role), resourceServer.getId());
if (policy.getAssociatedPolicies().isEmpty()) {
return false;
}
Resource roleResource = resource(role);
Scope scope = getMapCompositeScope(resourceServer);
return root.evaluatePermission(roleResource, scope, resourceServer);
}
@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 = getResourceServer(role);
Policy policy = authz.getStoreFactory().getPolicyStore().findByName(getMapClientScopePermissionName(role), resourceServer.getId());
if (policy.getAssociatedPolicies().isEmpty()) {
return false;
}
Resource roleResource = resource(role);
Scope scope = getMapClientScope(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 getMapRoleScope(ResourceServer server) {
Scope scope = authz.getStoreFactory().getScopeStore().findByName(MAP_ROLE_SCOPE, server.getId());
if (scope == null) {
scope = authz.getStoreFactory().getScopeStore().create(MAP_ROLE_SCOPE, server);
}
return scope;
}
private Scope getMapClientScope(ResourceServer server) {
Scope scope = authz.getStoreFactory().getScopeStore().findByName(MAP_ROLE_CLIENT_SCOPE_SCOPE, server.getId());
if (scope == null) {
scope = authz.getStoreFactory().getScopeStore().create(MAP_ROLE_CLIENT_SCOPE_SCOPE, server);
}
return scope;
}
private Scope getMapCompositeScope(ResourceServer server) {
Scope scope = authz.getStoreFactory().getScopeStore().findByName(MAP_ROLE_COMPOSITE_SCOPE, server.getId());
if (scope == null) {
scope = authz.getStoreFactory().getScopeStore().create(MAP_ROLE_COMPOSITE_SCOPE, server);
}
return scope;
}
private Resource createResource(RoleModel role) {
ResourceServer server = getResourceServer(role);
Resource resource = authz.getStoreFactory().getResourceStore().create(getRoleResourceName(role), server, server.getClientId());
resource.setType("Role");
Scope mapRoleScope = getMapRoleScope(server);
Policy mapRolePermission = Helper.addEmptyScopePermission(authz, server, getMapRolePermissionName(role), resource, mapRoleScope);
mapRolePermission.setDecisionStrategy(DecisionStrategy.AFFIRMATIVE);
Scope mapClientScope = getMapClientScope(server);
Policy mapClientScopePermission = Helper.addEmptyScopePermission(authz, server, getMapClientScopePermissionName(role), resource, mapClientScope);
mapClientScopePermission.setDecisionStrategy(DecisionStrategy.AFFIRMATIVE);
Scope mapCompositeScope = getMapCompositeScope(server);
Policy mapCompositePermission = Helper.addEmptyScopePermission(authz, server, getMapCompositePermissionName(role), resource, mapCompositeScope);
mapCompositePermission.setDecisionStrategy(DecisionStrategy.AFFIRMATIVE);
return resource;
}
private String getMapRolePermissionName(RoleModel role) {
return MAP_ROLE_SCOPE + ".permission." + role.getName();
}
private String getMapClientScopePermissionName(RoleModel role) {
return MAP_ROLE_CLIENT_SCOPE_SCOPE + ".permission." + role.getName();
}
private String getMapCompositePermissionName(RoleModel role) {
return MAP_ROLE_COMPOSITE_SCOPE + ".permission." + role.getName();
}
private ResourceServer getResourceServer(RoleModel role) {
ClientModel client = getRoleClient(role);
return root.findOrCreateResourceServer(client);
}
private static String getRoleResourceName(RoleModel role) {
return "role.resource." + role.getName();
}
}