From 1000a5d0402b1d1ea4e834dc5c8d48b12a884aed Mon Sep 17 00:00:00 2001 From: German Osin Date: Mon, 2 Jun 2025 13:30:02 +0200 Subject: [PATCH 1/2] Deprecated Connect.RESTART in favour of OPERATE --- .../ui/controller/KafkaConnectController.java | 8 ++------ .../java/io/kafbat/ui/model/rbac/Resource.java | 6 ++++++ .../ui/model/rbac/permission/ConnectAction.java | 8 ++++---- .../kafbat/ui/model/rbac/AccessContextTest.java | 15 +++++++++++++++ .../src/main/resources/swagger/kafbat-ui-api.yaml | 1 + .../Connect/Details/Actions/Actions.tsx | 12 ++++++------ .../Connect/Details/Tasks/ActionsCellTasks.tsx | 2 +- .../src/components/Connect/List/ActionsCell.tsx | 6 +++--- 8 files changed, 38 insertions(+), 20 deletions(-) diff --git a/api/src/main/java/io/kafbat/ui/controller/KafkaConnectController.java b/api/src/main/java/io/kafbat/ui/controller/KafkaConnectController.java index 4c5dc4d84..945d77f92 100644 --- a/api/src/main/java/io/kafbat/ui/controller/KafkaConnectController.java +++ b/api/src/main/java/io/kafbat/ui/controller/KafkaConnectController.java @@ -191,11 +191,7 @@ public Mono> updateConnectorState(String clusterName, Strin ConnectorActionDTO action, ServerWebExchange exchange) { ConnectAction[] connectActions; - if (RESTART_ACTIONS.contains(action)) { - connectActions = new ConnectAction[] {ConnectAction.VIEW, ConnectAction.RESTART}; - } else { - connectActions = new ConnectAction[] {ConnectAction.VIEW, ConnectAction.EDIT}; - } + connectActions = new ConnectAction[] {ConnectAction.VIEW, ConnectAction.OPERATE}; var context = AccessContext.builder() .cluster(clusterName) @@ -237,7 +233,7 @@ public Mono> restartConnectorTask(String clusterName, Strin var context = AccessContext.builder() .cluster(clusterName) - .connectActions(connectName, ConnectAction.VIEW, ConnectAction.RESTART) + .connectActions(connectName, ConnectAction.VIEW, ConnectAction.OPERATE) .operationName("restartConnectorTask") .operationParams(Map.of(CONNECTOR_NAME, connectorName)) .build(); diff --git a/api/src/main/java/io/kafbat/ui/model/rbac/Resource.java b/api/src/main/java/io/kafbat/ui/model/rbac/Resource.java index 600c9c4e6..51b76b52f 100644 --- a/api/src/main/java/io/kafbat/ui/model/rbac/Resource.java +++ b/api/src/main/java/io/kafbat/ui/model/rbac/Resource.java @@ -13,6 +13,7 @@ import io.kafbat.ui.model.rbac.permission.TopicAction; import jakarta.annotation.Nullable; import java.util.List; +import java.util.Map; import java.util.stream.Stream; import org.apache.commons.lang3.EnumUtils; @@ -38,6 +39,10 @@ public enum Resource { CLIENT_QUOTAS(ClientQuotaAction.values()); + private static final Map DEPRECATED_ACTIONS = Map.of( + ConnectAction.RESTART, ConnectAction.OPERATE + ); + private final List actions; Resource(PermissibleAction[] actions) { @@ -58,6 +63,7 @@ public List parseActionsWithDependantsUnnest(List act .map(toParse -> actions.stream() .filter(a -> toParse.equalsIgnoreCase(a.name())) .findFirst() + .map(a -> DEPRECATED_ACTIONS.getOrDefault(a, a)) .orElseThrow(() -> new IllegalArgumentException( "'%s' actions not applicable for resource %s".formatted(toParse, name()))) ) diff --git a/api/src/main/java/io/kafbat/ui/model/rbac/permission/ConnectAction.java b/api/src/main/java/io/kafbat/ui/model/rbac/permission/ConnectAction.java index a357245bc..306078168 100644 --- a/api/src/main/java/io/kafbat/ui/model/rbac/permission/ConnectAction.java +++ b/api/src/main/java/io/kafbat/ui/model/rbac/permission/ConnectAction.java @@ -9,10 +9,10 @@ public enum ConnectAction implements PermissibleAction { VIEW, EDIT(VIEW), CREATE(VIEW), - RESTART(VIEW), + OPERATE(VIEW), DELETE(VIEW), - RESET_OFFSETS(VIEW) - + RESET_OFFSETS(VIEW), + RESTART(VIEW), // DEPREACTED ; private final ConnectAction[] dependantActions; @@ -21,7 +21,7 @@ public enum ConnectAction implements PermissibleAction { this.dependantActions = dependantActions; } - public static final Set ALTER_ACTIONS = Set.of(CREATE, EDIT, DELETE, RESTART, RESET_OFFSETS); + public static final Set ALTER_ACTIONS = Set.of(CREATE, EDIT, DELETE, OPERATE, RESET_OFFSETS); @Nullable public static ConnectAction fromString(String name) { diff --git a/api/src/test/java/io/kafbat/ui/model/rbac/AccessContextTest.java b/api/src/test/java/io/kafbat/ui/model/rbac/AccessContextTest.java index 161a34075..4a39c2200 100644 --- a/api/src/test/java/io/kafbat/ui/model/rbac/AccessContextTest.java +++ b/api/src/test/java/io/kafbat/ui/model/rbac/AccessContextTest.java @@ -9,6 +9,7 @@ import io.kafbat.ui.model.rbac.AccessContext.ResourceAccess; import io.kafbat.ui.model.rbac.AccessContext.SingleResourceAccess; import io.kafbat.ui.model.rbac.permission.ClusterConfigAction; +import io.kafbat.ui.model.rbac.permission.ConnectAction; import io.kafbat.ui.model.rbac.permission.PermissibleAction; import io.kafbat.ui.model.rbac.permission.TopicAction; import jakarta.annotation.Nullable; @@ -98,6 +99,20 @@ void deniesAccessForResourceWithoutNameIfUserHasAllNeededPermissions() { assertThat(allowed).isFalse(); } + @Test + void shouldNotContainDeprecatedActions() { + SingleResourceAccess sra = + new SingleResourceAccess(Resource.CONNECT, List.of(ConnectAction.OPERATE)); + + var allowed = sra.isAccessible( + List.of( + permission(Resource.CONNECT, null, ConnectAction.RESTART) + ) + ); + + assertThat(allowed).isTrue(); + } + private Permission permission(Resource res, @Nullable String namePattern, PermissibleAction... actions) { Permission p = new Permission(); p.setResource(res.name()); diff --git a/contract/src/main/resources/swagger/kafbat-ui-api.yaml b/contract/src/main/resources/swagger/kafbat-ui-api.yaml index 158330062..7b4c65744 100644 --- a/contract/src/main/resources/swagger/kafbat-ui-api.yaml +++ b/contract/src/main/resources/swagger/kafbat-ui-api.yaml @@ -3947,6 +3947,7 @@ components: - MESSAGES_READ - MESSAGES_PRODUCE - MESSAGES_DELETE + - OPERATE - RESTART ResourceType: diff --git a/frontend/src/components/Connect/Details/Actions/Actions.tsx b/frontend/src/components/Connect/Details/Actions/Actions.tsx index 6909b4617..f1f2ff13d 100644 --- a/frontend/src/components/Connect/Details/Actions/Actions.tsx +++ b/frontend/src/components/Connect/Details/Actions/Actions.tsx @@ -91,7 +91,7 @@ const Actions: React.FC = () => { onClick={pauseConnectorHandler} permission={{ resource: ResourceType.CONNECT, - action: Action.EDIT, + action: Action.OPERATE, value: routerProps.connectName, }} > @@ -103,7 +103,7 @@ const Actions: React.FC = () => { onClick={stopConnectorHandler} permission={{ resource: ResourceType.CONNECT, - action: Action.EDIT, + action: Action.OPERATE, value: routerProps.connectName, }} > @@ -116,7 +116,7 @@ const Actions: React.FC = () => { onClick={resumeConnectorHandler} permission={{ resource: ResourceType.CONNECT, - action: Action.EDIT, + action: Action.OPERATE, value: routerProps.connectName, }} > @@ -127,7 +127,7 @@ const Actions: React.FC = () => { onClick={restartConnectorHandler} permission={{ resource: ResourceType.CONNECT, - action: Action.RESTART, + action: Action.OPERATE, value: routerProps.connectName, }} > @@ -137,7 +137,7 @@ const Actions: React.FC = () => { onClick={restartAllTasksHandler} permission={{ resource: ResourceType.CONNECT, - action: Action.RESTART, + action: Action.OPERATE, value: routerProps.connectName, }} > @@ -147,7 +147,7 @@ const Actions: React.FC = () => { onClick={restartFailedTasksHandler} permission={{ resource: ResourceType.CONNECT, - action: Action.RESTART, + action: Action.OPERATE, value: routerProps.connectName, }} > diff --git a/frontend/src/components/Connect/Details/Tasks/ActionsCellTasks.tsx b/frontend/src/components/Connect/Details/Tasks/ActionsCellTasks.tsx index 053c9c086..8055456a4 100644 --- a/frontend/src/components/Connect/Details/Tasks/ActionsCellTasks.tsx +++ b/frontend/src/components/Connect/Details/Tasks/ActionsCellTasks.tsx @@ -25,7 +25,7 @@ const ActionsCellTasks: React.FC> = ({ row }) => { confirm="Are you sure you want to restart the task?" permission={{ resource: ResourceType.CONNECT, - action: Action.RESTART, + action: Action.OPERATE, value: routerProps.connectName, }} > diff --git a/frontend/src/components/Connect/List/ActionsCell.tsx b/frontend/src/components/Connect/List/ActionsCell.tsx index 4ad50c00e..e41479fd2 100644 --- a/frontend/src/components/Connect/List/ActionsCell.tsx +++ b/frontend/src/components/Connect/List/ActionsCell.tsx @@ -126,7 +126,7 @@ const ActionsCell: React.FC> = ({ disabled={isMutating} permission={{ resource: ResourceType.CONNECT, - action: Action.RESTART, + action: Action.OPERATE, value: connect, }} > @@ -137,7 +137,7 @@ const ActionsCell: React.FC> = ({ disabled={isMutating} permission={{ resource: ResourceType.CONNECT, - action: Action.RESTART, + action: Action.OPERATE, value: connect, }} > @@ -148,7 +148,7 @@ const ActionsCell: React.FC> = ({ disabled={isMutating} permission={{ resource: ResourceType.CONNECT, - action: Action.RESTART, + action: Action.OPERATE, value: connect, }} > From 8a667350cdf576e7ecad6dee34bb340253695c39 Mon Sep 17 00:00:00 2001 From: German Osin Date: Tue, 3 Jun 2025 08:46:24 +0200 Subject: [PATCH 2/2] Moved to aliases --- .../io/kafbat/ui/model/rbac/Resource.java | 44 ++++++++++++------- .../model/rbac/permission/ConnectAction.java | 4 +- .../ui/model/rbac/AccessContextTest.java | 12 +++-- .../kafbat/ui/model/rbac/PermissionTest.java | 2 +- 4 files changed, 40 insertions(+), 22 deletions(-) diff --git a/api/src/main/java/io/kafbat/ui/model/rbac/Resource.java b/api/src/main/java/io/kafbat/ui/model/rbac/Resource.java index 51b76b52f..691bc9e94 100644 --- a/api/src/main/java/io/kafbat/ui/model/rbac/Resource.java +++ b/api/src/main/java/io/kafbat/ui/model/rbac/Resource.java @@ -12,8 +12,13 @@ import io.kafbat.ui.model.rbac.permission.SchemaAction; import io.kafbat.ui.model.rbac.permission.TopicAction; import jakarta.annotation.Nullable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.List; import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; import java.util.stream.Stream; import org.apache.commons.lang3.EnumUtils; @@ -29,7 +34,7 @@ public enum Resource { SCHEMA(SchemaAction.values()), - CONNECT(ConnectAction.values()), + CONNECT(ConnectAction.values(), ConnectAction.ALIASES), KSQL(KsqlAction.values()), @@ -39,18 +44,27 @@ public enum Resource { CLIENT_QUOTAS(ClientQuotaAction.values()); - private static final Map DEPRECATED_ACTIONS = Map.of( - ConnectAction.RESTART, ConnectAction.OPERATE - ); - private final List actions; + private final Map actions; + private final Map aliases; + + Resource(PermissibleAction[] actions, Map aliases) { + this.actions = Arrays.stream(actions) + .collect( + Collectors.toMap( + a -> a.name().toLowerCase(), + a -> a + ) + ); + this.aliases = aliases; + } Resource(PermissibleAction[] actions) { - this.actions = List.of(actions); + this(actions, Map.of()); } public List allActions() { - return actions; + return new ArrayList<>(actions.values()); } @Nullable @@ -59,18 +73,14 @@ public static Resource fromString(String name) { } public List parseActionsWithDependantsUnnest(List actionsToParse) { - return actionsToParse.stream() - .map(toParse -> actions.stream() - .filter(a -> toParse.equalsIgnoreCase(a.name())) - .findFirst() - .map(a -> DEPRECATED_ACTIONS.getOrDefault(a, a)) + return actionsToParse.stream().map(toParse -> + Optional.ofNullable(actions.get(toParse.toLowerCase())) + .or(() -> Optional.ofNullable(aliases.get(toParse.toLowerCase()))) .orElseThrow(() -> new IllegalArgumentException( "'%s' actions not applicable for resource %s".formatted(toParse, name()))) - ) - // unnesting all dependant actions - .flatMap(a -> Stream.concat(Stream.of(a), a.unnestAllDependants())) - .distinct() - .toList(); + ).flatMap(a -> + Stream.concat(Stream.of(a), a.unnestAllDependants()) + ).distinct().toList(); } } diff --git a/api/src/main/java/io/kafbat/ui/model/rbac/permission/ConnectAction.java b/api/src/main/java/io/kafbat/ui/model/rbac/permission/ConnectAction.java index 306078168..81fc8d382 100644 --- a/api/src/main/java/io/kafbat/ui/model/rbac/permission/ConnectAction.java +++ b/api/src/main/java/io/kafbat/ui/model/rbac/permission/ConnectAction.java @@ -1,5 +1,6 @@ package io.kafbat.ui.model.rbac.permission; +import java.util.Map; import java.util.Set; import org.apache.commons.lang3.EnumUtils; import org.jetbrains.annotations.Nullable; @@ -12,7 +13,6 @@ public enum ConnectAction implements PermissibleAction { OPERATE(VIEW), DELETE(VIEW), RESET_OFFSETS(VIEW), - RESTART(VIEW), // DEPREACTED ; private final ConnectAction[] dependantActions; @@ -23,6 +23,8 @@ public enum ConnectAction implements PermissibleAction { public static final Set ALTER_ACTIONS = Set.of(CREATE, EDIT, DELETE, OPERATE, RESET_OFFSETS); + public static final Map ALIASES = Map.of("restart", OPERATE); + @Nullable public static ConnectAction fromString(String name) { return EnumUtils.getEnum(ConnectAction.class, name); diff --git a/api/src/test/java/io/kafbat/ui/model/rbac/AccessContextTest.java b/api/src/test/java/io/kafbat/ui/model/rbac/AccessContextTest.java index 4a39c2200..b1417f2c7 100644 --- a/api/src/test/java/io/kafbat/ui/model/rbac/AccessContextTest.java +++ b/api/src/test/java/io/kafbat/ui/model/rbac/AccessContextTest.java @@ -100,13 +100,13 @@ void deniesAccessForResourceWithoutNameIfUserHasAllNeededPermissions() { } @Test - void shouldNotContainDeprecatedActions() { + void shouldMapActionAliases() { SingleResourceAccess sra = new SingleResourceAccess(Resource.CONNECT, List.of(ConnectAction.OPERATE)); var allowed = sra.isAccessible( List.of( - permission(Resource.CONNECT, null, ConnectAction.RESTART) + permission(Resource.CONNECT, null, List.of("restart")) ) ); @@ -114,9 +114,15 @@ void shouldNotContainDeprecatedActions() { } private Permission permission(Resource res, @Nullable String namePattern, PermissibleAction... actions) { + return permission( + res, namePattern, Stream.of(actions).map(PermissibleAction::name).toList() + ); + } + + private Permission permission(Resource res, @Nullable String namePattern, List actions) { Permission p = new Permission(); p.setResource(res.name()); - p.setActions(Stream.of(actions).map(PermissibleAction::name).toList()); + p.setActions(actions); p.setValue(namePattern); p.validate(); p.transform(); diff --git a/api/src/test/java/io/kafbat/ui/model/rbac/PermissionTest.java b/api/src/test/java/io/kafbat/ui/model/rbac/PermissionTest.java index 3d2fd72be..3aed48d18 100644 --- a/api/src/test/java/io/kafbat/ui/model/rbac/PermissionTest.java +++ b/api/src/test/java/io/kafbat/ui/model/rbac/PermissionTest.java @@ -34,7 +34,7 @@ void transformSetsFullActionsListIfAllActionPassed() { p.transform(); assertThat(p.getParsedActions()) - .isEqualTo(List.of(TopicAction.values())); + .containsExactlyInAnyOrder(TopicAction.values()); } @Test