[fix](auth) Forbid grant USAGE_PRIV to database.table (#11234)

This commit is contained in:
Mingyu Chen
2022-07-27 16:51:15 +08:00
committed by GitHub
parent 4ea2c04676
commit be2ac6aa59
5 changed files with 68 additions and 51 deletions

View File

@ -56,7 +56,7 @@ public class CreateRoleStmt extends DdlStmt {
// check if current user has GRANT priv on GLOBAL level.
if (!Env.getCurrentEnv().getAuth().checkGlobalPriv(ConnectContext.get(), PrivPredicate.GRANT)) {
ErrorReport.reportAnalysisException(ErrorCode.ERR_SPECIFIC_ACCESS_DENIED_ERROR, "CREATE USER");
ErrorReport.reportAnalysisException(ErrorCode.ERR_SPECIFIC_ACCESS_DENIED_ERROR, "CREATE ROLE");
}
}

View File

@ -121,9 +121,9 @@ public class GrantStmt extends DdlStmt {
}
if (tblPattern != null) {
checkPrivileges(analyzer, privileges, role, tblPattern);
checkTablePrivileges(privileges, role, tblPattern);
} else {
checkPrivileges(analyzer, privileges, role, resourcePattern);
checkResourcePrivileges(privileges, role, resourcePattern);
}
}
@ -136,86 +136,91 @@ public class GrantStmt extends DdlStmt {
* 5.1 User should has GLOBAL level GRANT_PRIV
* 5.2 or user has DATABASE/TABLE level GRANT_PRIV if grant/revoke to/from certain database or table.
* 5.3 or user should has 'resource' GRANT_PRIV if grant/revoke to/from certain 'resource'
* 6. Can not grant USAGE_PRIV to database or table
*
* @param analyzer
* @param privileges
* @param role
* @param tblPattern
* @throws AnalysisException
*/
public static void checkPrivileges(Analyzer analyzer, List<PaloPrivilege> privileges,
String role, TablePattern tblPattern) throws AnalysisException {
public static void checkTablePrivileges(List<PaloPrivilege> privileges, String role, TablePattern tblPattern)
throws AnalysisException {
// Rule 1
if (tblPattern.getPrivLevel() != PrivLevel.GLOBAL && (privileges.contains(PaloPrivilege.ADMIN_PRIV)
|| privileges.contains(PaloPrivilege.NODE_PRIV))) {
throw new AnalysisException("ADMIN_PRIV and NODE_PRIV can only be granted on *.*.*");
throw new AnalysisException("ADMIN_PRIV and NODE_PRIV can only be granted/revoke on/from *.*.*");
}
// Rule 2
if (privileges.contains(PaloPrivilege.NODE_PRIV) && !Env.getCurrentEnv().getAuth()
.checkGlobalPriv(ConnectContext.get(), PrivPredicate.OPERATOR)) {
throw new AnalysisException("Only the user with NODE_PRIV can grant NODE_PRIV to other user");
throw new AnalysisException("Only user with NODE_PRIV can grant/revoke NODE_PRIV to other user");
}
if (role != null) {
// Rule 3 and 4
if (!Env.getCurrentEnv().getAuth().checkGlobalPriv(ConnectContext.get(), PrivPredicate.GRANT)) {
ErrorReport.reportAnalysisException(ErrorCode.ERR_SPECIFIC_ACCESS_DENIED_ERROR, "GRANT");
ErrorReport.reportAnalysisException(ErrorCode.ERR_SPECIFIC_ACCESS_DENIED_ERROR, "GRANT/ROVOKE");
}
} else {
// Rule 5.1 and 5.2
if (tblPattern.getPrivLevel() == PrivLevel.GLOBAL) {
if (!Env.getCurrentEnv().getAuth().checkGlobalPriv(ConnectContext.get(), PrivPredicate.GRANT)) {
ErrorReport.reportAnalysisException(ErrorCode.ERR_SPECIFIC_ACCESS_DENIED_ERROR, "GRANT");
ErrorReport.reportAnalysisException(ErrorCode.ERR_SPECIFIC_ACCESS_DENIED_ERROR, "GRANT/ROVOKE");
}
} else if (tblPattern.getPrivLevel() == PrivLevel.CATALOG) {
if (!Env.getCurrentEnv().getAuth().checkCtlPriv(ConnectContext.get(),
tblPattern.getQualifiedCtl(), PrivPredicate.GRANT)) {
ErrorReport.reportAnalysisException(ErrorCode.ERR_SPECIFIC_ACCESS_DENIED_ERROR, "GRANT");
ErrorReport.reportAnalysisException(ErrorCode.ERR_SPECIFIC_ACCESS_DENIED_ERROR, "GRANT/ROVOKE");
}
} else if (tblPattern.getPrivLevel() == PrivLevel.DATABASE) {
if (!Env.getCurrentEnv().getAuth().checkDbPriv(ConnectContext.get(),
tblPattern.getQualifiedCtl(), tblPattern.getQualifiedDb(), PrivPredicate.GRANT)) {
ErrorReport.reportAnalysisException(ErrorCode.ERR_SPECIFIC_ACCESS_DENIED_ERROR, "GRANT");
ErrorReport.reportAnalysisException(ErrorCode.ERR_SPECIFIC_ACCESS_DENIED_ERROR, "GRANT/ROVOKE");
}
} else {
// table level
if (!Env.getCurrentEnv().getAuth().checkTblPriv(ConnectContext.get(),
tblPattern.getQualifiedCtl(), tblPattern.getQualifiedDb(),
tblPattern.getTbl(), PrivPredicate.GRANT)) {
ErrorReport.reportAnalysisException(ErrorCode.ERR_SPECIFIC_ACCESS_DENIED_ERROR, "GRANT");
if (!Env.getCurrentEnv().getAuth()
.checkTblPriv(ConnectContext.get(), tblPattern.getQualifiedCtl(), tblPattern.getQualifiedDb(),
tblPattern.getTbl(), PrivPredicate.GRANT)) {
ErrorReport.reportAnalysisException(ErrorCode.ERR_SPECIFIC_ACCESS_DENIED_ERROR, "GRANT/ROVOKE");
}
}
}
// Rule 6
if (privileges.contains(PaloPrivilege.USAGE_PRIV)) {
throw new AnalysisException("Can not grant/revoke USAGE_PRIV to/from database or table");
}
}
public static void checkPrivileges(Analyzer analyzer, List<PaloPrivilege> privileges,
String role, ResourcePattern resourcePattern) throws AnalysisException {
public static void checkResourcePrivileges(List<PaloPrivilege> privileges, String role,
ResourcePattern resourcePattern) throws AnalysisException {
// Rule 1
if (privileges.contains(PaloPrivilege.NODE_PRIV)) {
throw new AnalysisException("Can not grant NODE_PRIV to any other users or roles");
throw new AnalysisException("Can not grant/revoke NODE_PRIV to/from any other users or roles");
}
// Rule 2
if (resourcePattern.getPrivLevel() != PrivLevel.GLOBAL && privileges.contains(PaloPrivilege.ADMIN_PRIV)) {
throw new AnalysisException("ADMIN_PRIV privilege can only be granted on resource *");
throw new AnalysisException("ADMIN_PRIV privilege can only be granted/revoked on/from resource *");
}
if (role != null) {
// Rule 3 and 4
if (!Env.getCurrentEnv().getAuth().checkGlobalPriv(ConnectContext.get(), PrivPredicate.GRANT)) {
ErrorReport.reportAnalysisException(ErrorCode.ERR_SPECIFIC_ACCESS_DENIED_ERROR, "GRANT");
ErrorReport.reportAnalysisException(ErrorCode.ERR_SPECIFIC_ACCESS_DENIED_ERROR, "GRANT/ROVOKE");
}
} else {
// Rule 5.1 and 5.3
if (resourcePattern.getPrivLevel() == PrivLevel.GLOBAL) {
if (!Env.getCurrentEnv().getAuth().checkGlobalPriv(ConnectContext.get(), PrivPredicate.GRANT)) {
ErrorReport.reportAnalysisException(ErrorCode.ERR_SPECIFIC_ACCESS_DENIED_ERROR, "GRANT");
ErrorReport.reportAnalysisException(ErrorCode.ERR_SPECIFIC_ACCESS_DENIED_ERROR, "GRANT/ROVOKE");
}
} else {
if (!Env.getCurrentEnv().getAuth().checkResourcePriv(ConnectContext.get(),
resourcePattern.getResourceName(), PrivPredicate.GRANT)) {
ErrorReport.reportAnalysisException(ErrorCode.ERR_SPECIFIC_ACCESS_DENIED_ERROR, "GRANT");
ErrorReport.reportAnalysisException(ErrorCode.ERR_SPECIFIC_ACCESS_DENIED_ERROR, "GRANT/ROVOKE");
}
}
}

View File

@ -112,9 +112,9 @@ public class RevokeStmt extends DdlStmt {
// Revoke operation obey the same rule as Grant operation. reuse the same method
if (tblPattern != null) {
GrantStmt.checkPrivileges(analyzer, privileges, role, tblPattern);
GrantStmt.checkTablePrivileges(privileges, role, tblPattern);
} else {
GrantStmt.checkPrivileges(analyzer, privileges, role, resourcePattern);
GrantStmt.checkResourcePrivileges(privileges, role, resourcePattern);
}
}

View File

@ -712,25 +712,37 @@ public class PaloAuth implements Writable {
}
// 3. set password
setPasswordInternal(userIdent, password, null, false /* err on non exist */,
false /* set by resolver */, true /* is replay */);
setPasswordInternal(userIdent, password, null, false /* err on non exist */, false /* set by resolver */,
true /* is replay */);
try {
// 4. grant privs of role to user
grantPrivsByRole(userIdent, role);
// 4. grant privs of role to user
grantPrivsByRole(userIdent, role);
// other user properties
propertyMgr.addUserResource(userIdent.getQualifiedUser(), false /* not system user */);
// other user properties
propertyMgr.addUserResource(userIdent.getQualifiedUser(), false /* not system user */);
if (!userIdent.getQualifiedUser().equals(ROOT_USER) && !userIdent.getQualifiedUser().equals(ADMIN_USER)) {
// grant read privs to database information_schema
TablePattern tblPattern = new TablePattern(DEFAULT_CATALOG, InfoSchemaDb.DATABASE_NAME, "*");
try {
tblPattern.analyze(ClusterNamespace.getClusterNameFromFullName(userIdent.getQualifiedUser()));
} catch (AnalysisException e) {
LOG.warn("should not happen", e);
if (!userIdent.getQualifiedUser().equals(ROOT_USER) && !userIdent.getQualifiedUser()
.equals(ADMIN_USER)) {
// grant read privs to database information_schema
TablePattern tblPattern = new TablePattern(DEFAULT_CATALOG, InfoSchemaDb.DATABASE_NAME, "*");
try {
tblPattern.analyze(ClusterNamespace.getClusterNameFromFullName(userIdent.getQualifiedUser()));
} catch (AnalysisException e) {
LOG.warn("should not happen", e);
}
grantInternal(userIdent, null /* role */, tblPattern, PrivBitSet.of(PaloPrivilege.SELECT_PRIV),
false /* err on non exist */, true /* is replay */);
}
grantInternal(userIdent, null /* role */, tblPattern, PrivBitSet.of(PaloPrivilege.SELECT_PRIV),
false /* err on non exist */, true /* is replay */);
} catch (Throwable t) {
// This is a temp protection to avoid bug such as described in
// https://github.com/apache/doris/issues/11235
// Normally, all operations in try..catch block should not fail
// Why add try..catch block after "setPasswordInternal"?
// Because after calling "setPasswordInternal()", the in-memory state has been changed,
// so we should make sure the following operations not throw any exception, if it throws,
// exit the process because there is no way to rollback in-memory state.
LOG.error("got unexpected exception when creating user. exit", t);
System.exit(-1);
}
if (!isReplay) {

View File

@ -34,6 +34,7 @@ import org.apache.doris.catalog.Env;
import org.apache.doris.common.AnalysisException;
import org.apache.doris.common.Config;
import org.apache.doris.common.DdlException;
import org.apache.doris.common.ExceptionChecker;
import org.apache.doris.common.UserException;
import org.apache.doris.datasource.InternalDataSource;
import org.apache.doris.persist.EditLog;
@ -1591,15 +1592,14 @@ public class AuthTest {
// 2. grant resource priv to db table
TablePattern tablePattern = new TablePattern("db1", "*");
grantStmt = new GrantStmt(userIdentity, null, tablePattern, usagePrivileges);
hasException = false;
try {
grantStmt.analyze(analyzer);
auth.grant(grantStmt);
} catch (UserException e) {
e.printStackTrace();
hasException = true;
}
Assert.assertTrue(hasException);
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));
// 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));
}
}