keycloak/services/src/main/java/org/keycloak/services/resources/admin/permissions/GroupPermissions.java

491 lines
17 KiB
Java

/*
* Copyright 2022 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.authorization.permission.ResourcePermission;
import org.keycloak.authorization.policy.evaluation.EvaluationContext;
import org.keycloak.authorization.store.PolicyStore;
import org.keycloak.authorization.store.ResourceStore;
import org.keycloak.common.Profile;
import org.keycloak.models.AdminRoles;
import org.keycloak.models.GroupModel;
import org.keycloak.models.RealmModel;
import org.keycloak.representations.idm.authorization.Permission;
import org.keycloak.services.ForbiddenException;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
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 String MANAGE_MEMBERSHIP_SCOPE = "manage-membership";
private static final String MANAGE_MEMBERS_SCOPE = "manage-members";
private static final String VIEW_MEMBERS_SCOPE = "view-members";
private static final String RESOURCE_NAME_PREFIX = "group.resource.";
private final AuthorizationProvider authz;
private final MgmtPermissions root;
private final ResourceStore resourceStore;
private final PolicyStore policyStore;
GroupPermissions(AuthorizationProvider authz, MgmtPermissions root) {
this.authz = authz;
this.root = root;
if (authz!=null) {
resourceStore = authz.getStoreFactory().getResourceStore();
policyStore = authz.getStoreFactory().getPolicyStore();
} else {
resourceStore = null;
policyStore = null;
}
}
private static String getGroupResourceName(GroupModel group) {
return RESOURCE_NAME_PREFIX + group.getId();
}
private static String getManagePermissionGroup(GroupModel group) {
return "manage.permission.group." + group.getId();
}
private static String getManageMembersPermissionGroup(GroupModel group) {
return "manage.members.permission.group." + group.getId();
}
private static String getManageMembershipPermissionGroup(GroupModel group) {
return "manage.membership.permission.group." + group.getId();
}
private static String getViewPermissionGroup(GroupModel group) {
return "view.permission.group." + group.getId();
}
private static String getViewMembersPermissionGroup(GroupModel group) {
return "view.members.permission.group." + group.getId();
}
private void initialize(GroupModel group) {
ResourceServer server = root.initializeRealmResourceServer();
if (server == null) return;
root.initializeRealmDefaultScopes();
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 = resourceStore.findByName(server, groupResourceName);
if (groupResource == null) {
groupResource = resourceStore.create(server, groupResourceName, server.getClientId());
Set<Scope> scopeset = new HashSet<>();
scopeset.add(manageScope);
scopeset.add(viewScope);
scopeset.add(viewMembersScope);
scopeset.add(manageMembershipScope);
scopeset.add(manageMembersScope);
groupResource.updateScopes(scopeset);
groupResource.setType("Group");
}
String managePermissionName = getManagePermissionGroup(group);
Policy managePermission = policyStore.findByName(server, managePermissionName);
if (managePermission == null) {
Helper.addEmptyScopePermission(authz, server, managePermissionName, groupResource, manageScope);
}
String viewPermissionName = getViewPermissionGroup(group);
Policy viewPermission = policyStore.findByName(server, viewPermissionName);
if (viewPermission == null) {
Helper.addEmptyScopePermission(authz, server, viewPermissionName, groupResource, viewScope);
}
String manageMembersPermissionName = getManageMembersPermissionGroup(group);
Policy manageMembersPermission = policyStore.findByName(server, manageMembersPermissionName);
if (manageMembersPermission == null) {
Helper.addEmptyScopePermission(authz, server, manageMembersPermissionName, groupResource, manageMembersScope);
}
String viewMembersPermissionName = getViewMembersPermissionGroup(group);
Policy viewMembersPermission = policyStore.findByName(server, viewMembersPermissionName);
if (viewMembersPermission == null) {
Helper.addEmptyScopePermission(authz, server, viewMembersPermissionName, groupResource, viewMembersScope);
}
String manageMembershipPermissionName = getManageMembershipPermissionGroup(group);
Policy manageMembershipPermission = policyStore.findByName(server, manageMembershipPermissionName);
if (manageMembershipPermission == null) {
Helper.addEmptyScopePermission(authz, server, manageMembershipPermissionName, groupResource, manageMembershipScope);
}
}
@Override
public boolean canList() {
return canView() || 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 resourceStore.findByName(server, getGroupResourceName(group)) != null;
}
@Override
public void setPermissionsEnabled(GroupModel group, boolean enable) {
if (enable) {
initialize(group);
} else {
deletePermissions(group);
}
}
@Override
public Policy viewMembersPermission(GroupModel group) {
ResourceServer server = root.realmResourceServer();
if (server == null) return null;
return policyStore.findByName(server, getViewMembersPermissionGroup(group));
}
@Override
public Policy manageMembersPermission(GroupModel group) {
ResourceServer server = root.realmResourceServer();
if (server == null) return null;
return policyStore.findByName(server, getManageMembersPermissionGroup(group));
}
@Override
public Policy manageMembershipPermission(GroupModel group) {
ResourceServer server = root.realmResourceServer();
if (server == null) return null;
return policyStore.findByName(server, getManageMembershipPermissionGroup(group));
}
@Override
public Policy viewPermission(GroupModel group) {
ResourceServer server = root.realmResourceServer();
if (server == null) return null;
return policyStore.findByName(server, getViewPermissionGroup(group));
}
@Override
public Policy managePermission(GroupModel group) {
ResourceServer server = root.realmResourceServer();
if (server == null) return null;
return policyStore.findByName(server, getManagePermissionGroup(group));
}
@Override
public Resource resource(GroupModel group) {
ResourceServer server = root.realmResourceServer();
if (server == null) return null;
Resource resource = resourceStore.findByName(server, getGroupResourceName(group));
if (resource == null) return null;
return resource;
}
@Override
public Map<String, String> getPermissions(GroupModel group) {
if (authz == null) return null;
initialize(group);
Map<String, String> scopes = new LinkedHashMap<>();
scopes.put(AdminPermissionManagement.VIEW_SCOPE, viewPermission(group).getId());
scopes.put(AdminPermissionManagement.MANAGE_SCOPE, managePermission(group).getId());
scopes.put(VIEW_MEMBERS_SCOPE, viewMembersPermission(group).getId());
scopes.put(MANAGE_MEMBERS_SCOPE, manageMembersPermission(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;
}
return hasPermission(group, MgmtPermissions.MANAGE_SCOPE);
}
@Override
public void requireManage(GroupModel group) {
if (!canManage(group)) {
throw new ForbiddenException();
}
}
@Override
public boolean canView(GroupModel group) {
if (canView() || canManage()) {
return true;
}
if (!root.isAdminSameRealm()) {
return false;
}
return hasPermission(group, MgmtPermissions.VIEW_SCOPE, MgmtPermissions.MANAGE_SCOPE);
}
@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 getGroupsWithViewPermission(GroupModel group) {
if (root.users().canView() || root.users().canManage()) {
return true;
}
if (!root.isAdminSameRealm()) {
return false;
}
ResourceServer server = root.realmResourceServer();
if (server == null) return false;
return hasPermission(group, VIEW_MEMBERS_SCOPE, MANAGE_MEMBERS_SCOPE);
}
@Override
public Set<String> getGroupsWithViewPermission() {
if (root.users().canView() || root.users().canManage()) return Collections.emptySet();
if (!root.isAdminSameRealm()) {
return Collections.emptySet();
}
ResourceServer server = root.realmResourceServer();
if (server == null) {
return Collections.emptySet();
}
Set<String> granted = new HashSet<>();
resourceStore.findByType(server, "Group", resource -> {
if (hasPermission(resource, null, VIEW_MEMBERS_SCOPE, MANAGE_MEMBERS_SCOPE)) {
granted.add(resource.getName().substring(RESOURCE_NAME_PREFIX.length()));
}
});
return granted;
}
@Override
public void requireViewMembers(GroupModel group) {
if (!getGroupsWithViewPermission(group)) {
throw new ForbiddenException();
}
}
@Override
public boolean canViewMembers(GroupModel group) {
if (root.users().canView()) return true;
if (!root.isAdminSameRealm()) {
return false;
}
ResourceServer server = root.realmResourceServer();
if (server == null) return false;
return hasPermission(group, VIEW_MEMBERS_SCOPE);
}
@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;
return hasPermission(group, MANAGE_MEMBERS_SCOPE);
}
@Override
public boolean canManageMembership(GroupModel group) {
if (canManage(group)) return true;
if (!root.isAdminSameRealm()) {
return false;
}
return hasPermission(group, MANAGE_MEMBERSHIP_SCOPE);
}
@Override
public void requireManageMembership(GroupModel group) {
if (!canManageMembership(group)) {
throw new ForbiddenException();
}
}
@Override
public void requireManageMembers(GroupModel group) {
if (!canManageMembers(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));
map.put("viewMembers", canViewMembers(group));
map.put("manageMembers", canManageMembers(group));
return map;
}
private boolean hasPermission(GroupModel group, String... scopes) {
return hasPermission(group, null, scopes);
}
private boolean hasPermission(GroupModel group, EvaluationContext context, String... scopes) {
ResourceServer server = root.realmResourceServer();
if (server == null) {
return false;
}
Resource resource = resourceStore.findByName(server, getGroupResourceName(group));
if (resource == null) {
return false;
}
return hasPermission(resource, context, scopes);
}
private boolean hasPermission(Resource resource, EvaluationContext context, String... scopes) {
ResourceServer server = root.realmResourceServer();
Collection<Permission> permissions;
if (context == null) {
permissions = root.evaluatePermission(new ResourcePermission(resource, resource.getScopes(), server), server);
} else {
permissions = root.evaluatePermission(new ResourcePermission(resource, resource.getScopes(), server), server, context);
}
List<String> expectedScopes = Arrays.asList(scopes);
for (Permission permission : permissions) {
for (String scope : permission.getScopes()) {
if (expectedScopes.contains(scope)) {
return true;
}
}
}
return false;
}
private Resource groupResource(GroupModel group) {
ResourceServer server = root.realmResourceServer();
if (server == null) return null;
String groupResourceName = getGroupResourceName(group);
return resourceStore.findByName(server, groupResourceName);
}
private void deletePermissions(GroupModel group) {
ResourceServer server = root.realmResourceServer();
if (server == null) return;
RealmModel realm = server.getRealm();
Policy managePermission = managePermission(group);
if (managePermission != null) {
policyStore.delete(realm, managePermission.getId());
}
Policy viewPermission = viewPermission(group);
if (viewPermission != null) {
policyStore.delete(realm, viewPermission.getId());
}
Policy manageMembersPermission = manageMembersPermission(group);
if (manageMembersPermission != null) {
policyStore.delete(realm, manageMembersPermission.getId());
}
Policy viewMembersPermission = viewMembersPermission(group);
if (viewMembersPermission != null) {
policyStore.delete(realm, viewMembersPermission.getId());
}
Policy manageMembershipPermission = manageMembershipPermission(group);
if (manageMembershipPermission != null) {
policyStore.delete(realm, manageMembershipPermission.getId());
}
Resource resource = groupResource(group);
if (resource != null) resourceStore.delete(realm, resource.getId());
}
}