Group count and PartialExport permission fixes

Closes https://github.com/keycloak/keycloak/issues/12171
This commit is contained in:
rmartinc 2023-10-26 18:24:43 +02:00 committed by Pedro Igor
parent c23e1e0e2b
commit 7deb4ca545
4 changed files with 25 additions and 29 deletions

View file

@ -135,3 +135,6 @@ Stream<GroupModel> getTopLevelGroupsStream(RealmModel realm,
Endpoint `GET {keycloak server}/realms/{realm}/groups/{group_id}/children` added as a way to get subgroups of specific groups that support pagination
= Partial export requires manage-realm permission
The endpoint `POST {keycloak server}/realms/{realm}/partial-export` and the corresponding action in the admin console now require `manage-realm` permission for execution instead of `view-realm`. This endpoint exports the realm configuration into a JSON file and the new permission is more appropriate. The parameters `exportGroupsAndRoles` and `exportClients`, which include the realm groups/roles and clients in the export respectively, continue managing the same permissions (`query-groups` and `view-clients`).

View file

@ -13,6 +13,7 @@ import { useEffect, useState } from "react";
import { Controller, useForm } from "react-hook-form";
import { useTranslation } from "react-i18next";
import { useNavigate } from "react-router-dom";
import { useAccess } from "../context/access/Access";
import { adminClient } from "../admin-client";
import { useAlerts } from "../components/alert/Alerts";
@ -71,6 +72,9 @@ const RealmSettingsHeader = ({
const [partialImportOpen, setPartialImportOpen] = useState(false);
const [partialExportOpen, setPartialExportOpen] = useState(false);
const { hasAccess } = useAccess();
const canManageRealm = hasAccess("manage-realm");
const [toggleDisableDialog, DisableConfirm] = useConfirmDialog({
titleKey: "disableConfirmTitle",
messageKey: "disableConfirmRealm",
@ -120,6 +124,7 @@ const RealmSettingsHeader = ({
<DropdownItem
key="import"
data-testid="openPartialImportModal"
isDisabled={!canManageRealm}
onClick={() => {
setPartialImportOpen(true);
}}
@ -129,16 +134,22 @@ const RealmSettingsHeader = ({
<DropdownItem
key="export"
data-testid="openPartialExportModal"
isDisabled={!canManageRealm}
onClick={() => setPartialExportOpen(true)}
>
{t("partialExport")}
</DropdownItem>,
<DropdownSeparator key="separator" />,
<DropdownItem key="delete" onClick={toggleDeleteDialog}>
<DropdownItem
key="delete"
isDisabled={!canManageRealm}
onClick={toggleDeleteDialog}
>
{t("delete")}
</DropdownItem>,
]}
isEnabled={value}
isReadOnly={!canManageRealm}
onToggle={(value) => {
if (!value) {
toggleDisableDialog();
@ -181,7 +192,7 @@ export const RealmSettingsTabs = ({
convertToFormValues(r, setValue);
};
useEffect(setupForm, []);
useEffect(setupForm, [setValue, realm]);
const save = async (r: RealmRepresentation) => {
r = convertFormValuesToObject(r);

View file

@ -1160,7 +1160,7 @@ public class RealmAdminResource {
@Operation( summary = "Partial export of existing realm into a JSON file.")
public Response partialExport(@QueryParam("exportGroupsAndRoles") Boolean exportGroupsAndRoles,
@QueryParam("exportClients") Boolean exportClients) {
auth.realm().requireViewRealm();
auth.realm().requireManageRealm();
boolean groupsAndRolesExported = exportGroupsAndRoles != null && exportGroupsAndRoles;
boolean clientsExported = exportClients != null && exportClients;

View file

@ -127,7 +127,7 @@ public class PermissionsTest extends AbstractKeycloakTest {
builder.user(UserBuilder.create()
.username("multi")
.role(Constants.REALM_MANAGEMENT_CLIENT_ID, AdminRoles.QUERY_GROUPS)
.role(Constants.REALM_MANAGEMENT_CLIENT_ID, AdminRoles.VIEW_REALM)
.role(Constants.REALM_MANAGEMENT_CLIENT_ID, AdminRoles.MANAGE_REALM)
.role(Constants.REALM_MANAGEMENT_CLIENT_ID, AdminRoles.VIEW_CLIENTS)
.addPassword("password"));
@ -1345,6 +1345,7 @@ public class PermissionsTest extends AbstractKeycloakTest {
realm.groups().groups();
}
}, Resource.USER, false);
invoke(realm -> realm.groups().count(), Resource.USER, false);
invoke(new InvocationWithResponse() {
public void invoke(RealmResource realm, AtomicReference<Response> response) {
GroupRepresentation group = new GroupRepresentation();
@ -1800,31 +1801,12 @@ public class PermissionsTest extends AbstractKeycloakTest {
// re-enable as part of https://github.com/keycloak/keycloak/issues/14291
ProfileAssume.assumeFeatureDisabled(Profile.Feature.MAP_STORAGE);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
realm.partialExport(false, false);
}
}, clients.get("view-realm"), true);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
realm.partialExport(true, true);
}
}, clients.get("multi"), true);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
realm.partialExport(true, false);
}
}, clients.get("view-realm"), false);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
realm.partialExport(false, true);
}
}, clients.get("view-realm"), false);
invoke(new Invocation() {
public void invoke(RealmResource realm) {
realm.partialExport(false, false);
}
}, clients.get("none"), false);
invoke(realm -> realm.partialExport(false, false), clients.get("view-realm"), false);
invoke(realm -> realm.partialExport(false, false), clients.get("manage-realm"), true);
invoke(realm -> realm.partialExport(true, false), clients.get("manage-realm"), false);
invoke(realm -> realm.partialExport(false, true), clients.get("manage-realm"), false);
invoke(realm -> realm.partialExport(true, true), clients.get("multi"), true);
invoke(realm -> realm.partialExport(false, false), clients.get("none"), false);
}
@Test