Skip to content

Commit

Permalink
[enhance](auth)when assigning permissions, the current user must have…
Browse files Browse the repository at this point in the history
… corresponding permissions (#32825)

When authorizing other users or roles, not only need `grant_priv`,  also need the priv want to grant
for example :
The user executes the following SQL
```
grant select_priv,load_priv on db1.table1 to u1;
```
he need have grant_priv, select_priv and load_priv on db1.table1

```
grant usage_priv on resource r1 to u1;
```
he need to have grant_priv and usage_priv on u1
  • Loading branch information
zddr authored Mar 29, 2024
1 parent d25bfe4 commit f0dd450
Show file tree
Hide file tree
Showing 9 changed files with 314 additions and 113 deletions.
198 changes: 96 additions & 102 deletions fe/fe-core/src/main/java/org/apache/doris/analysis/GrantStmt.java

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -157,12 +157,12 @@ public void analyze(Analyzer analyzer) throws AnalysisException {

// Revoke operation obey the same rule as Grant operation. reuse the same method
if (tblPattern != null) {
GrantStmt.checkTablePrivileges(privileges, role, tblPattern, colPrivileges);
GrantStmt.checkTablePrivileges(privileges, tblPattern, colPrivileges);
} else if (resourcePattern != null) {
privileges = PrivBitSet.convertResourcePrivToCloudPriv(resourcePattern, privileges);
GrantStmt.checkResourcePrivileges(privileges, role, resourcePattern);
GrantStmt.checkResourcePrivileges(privileges, resourcePattern);
} else if (workloadGroupPattern != null) {
GrantStmt.checkWorkloadGroupPrivileges(privileges, role, workloadGroupPattern);
GrantStmt.checkWorkloadGroupPrivileges(privileges, workloadGroupPattern);
} else if (roles != null) {
GrantStmt.checkRolePrivileges();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ public enum ErrorCode {
+ "(current value: %d)"),
ERR_SPECIFIC_ACCESS_DENIED_ERROR(1227, new byte[]{'4', '2', '0', '0', '0'}, "Access denied; you need (at least "
+ "one of) the %s privilege(s) for this operation"),
ERR_SPECIFIC_ALL_ACCESS_DENIED_ERROR(1227, new byte[] {'4', '2', '0', '0', '0'}, "Access denied; you need all "
+ " %s privilege(s) for this operation"),
ERR_LOCAL_VARIABLE(1228, new byte[]{'H', 'Y', '0', '0', '0'}, "Variable '%s' is a SESSION variable and can't be "
+ "used with SET GLOBAL"),
ERR_GLOBAL_VARIABLE(1229, new byte[]{'H', 'Y', '0', '0', '0'}, "Variable '%s' is a GLOBAL variable and should be "
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,12 @@ public enum Privilege {
SHOW_VIEW_PRIV,
};

public static final Privilege[] notBelongToTablePrivileges = {
USAGE_PRIV,
CLUSTER_USAGE_PRIV,
STAGE_USAGE_PRIV,
};

public static final Map<Privilege, String> privInDorisToMysql =
ImmutableMap.<Privilege, String>builder() // No NODE_PRIV and ADMIN_PRIV in the mysql
.put(SELECT_PRIV, "SELECT")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -365,10 +365,6 @@ public void testSwitchCommand() throws Exception {
SwitchStmt switchHive = (SwitchStmt) parseAndAnalyzeStmt("switch hive;", user2Ctx);
env.changeCatalog(user2Ctx, switchHive.getCatalogName());
Assert.assertEquals(user2Ctx.getDefaultCatalog(), "hive");
// user2 can grant select_priv to tpch.customer
GrantStmt user2GrantHiveTable = (GrantStmt) parseAndAnalyzeStmt(
"grant select_priv on tpch.customer to 'user2'@'%';", user2Ctx);
auth.grant(user2GrantHiveTable);

showCatalogSql = "SHOW CATALOGS";
showStmt = (ShowCatalogStmt) parseAndAnalyzeStmt(showCatalogSql);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1957,13 +1957,15 @@ public void testResource() throws UserException {
TablePattern tablePattern = new TablePattern("db1", "*");
GrantStmt grantStmt2 = new GrantStmt(userIdentity, null, tablePattern, usagePrivileges);
ExceptionChecker.expectThrowsWithMsg(AnalysisException.class,
"Can not grant/revoke USAGE_PRIV to/from database or table", () -> grantStmt2.analyze(analyzer));
"Can not grant/revoke Usage_priv to/from any other users or roles",
() -> grantStmt2.analyze(analyzer));

// 3. grant resource prov to role on db.table
tablePattern = new TablePattern("db1", "*");
GrantStmt grantStmt3 = new GrantStmt(userIdentity, "test_role", tablePattern, usagePrivileges);
ExceptionChecker.expectThrowsWithMsg(AnalysisException.class,
"Can not grant/revoke USAGE_PRIV to/from database or table", () -> grantStmt3.analyze(analyzer));
"Can not grant/revoke Usage_priv to/from any other users or roles",
() -> grantStmt3.analyze(analyzer));

// 4.drop user
dropUser(userIdentity);
Expand Down Expand Up @@ -2259,13 +2261,14 @@ public void testWorkloadGroupPriv() throws UserException {
TablePattern tablePattern = new TablePattern("db1", "*");
GrantStmt grantStmt2 = new GrantStmt(userIdentity, null, tablePattern, usagePrivileges);
ExceptionChecker.expectThrowsWithMsg(AnalysisException.class,
"Can not grant/revoke USAGE_PRIV to/from database or table", () -> grantStmt2.analyze(analyzer));
"Can not grant/revoke Usage_priv to/from any other users or roles", () -> grantStmt2.analyze(analyzer));

// 3. grant workload group prov to role on db.table
tablePattern = new TablePattern("db1", "*");
GrantStmt grantStmt3 = new GrantStmt(userIdentity, "test_role", tablePattern, usagePrivileges);
ExceptionChecker.expectThrowsWithMsg(AnalysisException.class,
"Can not grant/revoke USAGE_PRIV to/from database or table", () -> grantStmt3.analyze(analyzer));
"Can not grant/revoke Usage_priv to/from any other users or roles", () -> grantStmt3.analyze(analyzer));

// 4.drop user
dropUser(userIdentity);

Expand Down
86 changes: 86 additions & 0 deletions regression-test/suites/account_p0/test_grant_priv.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// 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.

import org.junit.Assert;

suite("test_grant_priv") {
def user1 = 'test_grant_priv_user1'
def user2 = 'test_grant_priv_user2'
def role1 = 'test_grant_priv_role1'
def pwd = '123456'
def dbName = 'test_grant_priv_db'
def tokens = context.config.jdbcUrl.split('/')
def url=tokens[0] + "//" + tokens[2] + "/" + dbName + "?"

sql """drop user if exists ${user1}"""
sql """drop user if exists ${user2}"""
sql """drop role if exists ${role1}"""
sql """DROP DATABASE IF EXISTS ${dbName}"""

sql """CREATE DATABASE ${dbName}"""
sql """CREATE ROLE ${role1}"""
sql """CREATE USER '${user1}' IDENTIFIED BY '${pwd}'"""
sql """CREATE USER '${user2}' IDENTIFIED BY '${pwd}'"""

// test only have select_priv, can not grant to other user
sql """grant select_priv on ${dbName}.* to ${user1}"""
connect(user=user1, password="${pwd}", url=url) {
try {
sql """grant select_priv on ${dbName}.* to ${user2}"""
Assert.fail("can not grant to other user");
} catch (Exception e) {
log.info(e.getMessage())
}
}

// test both have select_priv and grant_priv , can grant to other user
sql """grant grant_priv on ${dbName}.* to ${user1}"""
connect(user=user1, password="${pwd}", url=url) {
try {
sql """grant select_priv on ${dbName}.* to ${user2}"""
} catch (Exception e) {
Assert.fail(e.getMessage());
}
// test have grant_priv,but not have load_priv, can not grant load_priv to other user
try {
sql """grant load_priv on ${dbName}.* to ${user2}"""
Assert.fail("can not grant to other user");
} catch (Exception e) {
log.info(e.getMessage())
}
// test have grant_priv, can not grant role to other user
try {
sql """grant '${role1}' to ${user2}"""
Assert.fail("can not grant to other user");
} catch (Exception e) {
log.info(e.getMessage())
}
}

// test have global grant_priv, can grant role to other user
sql """grant grant_priv on *.* to ${user1}"""
try {
sql """grant '${role1}' to ${user2}"""
} catch (Exception e) {
Assert.fail(e.getMessage());
}

sql """drop user if exists ${user1}"""
sql """drop user if exists ${user2}"""
sql """drop role if exists ${role1}"""
sql """DROP DATABASE IF EXISTS ${dbName}"""
}
57 changes: 57 additions & 0 deletions regression-test/suites/account_p0/test_grant_priv_resource.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// 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.

import org.junit.Assert;

suite("test_grant_priv_resource") {
def user1 = 'test_grant_priv_resource_user1'
def user2 = 'test_grant_priv_resource_user2'
def pwd = '123456'
def resource1 = 'test_grant_priv_resource_resource1'
def tokens = context.config.jdbcUrl.split('/')
def url=tokens[0] + "//" + tokens[2] + "/" + "information_schema" + "?"

sql """drop user if exists ${user1}"""
sql """drop user if exists ${user2}"""

sql """CREATE USER '${user1}' IDENTIFIED BY '${pwd}'"""
sql """CREATE USER '${user2}' IDENTIFIED BY '${pwd}'"""

// test only have USAGE_PRIV, can not grant to other user
sql """grant USAGE_PRIV on RESOURCE ${resource1} to ${user1}"""
connect(user=user1, password="${pwd}", url=url) {
try {
sql """grant USAGE_PRIV on RESOURCE ${resource1} to ${user2}"""
Assert.fail("can not grant to other user");
} catch (Exception e) {
log.info(e.getMessage())
}
}

// test both have USAGE_PRIV and grant_priv , can grant to other user
sql """grant grant_priv on RESOURCE * to ${user1}"""
connect(user=user1, password="${pwd}", url=url) {
try {
sql """grant USAGE_PRIV on RESOURCE ${resource1} to ${user2}"""
} catch (Exception e) {
Assert.fail(e.getMessage());
}
}

sql """drop user if exists ${user1}"""
sql """drop user if exists ${user2}"""
}
57 changes: 57 additions & 0 deletions regression-test/suites/account_p0/test_grant_priv_workload.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// 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.

import org.junit.Assert;

suite("test_grant_priv_workload") {
def user1 = 'test_grant_priv_workload_user1'
def user2 = 'test_grant_priv_workload_user2'
def pwd = '123456'
def workload1 = 'test_grant_priv_workload_workload1'
def tokens = context.config.jdbcUrl.split('/')
def url=tokens[0] + "//" + tokens[2] + "/" + "information_schema" + "?"

sql """drop user if exists ${user1}"""
sql """drop user if exists ${user2}"""

sql """CREATE USER '${user1}' IDENTIFIED BY '${pwd}'"""
sql """CREATE USER '${user2}' IDENTIFIED BY '${pwd}'"""

// test only have USAGE_PRIV, can not grant to other user
sql """grant USAGE_PRIV on WORKLOAD GROUP ${workload1} to ${user1}"""
connect(user=user1, password="${pwd}", url=url) {
try {
sql """grant USAGE_PRIV on WORKLOAD GROUP ${workload1} to ${user2}"""
Assert.fail("can not grant to other user");
} catch (Exception e) {
log.info(e.getMessage())
}
}

// test both have USAGE_PRIV and grant_priv , can grant to other user
sql """grant grant_priv on WORKLOAD GROUP ${workload1} to ${user1}"""
connect(user=user1, password="${pwd}", url=url) {
try {
sql """grant USAGE_PRIV on WORKLOAD GROUP ${workload1} to ${user2}"""
} catch (Exception e) {
Assert.fail(e.getMessage());
}
}

sql """drop user if exists ${user1}"""
sql """drop user if exists ${user2}"""
}

0 comments on commit f0dd450

Please sign in to comment.