This is an automated email from the ASF dual-hosted git repository.
starocean999 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/master by this push:
new 7f933f50462 [Enhancement] (nereids)implement revokeRoleCommand in
nereids (#50328)
7f933f50462 is described below
commit 7f933f5046249194631899702c1c7590cb0bda57
Author: csding <[email protected]>
AuthorDate: Tue May 20 14:33:17 2025 +0800
[Enhancement] (nereids)implement revokeRoleCommand in nereids (#50328)
Issue Number: close https://github.com/apache/doris/issues/42819
---
.../antlr4/org/apache/doris/nereids/DorisParser.g4 | 2 +-
.../org/apache/doris/mysql/privilege/Auth.java | 6 ++
.../doris/nereids/parser/LogicalPlanBuilder.java | 11 +++
.../apache/doris/nereids/trees/plans/PlanType.java | 3 +-
.../trees/plans/commands/RevokeRoleCommand.java | 93 ++++++++++++++++++++++
.../trees/plans/visitor/CommandVisitor.java | 5 ++
.../plans/commands/RevokeRoleCommandTest.java | 74 +++++++++++++++++
.../ddl/grant/test_revoke_role_nereids.groovy | 47 +++++++++++
8 files changed, 239 insertions(+), 2 deletions(-)
diff --git a/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4
b/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4
index 4c1c7f97f18..baf0c9dc904 100644
--- a/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4
+++ b/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4
@@ -634,6 +634,7 @@ supportedGrantRevokeStatement
(RESOURCE | CLUSTER | COMPUTE GROUP | STAGE | STORAGE VAULT | WORKLOAD
GROUP)
identifierOrTextOrAsterisk TO (userIdentify | ROLE identifierOrText)
#grantResourcePrivilege
| GRANT roles+=identifierOrText (COMMA roles+=identifierOrText)* TO
userIdentify #grantRole
+ | REVOKE roles+=identifierOrText (COMMA roles+=identifierOrText)* FROM
userIdentify #revokeRole
;
unsupportedGrantRevokeStatement
@@ -642,7 +643,6 @@ unsupportedGrantRevokeStatement
| REVOKE privilegeList ON
(RESOURCE | CLUSTER | COMPUTE GROUP | STAGE | STORAGE VAULT | WORKLOAD
GROUP)
identifierOrTextOrAsterisk FROM (userIdentify | ROLE identifierOrText)
#revokeResourcePrivilege
- | REVOKE roles+=identifierOrText (COMMA roles+=identifierOrText)* FROM
userIdentify #revokeRole
;
privilege
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/Auth.java
b/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/Auth.java
index abf8eb1a2f7..b1dc222db2c 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/Auth.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/Auth.java
@@ -65,6 +65,7 @@ import org.apache.doris.mysql.authenticate.ldap.LdapUserInfo;
import
org.apache.doris.nereids.trees.plans.commands.GrantResourcePrivilegeCommand;
import org.apache.doris.nereids.trees.plans.commands.GrantRoleCommand;
import
org.apache.doris.nereids.trees.plans.commands.GrantTablePrivilegeCommand;
+import org.apache.doris.nereids.trees.plans.commands.RevokeRoleCommand;
import org.apache.doris.nereids.trees.plans.commands.info.AlterUserInfo;
import org.apache.doris.nereids.trees.plans.commands.info.CreateUserInfo;
import org.apache.doris.persist.AlterUserOperationLog;
@@ -892,6 +893,11 @@ public class Auth implements Writable {
}
}
+ // revoke role
+ public void revokeRole(RevokeRoleCommand command) throws DdlException {
+ revokeInternal(command.getUserIdentity(), command.getRoles(), false);
+ }
+
public void replayRevoke(PrivInfo info) {
try {
PrivBitSet privs = info.getPrivs();
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
index c3ba347cca9..976b104c172 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
@@ -655,6 +655,7 @@ import
org.apache.doris.nereids.trees.plans.commands.RefreshMTMVCommand;
import org.apache.doris.nereids.trees.plans.commands.ReplayCommand;
import org.apache.doris.nereids.trees.plans.commands.ResumeJobCommand;
import org.apache.doris.nereids.trees.plans.commands.ResumeMTMVCommand;
+import org.apache.doris.nereids.trees.plans.commands.RevokeRoleCommand;
import
org.apache.doris.nereids.trees.plans.commands.SetDefaultStorageVaultCommand;
import org.apache.doris.nereids.trees.plans.commands.SetOptionsCommand;
import org.apache.doris.nereids.trees.plans.commands.SetTransactionCommand;
@@ -7210,6 +7211,16 @@ public class LogicalPlanBuilder extends
DorisParserBaseVisitor<Object> {
.collect(ImmutableList.toImmutableList());
}
+ @Override
+ public LogicalPlan visitRevokeRole(DorisParser.RevokeRoleContext ctx) {
+ UserIdentity userIdentity = visitUserIdentify(ctx.userIdentify());
+ List<String> roles = ctx.roles.stream()
+ .map(this::visitIdentifierOrText)
+ .collect(ImmutableList.toImmutableList());
+
+ return new RevokeRoleCommand(userIdentity, roles);
+ }
+
public LogicalPlan visitDropAnalyzeJob(DorisParser.DropAnalyzeJobContext
ctx) {
long jobId = Long.parseLong(ctx.INTEGER_VALUE().getText());
return new DropAnalyzeJobCommand(jobId);
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/PlanType.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/PlanType.java
index 5b0c16cb4c2..39291234282 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/PlanType.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/PlanType.java
@@ -380,5 +380,6 @@ public enum PlanType {
TRUNCATE_TABLE_COMMAND,
GRANT_ROLE_COMMAND,
GRANT_RESOURCE_PRIVILEGE_COMMAND,
- GRANT_TABLE_PRIVILEGE_COMMAND
+ GRANT_TABLE_PRIVILEGE_COMMAND,
+ REVOKE_ROLE_COMMAND
}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/RevokeRoleCommand.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/RevokeRoleCommand.java
new file mode 100644
index 00000000000..79648c226f7
--- /dev/null
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/RevokeRoleCommand.java
@@ -0,0 +1,93 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you 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.apache.doris.nereids.trees.plans.commands;
+
+import org.apache.doris.analysis.StmtType;
+import org.apache.doris.analysis.UserIdentity;
+import org.apache.doris.catalog.Env;
+import org.apache.doris.common.AnalysisException;
+import org.apache.doris.common.Config;
+import org.apache.doris.common.ErrorReport;
+import org.apache.doris.common.FeNameFormat;
+import org.apache.doris.nereids.trees.plans.PlanType;
+import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor;
+import org.apache.doris.qe.ConnectContext;
+import org.apache.doris.qe.StmtExecutor;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+/**
+ * REVOKE role [, role] FROM user_identity
+ */
+public class RevokeRoleCommand extends Command implements ForwardWithSync {
+ private final UserIdentity userIdentity;
+ private final List<String> roles;
+
+ public RevokeRoleCommand(UserIdentity userIdentity, List<String> roles) {
+ super(PlanType.REVOKE_ROLE_COMMAND);
+ this.userIdentity = Objects.requireNonNull(userIdentity, "userIdentity
is null");
+ this.roles = Objects.requireNonNull(roles, "roles is null");
+ }
+
+ @Override
+ public void run(ConnectContext ctx, StmtExecutor executor) throws
Exception {
+ validate();
+ Env.getCurrentEnv().getAuth().revokeRole(this);
+ }
+
+ /**
+ * validate
+ */
+ public void validate() throws AnalysisException {
+ if (Config.access_controller_type.equalsIgnoreCase("ranger-doris")) {
+ throw new AnalysisException("Revoke is prohibited when Ranger is
enabled.");
+ }
+ userIdentity.analyze();
+
+ for (int i = 0; i < roles.size(); i++) {
+ String originalRoleName = roles.get(i);
+ FeNameFormat.checkRoleName(originalRoleName, true /* can be admin
*/, "Can not revoke role");
+ }
+
+ GrantRoleCommand.checkRolePrivileges();
+ if
(roles.stream().map(String::toLowerCase).collect(Collectors.toList()).contains("admin")
+ && userIdentity.isAdminUser()) {
+ ErrorReport.reportAnalysisException("Unsupported operation");
+ }
+ }
+
+ @Override
+ public <R, C> R accept(PlanVisitor<R, C> visitor, C context) {
+ return visitor.visitRevokeRoleCommand(this, context);
+ }
+
+ @Override
+ public StmtType stmtType() {
+ return StmtType.REVOKE;
+ }
+
+ public UserIdentity getUserIdentity() {
+ return userIdentity;
+ }
+
+ public List<String> getRoles() {
+ return roles;
+ }
+}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/CommandVisitor.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/CommandVisitor.java
index 066366c70be..a16fe837659 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/CommandVisitor.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/CommandVisitor.java
@@ -123,6 +123,7 @@ import
org.apache.doris.nereids.trees.plans.commands.RefreshMTMVCommand;
import org.apache.doris.nereids.trees.plans.commands.ReplayCommand;
import org.apache.doris.nereids.trees.plans.commands.ResumeJobCommand;
import org.apache.doris.nereids.trees.plans.commands.ResumeMTMVCommand;
+import org.apache.doris.nereids.trees.plans.commands.RevokeRoleCommand;
import
org.apache.doris.nereids.trees.plans.commands.SetDefaultStorageVaultCommand;
import org.apache.doris.nereids.trees.plans.commands.SetOptionsCommand;
import org.apache.doris.nereids.trees.plans.commands.SetTransactionCommand;
@@ -1137,4 +1138,8 @@ public interface CommandVisitor<R, C> {
C context) {
return visitCommand(grantResourcePrivilegeCommand, context);
}
+
+ default R visitRevokeRoleCommand(RevokeRoleCommand revokeRoleCommand, C
context) {
+ return visitCommand(revokeRoleCommand, context);
+ }
}
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/commands/RevokeRoleCommandTest.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/commands/RevokeRoleCommandTest.java
new file mode 100644
index 00000000000..cd288f4f5fb
--- /dev/null
+++
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/commands/RevokeRoleCommandTest.java
@@ -0,0 +1,74 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you 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.apache.doris.nereids.trees.plans.commands;
+
+import org.apache.doris.analysis.UserIdentity;
+import org.apache.doris.common.AnalysisException;
+import org.apache.doris.common.DdlException;
+import org.apache.doris.nereids.parser.NereidsParser;
+import org.apache.doris.nereids.trees.plans.logical.LogicalPlan;
+import org.apache.doris.utframe.TestWithFeService;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.util.Arrays;
+
+public class RevokeRoleCommandTest extends TestWithFeService {
+ @Test
+ public void testValidate() throws DdlException {
+ GrantRoleCommand grantRoleCommand = new GrantRoleCommand(new
UserIdentity("test", "%"), Arrays.asList("test"));
+ Assertions.assertDoesNotThrow(() -> grantRoleCommand.validate());
+
+ String grantRoleSql = "GRANT 'role1','role2' TO 'jack'@'%'";
+ String createUserSql = "CREATE USER 'jack'";
+ String role1 = "CREATE ROLE role1";
+ String role2 = "CREATE ROLE role2";
+
+ NereidsParser nereidsParser = new NereidsParser();
+
+ LogicalPlan logicalPlan = nereidsParser.parseSingle(createUserSql);
+ Assertions.assertTrue(logicalPlan instanceof CreateUserCommand);
+ Assertions.assertDoesNotThrow(() -> ((CreateUserCommand)
logicalPlan).run(connectContext, null));
+
+ LogicalPlan logicalPlan1 = nereidsParser.parseSingle(role1);
+ Assertions.assertTrue(logicalPlan1 instanceof CreateRoleCommand);
+ Assertions.assertDoesNotThrow(() -> ((CreateRoleCommand)
logicalPlan1).run(connectContext, null));
+
+ LogicalPlan logicalPlan2 = nereidsParser.parseSingle(role2);
+ Assertions.assertTrue(logicalPlan2 instanceof CreateRoleCommand);
+ Assertions.assertDoesNotThrow(() -> ((CreateRoleCommand)
logicalPlan2).run(connectContext, null));
+
+ LogicalPlan logicalPlan3 = nereidsParser.parseSingle(grantRoleSql);
+ Assertions.assertTrue(logicalPlan3 instanceof GrantRoleCommand);
+ Assertions.assertDoesNotThrow(() -> ((GrantRoleCommand)
logicalPlan3).run(connectContext, null));
+
+ String revkeSql = "REVOKE 'role1','role2' FROM 'jack'@'%';";
+ LogicalPlan plan = nereidsParser.parseSingle(revkeSql);
+ Assertions.assertTrue(plan instanceof RevokeRoleCommand);
+ Assertions.assertDoesNotThrow(() -> ((RevokeRoleCommand)
plan).run(connectContext, null));
+ }
+
+ @Test
+ public void testUserFail() {
+ Assertions.assertThrows(NullPointerException.class, () -> new
RevokeRoleCommand(new UserIdentity("", "%"), null));
+
+ RevokeRoleCommand grantRoleCommand = new RevokeRoleCommand(new
UserIdentity("", "%"), Arrays.asList("test"));
+ Assertions.assertThrows(AnalysisException.class, () ->
grantRoleCommand.validate());
+ }
+}
diff --git
a/regression-test/suites/nereids_p0/ddl/grant/test_revoke_role_nereids.groovy
b/regression-test/suites/nereids_p0/ddl/grant/test_revoke_role_nereids.groovy
new file mode 100644
index 00000000000..7816444f2d6
--- /dev/null
+++
b/regression-test/suites/nereids_p0/ddl/grant/test_revoke_role_nereids.groovy
@@ -0,0 +1,47 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you 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.
+
+suite("test_revoke_role_nereids)") {
+
+ String user1 = 'test_revoke_role_nereids_user1'
+ String user2 = 'test_revoke_role_nereids_user2'
+ String role1 = 'test_revoke_role_nereids_role1'
+ String role2 = 'test_revoke_role_nereids_role2'
+ String pwd = 'C123_567p'
+
+ try_sql("DROP USER ${user1}")
+ try_sql("DROP USER ${user2}")
+ try_sql("DROP role ${role1}")
+ try_sql("DROP role ${role2}")
+ sql """CREATE USER '${user1}' IDENTIFIED BY '${pwd}'"""
+ sql """CREATE USER '${user2}' IDENTIFIED BY '${pwd}'"""
+ checkNereidsExecute("grant select_priv on regression_test to ${user1};")
+ checkNereidsExecute("grant select_priv on regression_test to ${user2};")
+
+ sql """CREATE ROLE ${role1}"""
+ sql """CREATE ROLE ${role2}"""
+
+ sql """ADMIN SET FRONTEND CONFIG ('experimental_enable_workload_group' =
'true');"""
+ sql """set experimental_enable_pipeline_engine = true;"""
+
+ // role
+ checkNereidsExecute("grant '${role1}', '${role2}' to '${user1}';")
+ checkNereidsExecute("revoke '${role1}', '${role2}' from '${user1}'")
+
+ checkNereidsExecute("grant '${role1}', '${role2}' to '${user2}';")
+ checkNereidsExecute("revoke '${role1}', '${role2}' from '${user2}'")
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]