[fix](auth) Forbid grant USAGE_PRIV to database.table (#11234)
This commit is contained in:
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user