Co-authored-by: Harbor Liu <460660596@qq.com>
This commit is contained in:
parent
3d2a0d5301
commit
e5b9ac8c92
|
|
@ -14,23 +14,31 @@
|
|||
|
||||
package com.starrocks.authentication;
|
||||
|
||||
import com.starrocks.authorization.AccessDeniedException;
|
||||
import com.starrocks.authorization.AuthorizationMgr;
|
||||
import com.starrocks.authorization.DefaultAuthorizationProvider;
|
||||
import com.starrocks.authorization.GrantType;
|
||||
import com.starrocks.authorization.PrivilegeType;
|
||||
import com.starrocks.common.Config;
|
||||
import com.starrocks.common.DdlException;
|
||||
import com.starrocks.common.ErrorReportException;
|
||||
import com.starrocks.mysql.MysqlPassword;
|
||||
import com.starrocks.persist.EditLog;
|
||||
import com.starrocks.qe.ConnectContext;
|
||||
import com.starrocks.qe.ExecuteAsExecutor;
|
||||
import com.starrocks.server.GlobalStateMgr;
|
||||
import com.starrocks.sql.analyzer.Analyzer;
|
||||
import com.starrocks.sql.analyzer.Authorizer;
|
||||
import com.starrocks.sql.ast.CreateRoleStmt;
|
||||
import com.starrocks.sql.ast.CreateUserStmt;
|
||||
import com.starrocks.sql.ast.ExecuteAsStmt;
|
||||
import com.starrocks.sql.ast.GrantPrivilegeStmt;
|
||||
import com.starrocks.sql.ast.GrantRoleStmt;
|
||||
import com.starrocks.sql.ast.RevokePrivilegeStmt;
|
||||
import com.starrocks.sql.ast.RevokeRoleStmt;
|
||||
import com.starrocks.sql.ast.UserIdentity;
|
||||
import com.starrocks.sql.parser.NodePosition;
|
||||
import com.starrocks.utframe.UtFrameUtils;
|
||||
import mockit.Mock;
|
||||
import mockit.MockUp;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
|
|
@ -191,4 +199,128 @@ public class ExecuteAsExecutorTest {
|
|||
Assertions.assertEquals(Set.of(), context.getGroups());
|
||||
Assertions.assertEquals(Set.of(), context.getCurrentRoleIds());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testImpersonatePermissionWithRoleGroupUser() throws Exception {
|
||||
ConnectContext context = new ConnectContext();
|
||||
|
||||
// Create roles
|
||||
authorizationMgr.createRole(new CreateRoleStmt(List.of("impersonate_role"), true, ""));
|
||||
|
||||
// Create users
|
||||
CreateUserStmt createUserStmt =
|
||||
new CreateUserStmt(new UserIdentity("admin_user", "%"), true, null, List.of(), Map.of(), NodePosition.ZERO);
|
||||
Analyzer.analyze(createUserStmt, context);
|
||||
authenticationMgr.createUser(createUserStmt);
|
||||
|
||||
createUserStmt =
|
||||
new CreateUserStmt(new UserIdentity("target_user", "%"), true, null, List.of(), Map.of(), NodePosition.ZERO);
|
||||
Analyzer.analyze(createUserStmt, context);
|
||||
authenticationMgr.createUser(createUserStmt);
|
||||
|
||||
createUserStmt =
|
||||
new CreateUserStmt(new UserIdentity("group_user", "%"), true, null, List.of(), Map.of(), NodePosition.ZERO);
|
||||
Analyzer.analyze(createUserStmt, context);
|
||||
authenticationMgr.createUser(createUserStmt);
|
||||
|
||||
// Grant impersonate permission to role
|
||||
GrantPrivilegeStmt grantStmt = (GrantPrivilegeStmt) UtFrameUtils.parseStmtWithNewParser(
|
||||
"GRANT IMPERSONATE ON USER target_user TO ROLE impersonate_role",
|
||||
new ConnectContext());
|
||||
authorizationMgr.grant(grantStmt);
|
||||
|
||||
// Grant role to external group
|
||||
GrantRoleStmt grantRoleStmt =
|
||||
new GrantRoleStmt(List.of("impersonate_role"), "test_group", GrantType.GROUP, NodePosition.ZERO);
|
||||
authorizationMgr.grantRole(grantRoleStmt);
|
||||
|
||||
// Set up LDAP group mapping for group_user to belong to test_group
|
||||
LDAPGroupProvider ldapGroupProvider = (LDAPGroupProvider) authenticationMgr.getGroupProvider("ldap_group_provider");
|
||||
Map<String, Set<String>> groups = new HashMap<>();
|
||||
groups.put("group_user", Set.of("test_group"));
|
||||
ldapGroupProvider.setUserToGroupCache(groups);
|
||||
|
||||
// Test 1: User with impersonate permission through role-group can execute as target user
|
||||
AuthenticationHandler.authenticate(context, "group_user", "%", MysqlPassword.EMPTY_PASSWORD);
|
||||
|
||||
// Verify user has the role through group membership
|
||||
long roleId = authorizationMgr.getRoleIdByNameAllowNull("impersonate_role");
|
||||
Assertions.assertEquals(Set.of("test_group"), context.getGroups());
|
||||
Assertions.assertEquals(Set.of(roleId), context.getCurrentRoleIds());
|
||||
|
||||
// Verify impersonate permission check passes
|
||||
UserIdentity targetUser = new UserIdentity("target_user", "%");
|
||||
try {
|
||||
Authorizer.checkUserAction(context, targetUser, PrivilegeType.IMPERSONATE);
|
||||
// If no exception is thrown, permission check passed
|
||||
} catch (AccessDeniedException e) {
|
||||
Assertions.fail("User should have impersonate permission through role-group membership");
|
||||
}
|
||||
|
||||
// Execute as target user should succeed
|
||||
ExecuteAsStmt executeAsStmt = new ExecuteAsStmt(new UserIdentity("target_user", "%"), false);
|
||||
ExecuteAsExecutor.execute(executeAsStmt, context);
|
||||
Assertions.assertEquals("target_user", context.getCurrentUserIdentity().getUser());
|
||||
|
||||
// Test 2: Revoke impersonate permission from role
|
||||
RevokePrivilegeStmt revokeStmt = (RevokePrivilegeStmt) UtFrameUtils.parseStmtWithNewParser(
|
||||
"REVOKE IMPERSONATE ON USER target_user FROM ROLE impersonate_role",
|
||||
new ConnectContext());
|
||||
authorizationMgr.revoke(revokeStmt);
|
||||
|
||||
// Re-authenticate to refresh context
|
||||
AuthenticationHandler.authenticate(context, "group_user", "%", MysqlPassword.EMPTY_PASSWORD);
|
||||
|
||||
// Verify user still has the role through group membership
|
||||
Assertions.assertEquals(Set.of("test_group"), context.getGroups());
|
||||
Assertions.assertEquals(Set.of(roleId), context.getCurrentRoleIds());
|
||||
|
||||
// Verify impersonate permission check now fails
|
||||
Assertions.assertThrows(AccessDeniedException.class,
|
||||
() -> Authorizer.checkUserAction(context, targetUser, PrivilegeType.IMPERSONATE));
|
||||
|
||||
// Execute as target user should fail
|
||||
Assertions.assertThrows(ErrorReportException.class, () -> Authorizer.check(executeAsStmt, context));
|
||||
|
||||
// Test 3: Grant impersonate permission back to role
|
||||
grantStmt = (GrantPrivilegeStmt) UtFrameUtils.parseStmtWithNewParser(
|
||||
"GRANT IMPERSONATE ON USER target_user TO ROLE impersonate_role",
|
||||
new ConnectContext());
|
||||
authorizationMgr.grant(grantStmt);
|
||||
|
||||
// Re-authenticate to refresh context
|
||||
AuthenticationHandler.authenticate(context, "group_user", "%", MysqlPassword.EMPTY_PASSWORD);
|
||||
|
||||
// Verify impersonate permission check passes again
|
||||
try {
|
||||
Authorizer.checkUserAction(context, targetUser, PrivilegeType.IMPERSONATE);
|
||||
// If no exception is thrown, permission check passed
|
||||
} catch (AccessDeniedException e) {
|
||||
Assertions.fail("User should have impersonate permission after re-granting to role");
|
||||
}
|
||||
|
||||
// Execute as target user should succeed again
|
||||
ExecuteAsExecutor.execute(executeAsStmt, context);
|
||||
Assertions.assertEquals("target_user", context.getCurrentUserIdentity().getUser());
|
||||
|
||||
// Test 4: Revoke role from group
|
||||
RevokeRoleStmt
|
||||
revokeRoleStmt =
|
||||
new RevokeRoleStmt(List.of("impersonate_role"), "test_group", GrantType.GROUP, NodePosition.ZERO);
|
||||
authorizationMgr.revokeRole(revokeRoleStmt);
|
||||
|
||||
// Re-authenticate to refresh context
|
||||
AuthenticationHandler.authenticate(context, "group_user", "%", MysqlPassword.EMPTY_PASSWORD);
|
||||
|
||||
// Verify user no longer has the role
|
||||
Assertions.assertEquals(Set.of("test_group"), context.getGroups());
|
||||
Assertions.assertEquals(Set.of(), context.getCurrentRoleIds());
|
||||
|
||||
// Verify impersonate permission check fails
|
||||
Assertions.assertThrows(AccessDeniedException.class,
|
||||
() -> Authorizer.checkUserAction(context, targetUser, PrivilegeType.IMPERSONATE));
|
||||
|
||||
// Execute as target user should fail
|
||||
Assertions.assertThrows(ErrorReportException.class, () -> Authorizer.check(executeAsStmt, context));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import com.starrocks.qe.ConnectContext;
|
|||
import com.starrocks.qe.DDLStmtExecutor;
|
||||
import com.starrocks.qe.ShowExecutor;
|
||||
import com.starrocks.qe.ShowResultSet;
|
||||
import com.starrocks.qe.SqlModeHelper;
|
||||
import com.starrocks.server.GlobalStateMgr;
|
||||
import com.starrocks.sql.analyzer.Analyzer;
|
||||
import com.starrocks.sql.analyzer.Authorizer;
|
||||
|
|
@ -349,4 +350,202 @@ public class GrantRoleToGroupTest {
|
|||
Assertions.assertThrows(AccessDeniedException.class, () -> Authorizer.checkSystemAction(ctx, PrivilegeType.GRANT));
|
||||
Assertions.assertThrows(ErrorReportException.class, () -> Authorizer.check(stmt3, ctx));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShowGrantsForExternalGroup() throws Exception {
|
||||
EditLog editLog = spy(new EditLog(null));
|
||||
doNothing().when(editLog).logEdit(anyShort(), any());
|
||||
GlobalStateMgr.getCurrentState().setEditLog(editLog);
|
||||
|
||||
ConnectContext ctx = new ConnectContext();
|
||||
ctx.setGlobalStateMgr(GlobalStateMgr.getCurrentState());
|
||||
|
||||
AuthorizationMgr authorizationMgr = new AuthorizationMgr(new DefaultAuthorizationProvider());
|
||||
GlobalStateMgr.getCurrentState().setAuthorizationMgr(authorizationMgr);
|
||||
GlobalStateMgr.getCurrentState().setAuthenticationMgr(new AuthenticationMgr());
|
||||
|
||||
// Create roles
|
||||
for (int i = 1; i <= 4; i++) {
|
||||
String sql = "create role r" + i;
|
||||
StatementBase stmt = UtFrameUtils.parseStmtWithNewParser(sql, ctx);
|
||||
DDLStmtExecutor.execute(stmt, ctx);
|
||||
}
|
||||
|
||||
// Test 1: Grant single role to external group and verify show grants
|
||||
GrantRoleStmt grantRoleStmt = new GrantRoleStmt(List.of("r1"), "test_group_1", GrantType.GROUP, NodePosition.ZERO);
|
||||
Analyzer.analyze(grantRoleStmt, ctx);
|
||||
authorizationMgr.grantRole(grantRoleStmt);
|
||||
|
||||
ShowGrantsStmt stmt = new ShowGrantsStmt("test_group_1", GrantType.GROUP, NodePosition.ZERO);
|
||||
Analyzer.analyze(stmt, ctx);
|
||||
ShowResultSet showResultSet = ShowExecutor.execute(stmt, ctx);
|
||||
Assertions.assertEquals("[[test_group_1, null, GRANT 'r1' TO EXTERNAL GROUP test_group_1]]",
|
||||
showResultSet.getResultRows().toString());
|
||||
|
||||
// Test 2: Grant multiple roles to external group and verify show grants
|
||||
grantRoleStmt = new GrantRoleStmt(List.of("r2", "r3", "r4"), "test_group_2", GrantType.GROUP, NodePosition.ZERO);
|
||||
Analyzer.analyze(grantRoleStmt, ctx);
|
||||
authorizationMgr.grantRole(grantRoleStmt);
|
||||
|
||||
stmt = new ShowGrantsStmt("test_group_2", GrantType.GROUP, NodePosition.ZERO);
|
||||
Analyzer.analyze(stmt, ctx);
|
||||
showResultSet = ShowExecutor.execute(stmt, ctx);
|
||||
Assertions.assertEquals("[[test_group_2, null, GRANT 'r2', 'r3', 'r4' TO EXTERNAL GROUP test_group_2]]",
|
||||
showResultSet.getResultRows().toString());
|
||||
|
||||
// Test 3: Grant additional roles to existing group and verify show grants
|
||||
grantRoleStmt = new GrantRoleStmt(List.of("r3", "r4"), "test_group_1", GrantType.GROUP, NodePosition.ZERO);
|
||||
Analyzer.analyze(grantRoleStmt, ctx);
|
||||
authorizationMgr.grantRole(grantRoleStmt);
|
||||
|
||||
stmt = new ShowGrantsStmt("test_group_1", GrantType.GROUP, NodePosition.ZERO);
|
||||
Analyzer.analyze(stmt, ctx);
|
||||
showResultSet = ShowExecutor.execute(stmt, ctx);
|
||||
Assertions.assertEquals("[[test_group_1, null, GRANT 'r1', 'r3', 'r4' TO EXTERNAL GROUP test_group_1]]",
|
||||
showResultSet.getResultRows().toString());
|
||||
|
||||
// Test 4: Revoke some roles and verify show grants reflects the changes
|
||||
RevokeRoleStmt revokeRoleStmt = new RevokeRoleStmt(List.of("r3"), "test_group_1", GrantType.GROUP, NodePosition.ZERO);
|
||||
Analyzer.analyze(revokeRoleStmt, ctx);
|
||||
authorizationMgr.revokeRole(revokeRoleStmt);
|
||||
|
||||
stmt = new ShowGrantsStmt("test_group_1", GrantType.GROUP, NodePosition.ZERO);
|
||||
Analyzer.analyze(stmt, ctx);
|
||||
showResultSet = ShowExecutor.execute(stmt, ctx);
|
||||
Assertions.assertEquals("[[test_group_1, null, GRANT 'r1', 'r4' TO EXTERNAL GROUP test_group_1]]",
|
||||
showResultSet.getResultRows().toString());
|
||||
|
||||
// Test 5: Revoke all roles and verify show grants shows empty result
|
||||
revokeRoleStmt = new RevokeRoleStmt(List.of("r1", "r4"), "test_group_1", GrantType.GROUP, NodePosition.ZERO);
|
||||
Analyzer.analyze(revokeRoleStmt, ctx);
|
||||
authorizationMgr.revokeRole(revokeRoleStmt);
|
||||
|
||||
stmt = new ShowGrantsStmt("test_group_1", GrantType.GROUP, NodePosition.ZERO);
|
||||
Analyzer.analyze(stmt, ctx);
|
||||
showResultSet = ShowExecutor.execute(stmt, ctx);
|
||||
Assertions.assertEquals("[]", showResultSet.getResultRows().toString());
|
||||
|
||||
// Test 6: Verify that test_group_2 still has its roles (isolation test)
|
||||
stmt = new ShowGrantsStmt("test_group_2", GrantType.GROUP, NodePosition.ZERO);
|
||||
Analyzer.analyze(stmt, ctx);
|
||||
showResultSet = ShowExecutor.execute(stmt, ctx);
|
||||
Assertions.assertEquals("[[test_group_2, null, GRANT 'r2', 'r3', 'r4' TO EXTERNAL GROUP test_group_2]]",
|
||||
showResultSet.getResultRows().toString());
|
||||
|
||||
// Test 7: Test non-existent group shows empty result
|
||||
stmt = new ShowGrantsStmt("non_existent_group", GrantType.GROUP, NodePosition.ZERO);
|
||||
Analyzer.analyze(stmt, ctx);
|
||||
showResultSet = ShowExecutor.execute(stmt, ctx);
|
||||
Assertions.assertEquals("[]", showResultSet.getResultRows().toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGrantAndRevokeExternalGroup() throws Exception {
|
||||
EditLog editLog = spy(new EditLog(null));
|
||||
doNothing().when(editLog).logEdit(anyShort(), any());
|
||||
GlobalStateMgr.getCurrentState().setEditLog(editLog);
|
||||
|
||||
ConnectContext ctx = new ConnectContext();
|
||||
ctx.setGlobalStateMgr(GlobalStateMgr.getCurrentState());
|
||||
|
||||
AuthorizationMgr authorizationMgr = new AuthorizationMgr(new DefaultAuthorizationProvider());
|
||||
GlobalStateMgr.getCurrentState().setAuthorizationMgr(authorizationMgr);
|
||||
GlobalStateMgr.getCurrentState().setAuthenticationMgr(new AuthenticationMgr());
|
||||
|
||||
// Create roles
|
||||
for (int i = 1; i <= 3; i++) {
|
||||
String sql = "create role r" + i;
|
||||
StatementBase stmt = UtFrameUtils.parseStmtWithNewParser(sql, ctx);
|
||||
DDLStmtExecutor.execute(stmt, ctx);
|
||||
}
|
||||
|
||||
Long r1Id = authorizationMgr.getRoleIdByNameAllowNull("r1");
|
||||
Long r2Id = authorizationMgr.getRoleIdByNameAllowNull("r2");
|
||||
Long r3Id = authorizationMgr.getRoleIdByNameAllowNull("r3");
|
||||
|
||||
// Test 1: Grant multiple roles to external group
|
||||
GrantRoleStmt grantRoleStmt =
|
||||
new GrantRoleStmt(List.of("r1", "r2", "r3"), "external_group_1", GrantType.GROUP, NodePosition.ZERO);
|
||||
Analyzer.analyze(grantRoleStmt, ctx);
|
||||
authorizationMgr.grantRole(grantRoleStmt);
|
||||
|
||||
// Verify roles are granted
|
||||
Set<Long> roleIds = authorizationMgr.getRoleIdListByGroup("external_group_1");
|
||||
Assertions.assertEquals(3, roleIds.size());
|
||||
Assertions.assertTrue(roleIds.contains(r1Id));
|
||||
Assertions.assertTrue(roleIds.contains(r2Id));
|
||||
Assertions.assertTrue(roleIds.contains(r3Id));
|
||||
|
||||
// Test 2: Revoke one role from external group
|
||||
RevokeRoleStmt revokeRoleStmt = new RevokeRoleStmt(List.of("r2"), "external_group_1", GrantType.GROUP, NodePosition.ZERO);
|
||||
Analyzer.analyze(revokeRoleStmt, ctx);
|
||||
authorizationMgr.revokeRole(revokeRoleStmt);
|
||||
|
||||
// Verify revocation takes effect immediately
|
||||
roleIds = authorizationMgr.getRoleIdListByGroup("external_group_1");
|
||||
Assertions.assertEquals(2, roleIds.size());
|
||||
Assertions.assertTrue(roleIds.contains(r1Id));
|
||||
Assertions.assertFalse(roleIds.contains(r2Id)); // r2 should be revoked
|
||||
Assertions.assertTrue(roleIds.contains(r3Id));
|
||||
|
||||
// Test 3: Revoke multiple roles at once
|
||||
revokeRoleStmt = new RevokeRoleStmt(List.of("r1", "r3"), "external_group_1", GrantType.GROUP, NodePosition.ZERO);
|
||||
Analyzer.analyze(revokeRoleStmt, ctx);
|
||||
authorizationMgr.revokeRole(revokeRoleStmt);
|
||||
|
||||
// Verify all roles are revoked
|
||||
roleIds = authorizationMgr.getRoleIdListByGroup("external_group_1");
|
||||
Assertions.assertEquals(0, roleIds.size());
|
||||
|
||||
// Test 4: Grant role to another external group and verify isolation
|
||||
grantRoleStmt = new GrantRoleStmt(List.of("r1", "r2"), "external_group_2", GrantType.GROUP, NodePosition.ZERO);
|
||||
Analyzer.analyze(grantRoleStmt, ctx);
|
||||
authorizationMgr.grantRole(grantRoleStmt);
|
||||
|
||||
// Verify external_group_1 is still empty
|
||||
roleIds = authorizationMgr.getRoleIdListByGroup("external_group_1");
|
||||
Assertions.assertEquals(0, roleIds.size());
|
||||
|
||||
// Verify external_group_2 has the roles
|
||||
roleIds = authorizationMgr.getRoleIdListByGroup("external_group_2");
|
||||
Assertions.assertEquals(2, roleIds.size());
|
||||
Assertions.assertTrue(roleIds.contains(r1Id));
|
||||
Assertions.assertTrue(roleIds.contains(r2Id));
|
||||
|
||||
// Test 5: Revoke from external_group_2 and verify effect
|
||||
revokeRoleStmt = new RevokeRoleStmt(List.of("r1"), "external_group_2", GrantType.GROUP, NodePosition.ZERO);
|
||||
Analyzer.analyze(revokeRoleStmt, ctx);
|
||||
authorizationMgr.revokeRole(revokeRoleStmt);
|
||||
|
||||
roleIds = authorizationMgr.getRoleIdListByGroup("external_group_2");
|
||||
Assertions.assertEquals(1, roleIds.size());
|
||||
Assertions.assertFalse(roleIds.contains(r1Id)); // r1 should be revoked
|
||||
Assertions.assertTrue(roleIds.contains(r2Id)); // r2 should still be there
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGrantRoleStmtParser() {
|
||||
String sql = "grant r1 to external group g1";
|
||||
StatementBase stmt = SqlParser.parseSingleStatement(sql, SqlModeHelper.MODE_DEFAULT);
|
||||
Assertions.assertInstanceOf(GrantRoleStmt.class, stmt);
|
||||
GrantRoleStmt grantRoleStmt = (GrantRoleStmt) stmt;
|
||||
Assertions.assertEquals(List.of("r1"), grantRoleStmt.getGranteeRole());
|
||||
Assertions.assertEquals("g1", grantRoleStmt.getRoleOrGroup());
|
||||
Assertions.assertEquals(GrantType.GROUP, grantRoleStmt.getGrantType());
|
||||
|
||||
sql = "revoke r1 from external group g1";
|
||||
stmt = SqlParser.parseSingleStatement(sql, SqlModeHelper.MODE_DEFAULT);
|
||||
Assertions.assertInstanceOf(RevokeRoleStmt.class, stmt);
|
||||
RevokeRoleStmt revokeRoleStmt = (RevokeRoleStmt) stmt;
|
||||
Assertions.assertEquals(List.of("r1"), revokeRoleStmt.getGranteeRole());
|
||||
Assertions.assertEquals("g1", revokeRoleStmt.getRoleOrGroup());
|
||||
Assertions.assertEquals(GrantType.GROUP, revokeRoleStmt.getGrantType());
|
||||
|
||||
sql = "show grants for external group g1";
|
||||
stmt = SqlParser.parseSingleStatement(sql, SqlModeHelper.MODE_DEFAULT);
|
||||
Assertions.assertInstanceOf(ShowGrantsStmt.class, stmt);
|
||||
ShowGrantsStmt showGrantsStmt = (ShowGrantsStmt) stmt;
|
||||
Assertions.assertEquals("g1", showGrantsStmt.getGroupOrRole());
|
||||
Assertions.assertEquals(GrantType.GROUP, showGrantsStmt.getGrantType());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue