Skip to content

Commit

Permalink
[Authorization] Modify the authorization checking logic (apache#2372)
Browse files Browse the repository at this point in the history
**Authorization checking logic**

There are some problems with the current password and permission checking logic. For example:
First, we create a user by:
`create user cmy@"%" identified by "12345";`

And then 'cmy' can login with password '12345' from any hosts.

Second, we create another user by:
`create user cmy@"192.168.%" identified by "abcde";`

Because "192.168.%" has a higher priority in the permission table than "%". So when "cmy" try
to login in by password "12345" from host "192.168.1.1", it should match the second permission
entry, and will be rejected because of invalid password.
But in current implementation, Doris will continue to check password on first entry, than let it pass. So we should change it.

**Permission checking logic**

After a user login, it should has a unique identity which is got from permission table. For example,
when "cmy" from host "192.168.1.1" login, it's identity should be `cmy@"192.168.%"`. And Doris
should use this identity to check other permission, not by using the user's real identity, which is
`cmy@"192.168.1.1"`.

**Black list**
Functionally speaking, Doris only support adding WHITE LIST, which is to allow user to login from
those hosts in the white list. But is some cases, we do need a BLACK LIST function.
Fortunately, by changing the logic described above, we can simulate the effect of the BLACK LIST.

For example, First we add a user by:
`create user cmy@'%' identified by '12345';`

And now user 'cmy' can login from any hosts. and if we don't want 'cmy' to login from host A, we
can add a new user by:
`create user cmy@'A' identified by 'other_passwd';`

Because "A" has a higher priority in the permission table than "%". If 'cmy' try to login from A using password '12345', it will be rejected.
  • Loading branch information
morningman authored Dec 6, 2019
1 parent 177fec8 commit a46bf1a
Show file tree
Hide file tree
Showing 81 changed files with 1,170 additions and 2,182 deletions.
17 changes: 10 additions & 7 deletions be/src/exec/schema_scan_node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "exec/text_converter.hpp"
#include "exec/schema_scanner/schema_helper.h"
#include "gen_cpp/PlanNodes_types.h"
#include "gen_cpp/Types_types.h"
#include "runtime/runtime_state.h"
#include "runtime/row_batch.h"
#include "runtime/string_value.h"
Expand Down Expand Up @@ -66,18 +67,20 @@ Status SchemaScanNode::init(const TPlanNode& tnode, RuntimeState* state) {
_scanner_param.wild = _pool->add(new std::string(tnode.schema_scan_node.wild));
}

if (tnode.schema_scan_node.__isset.user) {
_scanner_param.user = _pool->add(new std::string(tnode.schema_scan_node.user));
if (tnode.schema_scan_node.__isset.current_user_ident) {
_scanner_param.current_user_ident = _pool->add(new TUserIdentity(tnode.schema_scan_node.current_user_ident));
} else {
if (tnode.schema_scan_node.__isset.user) {
_scanner_param.user = _pool->add(new std::string(tnode.schema_scan_node.user));
}
if (tnode.schema_scan_node.__isset.user_ip) {
_scanner_param.user_ip = _pool->add(new std::string(tnode.schema_scan_node.user_ip));
}
}

if (tnode.schema_scan_node.__isset.ip) {
_scanner_param.ip = _pool->add(new std::string(tnode.schema_scan_node.ip));
}

if (tnode.schema_scan_node.__isset.user_ip) {
_scanner_param.user_ip = _pool->add(new std::string(tnode.schema_scan_node.user_ip));
}

if (tnode.schema_scan_node.__isset.port) {
_scanner_param.port = tnode.schema_scan_node.port;
}
Expand Down
12 changes: 7 additions & 5 deletions be/src/exec/schema_scanner.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "common/status.h"
#include "common/object_pool.h"
#include "gen_cpp/Descriptors_types.h"
#include "gen_cpp/Types_types.h"
#include "runtime/tuple.h"
#include "runtime/mem_pool.h"

Expand All @@ -37,14 +38,15 @@ struct SchemaScannerParam {
const std::string* db;
const std::string* table;
const std::string* wild;
const std::string* user;
const std::string* user_ip;
const std::string* ip;
int32_t port;
const std::string* user; // deprecated
const std::string* user_ip; // deprecated
const TUserIdentity* current_user_ident; // to replace the user and user ip
const std::string* ip; // frontend ip
int32_t port; // frontend thrift port
int64_t thread_id;

SchemaScannerParam()
: db(NULL), table(NULL), wild(NULL), user(NULL), user_ip(NULL), ip(NULL), port(0) { }
: db(NULL), table(NULL), wild(NULL), user(NULL), user_ip(NULL), current_user_ident(NULL), ip(NULL), port(0) { }
};

// virtual scanner for all schema table
Expand Down
39 changes: 27 additions & 12 deletions be/src/exec/schema_scanner/schema_columns_scanner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,15 @@ Status SchemaColumnsScanner::start(RuntimeState *state) {
if (NULL != _param->db) {
db_params.__set_pattern(*(_param->db));
}
if (NULL != _param->user) {
db_params.__set_user(*(_param->user));
if (NULL != _param->current_user_ident) {
db_params.__set_current_user_ident(*_param->current_user_ident);
} else {
if (NULL != _param->user) {
db_params.__set_user(*(_param->user));
}
if (NULL != _param->user_ip) {
db_params.__set_user_ip(*(_param->user_ip));
}
}

if (NULL != _param->ip && 0 != _param->port) {
Expand Down Expand Up @@ -320,11 +327,15 @@ Status SchemaColumnsScanner::get_new_desc() {
TDescribeTableParams desc_params;
desc_params.__set_db(_db_result.dbs[_db_index - 1]);
desc_params.__set_table_name(_table_result.tables[_table_index++]);
if (NULL != _param->user) {
desc_params.__set_user(*(_param->user));
}
if (NULL != _param->user_ip) {
desc_params.__set_user_ip(*(_param->user_ip));
if (NULL != _param->current_user_ident) {
desc_params.__set_current_user_ident(*(_param->current_user_ident));
} else {
if (NULL != _param->user) {
desc_params.__set_user(*(_param->user));
}
if (NULL != _param->user_ip) {
desc_params.__set_user_ip(*(_param->user_ip));
}
}

if (NULL != _param->ip && 0 != _param->port) {
Expand All @@ -344,11 +355,15 @@ Status SchemaColumnsScanner::get_new_table() {
if (NULL != _param->table) {
table_params.__set_pattern(*(_param->table));
}
if (NULL != _param->user) {
table_params.__set_user(*(_param->user));
}
if (NULL != _param->user_ip) {
table_params.__set_user_ip(*(_param->user_ip));
if (NULL != _param->current_user_ident) {
table_params.__set_current_user_ident(*(_param->current_user_ident));
} else {
if (NULL != _param->user) {
table_params.__set_user(*(_param->user));
}
if (NULL != _param->user_ip) {
table_params.__set_user_ip(*(_param->user_ip));
}
}

if (NULL != _param->ip && 0 != _param->port) {
Expand Down
15 changes: 10 additions & 5 deletions be/src/exec/schema_scanner/schema_schemata_scanner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,17 @@ Status SchemaSchemataScanner::start(RuntimeState *state) {
if (NULL != _param->wild) {
db_params.__set_pattern(*(_param->wild));
}
if (NULL != _param->user) {
db_params.__set_user(*(_param->user));
}
if (NULL != _param->user_ip) {
db_params.__set_user_ip(*(_param->user_ip));
if (NULL != _param->current_user_ident) {
db_params.__set_current_user_ident(*(_param->current_user_ident));
} else {
if (NULL != _param->user) {
db_params.__set_user(*(_param->user));
}
if (NULL != _param->user_ip) {
db_params.__set_user_ip(*(_param->user_ip));
}
}

if (NULL != _param->ip && 0 != _param->port) {
RETURN_IF_ERROR(SchemaHelper::get_db_names(*(_param->ip),
_param->port, db_params, &_db_result));
Expand Down
28 changes: 18 additions & 10 deletions be/src/exec/schema_scanner/schema_tables_scanner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,15 @@ Status SchemaTablesScanner::start(RuntimeState *state) {
if (NULL != _param->db) {
db_params.__set_pattern(*(_param->db));
}
if (NULL != _param->user) {
db_params.__set_user(*(_param->user));
}
if (NULL != _param->user_ip) {
db_params.__set_user_ip(*(_param->user_ip));
if (NULL != _param->current_user_ident) {
db_params.__set_current_user_ident(*(_param->current_user_ident));
} else {
if (NULL != _param->user) {
db_params.__set_user(*(_param->user));
}
if (NULL != _param->user_ip) {
db_params.__set_user_ip(*(_param->user_ip));
}
}

if (NULL != _param->ip && 0 != _param->port) {
Expand Down Expand Up @@ -241,11 +245,15 @@ Status SchemaTablesScanner::get_new_table() {
if (NULL != _param->wild) {
table_params.__set_pattern(*(_param->wild));
}
if (NULL != _param->user) {
table_params.__set_user(*(_param->user));
}
if (NULL != _param->user_ip) {
table_params.__set_user_ip(*(_param->user_ip));
if (NULL != _param->current_user_ident) {
table_params.__set_current_user_ident(*(_param->current_user_ident));
} else {
if (NULL != _param->user) {
table_params.__set_user(*(_param->user));
}
if (NULL != _param->user_ip) {
table_params.__set_user_ip(*(_param->user_ip));
}
}

if (NULL != _param->ip && 0 != _param->port) {
Expand Down
1 change: 1 addition & 0 deletions build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ if [ ${BUILD_FE} -eq 1 ]; then

cp -r -p ${DORIS_HOME}/bin/*_fe.sh ${DORIS_OUTPUT}/fe/bin/
cp -r -p ${DORIS_HOME}/conf/fe.conf ${DORIS_OUTPUT}/fe/conf/
rm -rf ${DORIS_OUTPUT}/fe/lib/*
cp -r -p ${DORIS_HOME}/fe/target/lib/* ${DORIS_OUTPUT}/fe/lib/
cp -r -p ${DORIS_HOME}/fe/target/palo-fe.jar ${DORIS_OUTPUT}/fe/lib/
cp -r -p ${DORIS_HOME}/docs/build/help-resource.zip ${DORIS_OUTPUT}/fe/lib/
Expand Down
10 changes: 10 additions & 0 deletions docs/documentation/cn/administrator-guide/privilege.md
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,14 @@ ADMIN\_PRIV 和 GRANT\_PRIV 权限同时拥有**“授予权限”**的权限,

8. 拥有 GLOBAL 层级 GRANT_PRIV 其实等同于拥有 ADMIN\_PRIV,因为该层级的 GRANT\_PRIV 有授予任意权限的权限,请谨慎使用。

9. `current_user()``user()`

用户可以通过 `SELECT current_user();``SELECT user();` 分别查看 `current_user``user`。其中 `current_user` 表示当前用户是以哪种身份通过认证系统的,而 `user` 则是用户当前实际的 `user_identity`。举例说明:

假设创建了 `user1@'192.%'` 这个用户,然后以为来自 192.168.10.1 的用户 user1 登陆了系统,则此时的 `current_user``user1@'192.%'`,而 `user``user1@'192.168.10.1'`

所有的权限都是赋予某一个 `current_user` 的,真实用户拥有对应的 `current_user` 的所有权限。

## 最佳实践

这里举例一些 Doris 权限系统的使用场景。
Expand All @@ -202,6 +210,8 @@ ADMIN\_PRIV 和 GRANT\_PRIV 权限同时拥有**“授予权限”**的权限,

一个集群内有多个业务,每个业务可能使用一个或多个数据。每个业务需要管理自己的用户。在这种场景下。管理员用户可以为每个数据库创建一个拥有 DATABASE 层级 GRANT 权限的用户。该用户仅可以对用户进行指定的数据库的授权。

3. 黑名单

Doris 本身不支持黑名单,只有白名单功能,但我们可以通过某些方式来模拟黑名单。假设先创建了名为 `user@'192.%'` 的用户,表示允许来自 `192.*` 的用户登录。此时如果想禁止来自 `192.168.10.1` 的用户登录。则可以再创建一个用户 `cmy@'192.168.10.1'` 的用户,并设置一个新的密码。因为 `192.168.10.1` 的优先级高于 `192.%`,所以来自 `192.168.10.1` 将不能再使用旧密码进行登录。


Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,20 @@ under the License.

Syntax:

DROP USER 'user_name'
DROP USER 'user_identity'

DROP USER 命令会删除一个 palo 用户。这里 Doris 不支持删除指定的 user_identity。当删除一个指定用户后,该用户所对应的所有 user_identity 都会被删除。比如之前通过 CREATE USER 语句创建了 jack@'192.%' 以及 jack@['domain'] 两个用户,则在执行 DROP USER 'jack' 后,jack@'192.%' 以及 jack@['domain'] 都将被删除。
`user_identity`:

user@'host'
user@['domain']

删除指定的 user identitiy.

## example

1. 删除用户 jack
1. 删除用户 jack@'192.%'

DROP USER 'jack'
DROP USER 'jack'@'192.%'

## keyword

Expand Down
10 changes: 10 additions & 0 deletions docs/documentation/en/administrator-guide/privilege_EN.md
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,14 @@ ADMIN\_PRIV and GRANT\_PRIV have the authority of **"grant authority"** at the s

8. Having GRANT\_PRIV at GLOBAL level is actually equivalent to having ADMIN\_PRIV, because GRANT\_PRIV at this level has the right to grant arbitrary permissions, please use it carefully.

9. `current_user()` and `user()`

Users can view `current_user` and `user` respectively by `SELECT current_user();` and `SELECT user();`. Where `current_user` indicates which identity the current user is passing through the authentication system, and `user` is the user's current actual `user_identity`.

  For example, suppose the user `user1@'192.%'` is created, and then a user user1 from 192.168.10.1 is logged into the system. At this time, `current_user` is `user1@'192.%'`, and `user` is `user1@'192.168.10.1'`.

All privileges are given to a `current_user`, and the real user has all the privileges of the corresponding `current_user`.

## Best Practices

Here are some usage scenarios of Doris privilege system.
Expand All @@ -202,6 +210,8 @@ Here are some usage scenarios of Doris privilege system.

There are multiple services in a cluster, and each business may use one or more data. Each business needs to manage its own users. In this scenario. Administrator users can create a user with GRANT privileges at the DATABASE level for each database. The user can only authorize the specified database for the user.

3. Blacklist

Doris itself does not support blacklist, only whitelist, but we can simulate blacklist in some way. Suppose you first create a user named `user@'192.%'`, which allows users from `192.*` to login. At this time, if you want to prohibit users from `192.168.10.1` from logging in, you can create another user with `cmy@'192.168.10.1'` and set a new password. Since `192.168.10.1` has a higher priority than `192.%`, user can no longer login by using the old password from `192.168.10.1`.


Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,21 @@ under the License.

Syntax:

DROP USER 'user_name'
DROP USER 'user_identity'

The DROP USER command deletes a Palo user. Doris does not support deleting the specified user_identity here. When a specified user is deleted, all user_identities corresponding to that user are deleted. For example, two users, Jack @'192%'and Jack @['domain'] were created through the CREATE USER statement. After DROP USER'jack' was executed, Jack @'192%'and Jack @['domain'] would be deleted.
`user_identity`:

user@'host'
user@['domain']

Drop a specified user identity.

## example

1. Delete user jack
1. Delete user jack@'192.%'

DROP USER 'jack'
DROP USER 'jack'@'192.%'

## keyword
DROP, USER

DROP, USER
10 changes: 5 additions & 5 deletions fe/src/main/java/org/apache/doris/analysis/CreateUserStmt.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,15 @@
import org.apache.logging.log4j.Logger;

/*
* We support the following create user stmt
* 1. create user user@ip [identified by 'password']
* We support the following create user stmts:
* 1. create user user@'ip' [identified by 'password']
* specify the user name at a certain ip(wildcard is accepted), with optional password.
* the user@ip must not exist in system
*
* 2. create user user@[domain] [identified by 'password']
* 2. create user user@['domain'] [identified by 'password']
* specify the user name at a certain domain, with optional password.
* the user@[domain] must not exist in system
* the daemon thread will resolve this domain to user@ip format
* the user@['domain'] must not exist in system
* the daemon thread will resolve this domain to user@'ip' format
*
* 3. create user user@xx [identified by 'password'] role role_name
* not only create the specified user, but also grant all privs of the specified role to the user.
Expand Down
8 changes: 3 additions & 5 deletions fe/src/main/java/org/apache/doris/analysis/DropUserStmt.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@
import org.apache.doris.mysql.privilege.PrivPredicate;
import org.apache.doris.qe.ConnectContext;

// drop user cmy;
// drop user cmy@['domain'];
// drop user cmy <==> drop user cmy@'%'
// drop user cmy@'192.168.1.%'
public class DropUserStmt extends DdlStmt {
private UserIdentity userIdent;

Expand All @@ -42,10 +44,6 @@ public void analyze(Analyzer analyzer) throws AnalysisException, UserException {
super.analyze(analyzer);
userIdent.analyze(analyzer.getClusterName());

if (!userIdent.getHost().equals("%")) {
throw new AnalysisException("Can not drop user with specified host: " + userIdent.getHost());
}

// only user with GLOBAL level's GRANT_PRIV can drop user.
if (!Catalog.getCurrentCatalog().getAuth().checkGlobalPriv(ConnectContext.get(), PrivPredicate.GRANT)) {
ErrorReport.reportAnalysisException(ErrorCode.ERR_SPECIFIC_ACCESS_DENIED_ERROR, "DROP USER");
Expand Down
16 changes: 6 additions & 10 deletions fe/src/main/java/org/apache/doris/analysis/ShowGrantsStmt.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
import org.apache.doris.qe.ConnectContext;
import org.apache.doris.qe.ShowResultSetMetaData;

import com.google.common.base.Preconditions;

/*
* SHOW ALL GRANTS;
* show all grants.
Expand Down Expand Up @@ -64,10 +66,6 @@ public UserIdentity getUserIdent() {
return userIdent;
}

public boolean isAll() {
return isAll;
}

@Override
public void analyze(Analyzer analyzer) throws AnalysisException {
if (userIdent != null) {
Expand All @@ -78,15 +76,13 @@ public void analyze(Analyzer analyzer) throws AnalysisException {
} else {
if (!isAll) {
// self
userIdent = new UserIdentity(ConnectContext.get().getQualifiedUser(),
ConnectContext.get().getRemoteIP());
userIdent.setIsAnalyzed();
userIdent = ConnectContext.get().getCurrentUserIdentity();
}
}
Preconditions.checkState(isAll || userIdent != null);
UserIdentity self = ConnectContext.get().getCurrentUserIdentity();

UserIdentity self = new UserIdentity(ConnectContext.get().getQualifiedUser(),
ConnectContext.get().getRemoteIP());

// if show all grants, or show other user's grants, need global GRANT priv.
if (isAll || !self.equals(userIdent)) {
if (!Catalog.getCurrentCatalog().getAuth().checkGlobalPriv(ConnectContext.get(), PrivPredicate.GRANT)) {
ErrorReport.reportAnalysisException(ErrorCode.ERR_SPECIFIC_ACCESS_DENIED_ERROR, "GRANT");
Expand Down
Loading

0 comments on commit a46bf1a

Please sign in to comment.