diff --git a/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/AccessControllerManager.java b/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/AccessControllerManager.java index 2e588ea792..eb81d1d692 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/AccessControllerManager.java +++ b/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/AccessControllerManager.java @@ -20,6 +20,7 @@ package org.apache.doris.mysql.privilege; import org.apache.doris.analysis.TableName; import org.apache.doris.analysis.UserIdentity; import org.apache.doris.catalog.AuthorizationInfo; +import org.apache.doris.common.AuthorizationException; import org.apache.doris.common.UserException; import org.apache.doris.datasource.InternalCatalog; import org.apache.doris.mysql.privilege.Auth.PrivLevel; @@ -32,6 +33,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import java.util.Map; +import java.util.Set; /** * AccessControllerManager is the entry point of privilege authentication. @@ -101,7 +103,7 @@ public class AccessControllerManager { return checkCtlPriv(ctx.getCurrentUserIdentity(), ctl, wanted); } - private boolean checkCtlPriv(UserIdentity currentUser, String ctl, PrivPredicate wanted) { + public boolean checkCtlPriv(UserIdentity currentUser, String ctl, PrivPredicate wanted) { boolean hasGlobal = sysAccessController.checkGlobalPriv(currentUser, wanted); return getAccessControllerOrDefault(ctl).checkCtlPriv(hasGlobal, currentUser, ctl, wanted); } @@ -159,6 +161,18 @@ public class AccessControllerManager { } } + public boolean checkColumnsPriv(UserIdentity currentUser, String db, String tbl, Set cols, + PrivPredicate wanted) { + boolean hasGlobal = sysAccessController.checkGlobalPriv(currentUser, wanted); + CatalogAccessController accessController = getAccessControllerOrDefault(Auth.DEFAULT_CATALOG); + try { + accessController.checkColsPriv(hasGlobal, currentUser, Auth.DEFAULT_CATALOG, db, tbl, cols, wanted); + return true; + } catch (AuthorizationException e) { + return false; + } + } + // ==== Resource ==== public boolean checkResourcePriv(ConnectContext ctx, String resourceName, PrivPredicate wanted) { return checkResourcePriv(ctx.getCurrentUserIdentity(), resourceName, wanted); diff --git a/fe/fe-core/src/main/java/org/apache/doris/service/FrontendServiceImpl.java b/fe/fe-core/src/main/java/org/apache/doris/service/FrontendServiceImpl.java index addc919e64..240eb352c6 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/service/FrontendServiceImpl.java +++ b/fe/fe-core/src/main/java/org/apache/doris/service/FrontendServiceImpl.java @@ -62,6 +62,7 @@ import org.apache.doris.datasource.ExternalCatalog; import org.apache.doris.datasource.HMSExternalCatalog; import org.apache.doris.datasource.InternalCatalog; import org.apache.doris.master.MasterImpl; +import org.apache.doris.mysql.privilege.AccessControllerManager; import org.apache.doris.mysql.privilege.PrivPredicate; import org.apache.doris.planner.StreamLoadPlanner; import org.apache.doris.qe.ConnectContext; @@ -78,6 +79,8 @@ import org.apache.doris.thrift.FrontendServiceVersion; import org.apache.doris.thrift.TAddColumnsRequest; import org.apache.doris.thrift.TAddColumnsResult; import org.apache.doris.thrift.TCell; +import org.apache.doris.thrift.TCheckAuthRequest; +import org.apache.doris.thrift.TCheckAuthResult; import org.apache.doris.thrift.TColumn; import org.apache.doris.thrift.TColumnDef; import org.apache.doris.thrift.TColumnDesc; @@ -119,7 +122,10 @@ import org.apache.doris.thrift.TMasterResult; import org.apache.doris.thrift.TMetadataTableRequestParams; import org.apache.doris.thrift.TMySqlLoadAcquireTokenResult; import org.apache.doris.thrift.TNetworkAddress; +import org.apache.doris.thrift.TPrivilegeCtrl; +import org.apache.doris.thrift.TPrivilegeHier; import org.apache.doris.thrift.TPrivilegeStatus; +import org.apache.doris.thrift.TPrivilegeType; import org.apache.doris.thrift.TReportExecStatusParams; import org.apache.doris.thrift.TReportExecStatusResult; import org.apache.doris.thrift.TReportRequest; @@ -1629,5 +1635,110 @@ public class FrontendServiceImpl implements FrontendService.Iface { return result; } + + @Override + public TCheckAuthResult checkAuth(TCheckAuthRequest request) throws TException { + String clientAddr = getClientAddrAsString(); + LOG.debug("receive auth request: {}, backend: {}", request, clientAddr); + + TCheckAuthResult result = new TCheckAuthResult(); + TStatus status = new TStatus(TStatusCode.OK); + result.setStatus(status); + + String cluster = request.getCluster(); + if (Strings.isNullOrEmpty(cluster)) { + cluster = SystemInfoService.DEFAULT_CLUSTER; + } + + // check account and password + final String fullUserName = ClusterNamespace.getFullName(cluster, request.getUser()); + List currentUser = Lists.newArrayList(); + try { + Env.getCurrentEnv().getAuth().checkPlainPassword(fullUserName, request.getUserIp(), request.getPasswd(), + currentUser); + } catch (AuthenticationException e) { + status.setStatusCode(TStatusCode.ANALYSIS_ERROR); + status.addToErrorMsgs(Strings.nullToEmpty(e.getMessage())); + return result; + } + + Preconditions.checkState(currentUser.size() == 1); + PrivPredicate predicate = getPrivPredicate(request.getPrivType()); + if (predicate == null) { + return result; + } + // check privilege + AccessControllerManager accessManager = Env.getCurrentEnv().getAccessManager(); + TPrivilegeCtrl privCtrl = request.getPrivCtrl(); + TPrivilegeHier privHier = privCtrl.getPrivHier(); + if (privHier == TPrivilegeHier.GLOBAL) { + if (!accessManager.checkGlobalPriv(currentUser.get(0), predicate)) { + status.setStatusCode(TStatusCode.ANALYSIS_ERROR); + status.addToErrorMsgs("Global permissions error"); + } + } else if (privHier == TPrivilegeHier.CATALOG) { + if (!accessManager.checkCtlPriv(currentUser.get(0), privCtrl.getCtl(), predicate)) { + status.setStatusCode(TStatusCode.ANALYSIS_ERROR); + status.addToErrorMsgs("Catalog permissions error"); + } + } else if (privHier == TPrivilegeHier.DATABASE) { + String fullDbName = ClusterNamespace.getFullName(cluster, privCtrl.getDb()); + if (!accessManager.checkDbPriv(currentUser.get(0), fullDbName, predicate)) { + status.setStatusCode(TStatusCode.ANALYSIS_ERROR); + status.addToErrorMsgs("Database permissions error"); + } + } else if (privHier == TPrivilegeHier.TABLE) { + String fullDbName = ClusterNamespace.getFullName(cluster, privCtrl.getDb()); + if (!accessManager.checkTblPriv(currentUser.get(0), fullDbName, privCtrl.getTbl(), predicate)) { + status.setStatusCode(TStatusCode.ANALYSIS_ERROR); + status.addToErrorMsgs("Table permissions error"); + } + } else if (privHier == TPrivilegeHier.COLUMNS) { + String fullDbName = ClusterNamespace.getFullName(cluster, privCtrl.getDb()); + if (!accessManager.checkColumnsPriv(currentUser.get(0), fullDbName, privCtrl.getTbl(), privCtrl.getCols(), + predicate)) { + status.setStatusCode(TStatusCode.ANALYSIS_ERROR); + status.addToErrorMsgs("Columns permissions error"); + } + } else if (privHier == TPrivilegeHier.RESOURSE) { + if (!accessManager.checkResourcePriv(currentUser.get(0), privCtrl.getRes(), predicate)) { + status.setStatusCode(TStatusCode.ANALYSIS_ERROR); + status.addToErrorMsgs("Resourse permissions error"); + } + } else { + status.setStatusCode(TStatusCode.ANALYSIS_ERROR); + status.addToErrorMsgs("Privilege control error"); + } + return result; + } + + private PrivPredicate getPrivPredicate(TPrivilegeType privType) { + switch (privType) { + case SHOW: + return PrivPredicate.SHOW; + case SHOW_RESOURCES: + return PrivPredicate.SHOW_RESOURCES; + case GRANT: + return PrivPredicate.GRANT; + case ADMIN: + return PrivPredicate.ADMIN; + case LOAD: + return PrivPredicate.LOAD; + case ALTER: + return PrivPredicate.ALTER; + case USAGE: + return PrivPredicate.USAGE; + case CREATE: + return PrivPredicate.CREATE; + case ALL: + return PrivPredicate.ALL; + case OPERATOR: + return PrivPredicate.OPERATOR; + case DROP: + return PrivPredicate.DROP; + default: + return null; + } + } } diff --git a/gensrc/thrift/FrontendService.thrift b/gensrc/thrift/FrontendService.thrift index 7006e27749..54b12fd8ac 100644 --- a/gensrc/thrift/FrontendService.thrift +++ b/gensrc/thrift/FrontendService.thrift @@ -774,6 +774,52 @@ struct TConfirmUnusedRemoteFilesResult { 1: optional list confirmed_tablets } +enum TPrivilegeHier { + GLOBAL = 0, + CATALOG = 1, + DATABASE = 2, + TABLE = 3, + COLUMNS = 4, + RESOURSE = 5, +} + +struct TPrivilegeCtrl { + 1: required TPrivilegeHier priv_hier + 2: optional string ctl + 3: optional string db + 4: optional string tbl + 5: optional set cols + 6: optional string res +} + +enum TPrivilegeType { + SHOW = 0, + SHOW_RESOURCES = 1, + GRANT = 2, + ADMIN = 3, + LOAD = 4, + ALTER = 5, + USAGE = 6, + CREATE = 7, + ALL = 8, + OPERATOR = 9, + DROP = 10 +} + +struct TCheckAuthRequest { + 1: optional string cluster + 2: required string user + 3: required string passwd + 4: optional string user_ip + 5: optional TPrivilegeCtrl priv_ctrl + 6: optional TPrivilegeType priv_type + 7: optional i64 thrift_rpc_timeout_ms +} + +struct TCheckAuthResult { + 1: required Status.TStatus status +} + service FrontendService { TGetDbsResult getDbNames(1: TGetDbsParams params) TGetTablesResult getTableNames(1: TGetTablesParams params) @@ -818,4 +864,6 @@ service FrontendService { TMySqlLoadAcquireTokenResult acquireToken() TConfirmUnusedRemoteFilesResult confirmUnusedRemoteFiles(1: TConfirmUnusedRemoteFilesRequest request) + + TCheckAuthResult checkAuth(1: TCheckAuthRequest request) }