This is an automated email from the ASF dual-hosted git repository. yiguolei pushed a commit to branch branch-2.1 in repository https://gitbox.apache.org/repos/asf/doris.git
commit 5d7543b30b19a564956af01ff745bbe6b24e56f3 Author: Mingyu Chen <morning...@163.com> AuthorDate: Sat Jan 27 09:08:15 2024 +0800 [feature](ranger) Support Apache ranger for Doris (#27864) For usage, see: https://github.com/apache/doris/blob/5d340ce24f851c642f3969d2a460432ae4d8a3e2/docs/zh-CN/docs/admin-manual/privilege-ldap/ranger.md For range-doris-plugin, see: https://github.com/morningman/ranger/tree/doris-plugin To support ranger, there are several other modification: 1. Support `show resources like "pattern"` 2. Support `show workload group like "pattern"` 3. Support `show schemas like "pattern"` --- docs/en/docs/admin-manual/privilege-ldap/ranger.md | 249 +++++++++++++++++++++ docs/en/docs/lakehouse/multi-catalog/hive.md | 30 ++- .../Show-Statements/SHOW-RESOURCES.md | 19 +- .../Show-Statements/SHOW-WORKLOAD-GROUPS.md | 19 +- docs/images/ranger/ranger1.png | Bin 0 -> 176660 bytes docs/images/ranger/ranger2.png | Bin 0 -> 150841 bytes docs/images/ranger/ranger3.png | Bin 0 -> 152867 bytes docs/images/ranger/ranger4.png | Bin 0 -> 175828 bytes docs/sidebars.json | 3 +- .../docs/admin-manual/privilege-ldap/ranger.md | 249 +++++++++++++++++++++ docs/zh-CN/docs/lakehouse/multi-catalog/hive.md | 32 ++- .../Show-Statements/SHOW-RESOURCES.md | 11 +- .../Show-Statements/SHOW-WORKLOAD-GROUPS.md | 17 +- .../main/java/org/apache/doris/common/Config.java | 81 +++++++ fe/fe-core/src/main/cup/sql_parser.cup | 10 +- .../apache/doris/analysis/AlterDatabaseRename.java | 5 +- .../org/apache/doris/analysis/AlterUserStmt.java | 5 +- .../org/apache/doris/analysis/CreateUserStmt.java | 5 +- .../org/apache/doris/analysis/RecoverDbStmt.java | 6 +- .../doris/analysis/RecoverPartitionStmt.java | 6 +- .../apache/doris/analysis/RecoverTableStmt.java | 13 +- .../apache/doris/analysis/ShowCreateDbStmt.java | 11 +- .../apache/doris/analysis/ShowResourcesStmt.java | 51 +++-- .../doris/analysis/ShowWorkloadGroupsStmt.java | 23 +- .../java/org/apache/doris/catalog/ResourceMgr.java | 8 +- .../authorizer/ranger/RangerAccessController.java | 69 ++++++ .../authorizer/ranger/doris/DorisAccessType.java | 80 +++++++ .../doris/DorisObjectType.java} | 6 +- .../ranger/doris/RangerDorisAccessController.java | 181 +++++++++++++++ .../doris/RangerDorisPlugin.java} | 6 +- .../ranger/doris/RangerDorisResource.java | 80 +++++++ .../{ => ranger/hive}/HiveAccessType.java | 2 +- .../{ => ranger/hive}/HiveObjectType.java | 2 +- .../hive}/RangerHiveAccessController.java | 72 +++--- .../hive}/RangerHiveAccessControllerFactory.java | 2 +- .../{ => ranger/hive}/RangerHiveAuditHandler.java | 2 +- .../hive}/RangerHiveAuditLogFlusher.java | 2 +- .../hive}/RangerHiveAuthorizerProvider.java | 2 +- .../{ => ranger/hive}/RangerHivePlugin.java | 2 +- .../{ => ranger/hive}/RangerHiveResource.java | 2 +- .../doris/httpv2/controller/BaseController.java | 9 +- .../mysql/privilege/AccessControllerManager.java | 55 +++-- .../mysql/privilege/CatalogAccessController.java | 10 + ...ntroller.java => InternalAccessController.java} | 19 +- .../doris/mysql/privilege/PrivPredicate.java | 23 +- .../mysql/privilege/SystemAccessController.java | 54 ----- .../java/org/apache/doris/qe/ShowExecutor.java | 20 +- .../resource/workloadgroup/WorkloadGroupMgr.java | 17 +- .../apache/doris/analysis/CreateUserStmtTest.java | 6 +- .../doris/analysis/ShowResourcesStmtTest.java | 12 +- .../apache/doris/datasource/ColumnPrivTest.java | 16 ++ 51 files changed, 1324 insertions(+), 280 deletions(-) diff --git a/docs/en/docs/admin-manual/privilege-ldap/ranger.md b/docs/en/docs/admin-manual/privilege-ldap/ranger.md new file mode 100644 index 00000000000..c2778aafa21 --- /dev/null +++ b/docs/en/docs/admin-manual/privilege-ldap/ranger.md @@ -0,0 +1,249 @@ +--- +{ + "title": "Integration with Apache Ranger", + "language": "en" +} +--- + +<!-- +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. +--> + +# Integration with Apache Ranger + +Apache Ranger is a security framework used to monitor, enable services, and manage all-round data security access on the Hadoop platform. + +In version 2.1.0, Doris supports unified permission management by integrating Apache Ranger. + +> Note: +> +> - This feature is currently experimental, and the resource objects and permissions configurable in Ranger may change in subsequent versions. +> +> - Apache Ranger version needs to be above 2.1.0. + +## Installation + +### Install Doris Ranger plug-in + +1. Download the following files + + - [ranger-doris-plugin-3.0.0-SNAPSHOT.jar](https://selectdb-doris-1308700295.cos.ap-beijing.myqcloud.com/ranger/ranger-doris-plugin-3.0.0-SNAPSHOT.jar) + - [mysql-connector-java-8.0.25.jar](https://selectdb-doris-1308700295.cos.ap-beijing.myqcloud.com/release/jdbc_driver/mysql-connector-java-8.0.25.jar) + +2. Place the downloaded file in the plugins directory of the Ranger service, such as: + + ``` + /usr/local/service/ranger/ews/webapp/WEB-INF/classes/ranger-plugins/doris/ranger-doris-plugin-3.0.0-SNAPSHOT.jar + /usr/local/service/ranger/ews/webapp/WEB-INF/classes/ranger-plugins/doris/mysql-connector-java-8.0.25.jar + ``` + +3. Restart the Ranger service. + +4. Download [ranger-servicedef-doris.json](https://selectdb-doris-1308700295.cos.ap-beijing.myqcloud.com/ranger/ranger-servicedef-doris.json) + +5. Execute the following command to upload the definition file to the Ranger service: + + ``` + curl -u user:password -X POST \ + -H "Accept: application/json" \ + -H "Content-Type: application/json" \ + http://172.21.0.32:6080/service/plugins/definitions \ + -d...@ranger-servicedef-doris.json + ``` + + The username and password are the username and password used to log in to Ranger WebUI. + + The service address port can be viewed in the `ranger.service.http.port` configuration item of the `ranger-admin-site.xml` configuration file. + + If the execution is successful, the service definition in Json format will be returned, such as: + + ``` + { + "id": 207, + "guid": "d3ff9e41-f9dd-4217-bb5f-3fa9996454b6", + "isEnabled": true, + "createdBy": "Admin", + "updatedBy": "Admin", + "createTime": 1705817398112, + "updateTime": 1705817398112, + "version": 1, + "name": "doris", + "displayName": "Apache Doris", + "implClass": "org.apache.ranger.services.doris.RangerServiceDoris", + "label": "Doris", + "description": "Apache Doris", + "options": { + "enableDenyAndExceptionsInPolicies": "true" + }, + ... + } + ``` + + If you want to recreate it, you can use the following command to delete the service definition and then upload it again: + + ``` + curl -v -u user:password -X DELETE \ + http://172.21.0.32:6080/service/plugins/definitions/207 + ``` + + Where `207` is the id returned when created. Before deletion, you need to delete the created Doris service in the Ranger WebUI. + + You can also use the following command to list the currently added service definitions in order to obtain the id: + + ``` + curl -v -u user:password -X GET \ + http://172.21.0.32:6080/service/plugins/definitions/ + ``` + +### Configure the Doris Ranger plug-in + +After the installation is complete, open the Ranger WebUI and you can see the Apache Doris plug-in in the Service Manger interface: + + + +Click the `+` button next to the plugin to add a Doris service: + + + +The meaning of some parameters of Config Properties is as follows: + +- `Username`/`Pasword`: the username and password of the Doris cluster. It is recommended to use the Admin user here. +- `jdbc.driver_class`: Connect to the JDBC driver used by Doris. `com.mysql.cj.jdbc.Driver` +- `jdbc.url`: JDBC url connection string of Doris cluster. `jdbc:mysql://172.21.0.101:9030?useSSL=false` +- Additional parameters: + - `resource.lookup.timeout.value.in.ms`: timeout for obtaining meta-information. It is recommended to fill in `10000`, which is 10 seconds. + +You can click `Test Connection` to check whether the connection can be made. + +Then click `Add` to add the service. + +Afterwards, you can see the created service in the Apache Doris plug-in on the Service Manger page. Click on the service to start configuring Ranger. + +### Configure Doris cluster + +1. Create a `ranger-doris-security.xml` file in the conf directory of all FEs with the following content: + + ``` + <?xml version="1.0" encoding="UTF-8"?> + <?xml-stylesheet type="text/xsl" href="configuration.xsl"?> + <configuration> + <property> + <name>ranger.plugin.doris.policy.cache.dir</name> + <value>/path/to/ranger/cache/</value> + </property> + <property> + <name>ranger.plugin.doris.policy.pollIntervalMs</name> + <value>30000</value> + </property> + <property> + <name>ranger.plugin.doris.policy.rest.client.connection.timeoutMs</name> + <value>60000</value> + </property> + <property> + <name>ranger.plugin.doris.policy.rest.client.read.timeoutMs</name> + <value>60000</value> + </property> + <property> + <name>ranger.plugin.doris.policy.rest.url</name> + <value>http://172.21.0.32:6080</value> + </property> + <property> + <name>ranger.plugin.doris.policy.source.impl</name> + <value>org.apache.ranger.admin.client.RangerAdminRESTClient</value> + </property> + <property> + <name>ranger.plugin.doris.service.name</name> + <value>doris</value> + </property> + </configuration> + ``` + + You need to change `ranger.plugin.doris.policy.cache.dir` and `ranger.plugin.doris.policy.rest.url` to actual values. + +2. Create a `log4j.properties` file in the conf directory of all FEs with the following content: + + ``` + log4j.rootLogger = debug,stdout,D + + log4j.appender.stdout = org.apache.log4j.ConsoleAppender + log4j.appender.stdout.Target = System.out + log4j.appender.stdout.layout = org.apache.log4j.PatternLayout + log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n + + log4j.appender.D = org.apache.log4j.DailyRollingFileAppender + log4j.appender.D.File = /path/to/fe/log/ranger.log + log4j.appender.D.Append = true + log4j.appender.D.Threshold = INFO + log4j.appender.D.layout = org.apache.log4j.PatternLayout + log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n + ``` + + You need to change `log4j.appender.D.File` to the actual value, which is used to store the log of the Ranger plug-in. + +3. Add configuration in fe.conf of all FEs: + + `access_controller_type=ranger-doris` + +4. Restart all FE nodes. + +## Resources and permissions + +1. Doris resources currently supported in Ranger include: + + - `Catalog` + - `Database` + - `Table` + - `Column` + - `Resource` + - `Workload Group` + +2. Doris permissions currently supported in Ranger include: + + - `SHOW` + - `SHOW_VIEW` + - `SHOW_RESOURCES` + - `SHOW_WORKLOAD_GROUP` + - `LOAD` + - `ALTER` + - `CREATE` + - `ALTER_CREATE` + - `ALTER_CREATE_DROP` + - `DROP` + - `SELECT` + +## Best Practices + +### Example 1 + +1. Create `user1` in Doris. +2. In Doris, first create a Catalog: `hive` using the `admin` user. +3. Create `user1` in Ranger. +4. Add a Policy in Ranger: `show_hive_catalog` + +  + +5. Use `user1` to log in to Doris and execute `show catalogs`. Only the `hive` catalog can be seen. +6. Add a Policy in Ranger: `select_hive_catalog` + +  + +7. Log in to Doris using `user1`. This user can view or query all tables under the `hive` catalog and all databases starting with `tpch`. + +## FAQ + +1. The column permissions, row permissions and Data Mask in Ranger are not supported yet. diff --git a/docs/en/docs/lakehouse/multi-catalog/hive.md b/docs/en/docs/lakehouse/multi-catalog/hive.md index e5fc390a05d..c26fe65eeb4 100644 --- a/docs/en/docs/lakehouse/multi-catalog/hive.md +++ b/docs/en/docs/lakehouse/multi-catalog/hive.md @@ -402,7 +402,11 @@ Doris has implemented Broker query support for HMS Catalog Iceberg based on the Apache Ranger is a security framework for monitoring, enabling services, and comprehensive data security access management on the Hadoop platform. -Currently doris supports ranger library, table, and column permissions, but does not support encryption, row permissions, etc. +Doris supports using Apache Ranger for authentication for a specified External Hive Catalog. + +Currently, authentication of database, table, and column is supported. Functions such as encryption, row policy, and data masks are not currently supported. + +To use Apache Ranger to authenticate the entire Doris cluster service, please refer to [Apache Ranger Authentication](../../admin-manual/privilege-ldap/ranger.md) ### Settings @@ -410,23 +414,14 @@ To connect to the Hive Metastore with Ranger permission verification enabled, yo 1. When creating a Catalog, add: -```sql -"access_controller.properties.ranger.service.name" = "hive", -"access_controller.class" = "org.apache.doris.catalog.authorizer.RangerHiveAccessControllerFactory", -``` + ```sql + "access_controller.properties.ranger.service.name" = "hive", + "access_controller.class" = "org.apache.doris.catalog.authorizer.RangerHiveAccessControllerFactory", + ``` ->Note: -> ->"access_controller.properties.ranger.service.name" It refers to the type of service, such as hive, hdfs, etc. -> If the configuration of `ranger-hive-security.xml`is as follows, "access_controller.properties.ranger.service.name" should be set to `hive` instead of `testative` -> - -```sql -<property> - <name>ranger.plugin.hive.service.name</name> - <value>testhive</value> -</property> -``` + > Note: + > + > `access_controller.properties.ranger.service.name` refers to the type of service, such as `hive`, `hdfs`, etc. It is not the value of `ranger.plugin.hive.service.name` in the configuration file. 2. Configure all FE environments: @@ -501,6 +496,7 @@ To connect to the Hive Metastore with Ranger permission verification enabled, yo 4. Create role1 with the same name in doris, and assign role1 to user1, user1 will have the query authority of db1.table1.col1 and col2 at the same time +5. The permissions of Admin and Root users are not controlled by Apache Ranger. ## Integrate with Kerberos diff --git a/docs/en/docs/sql-manual/sql-reference/Show-Statements/SHOW-RESOURCES.md b/docs/en/docs/sql-manual/sql-reference/Show-Statements/SHOW-RESOURCES.md index 7722cae8316..71f5224a3a9 100644 --- a/docs/en/docs/sql-manual/sql-reference/Show-Statements/SHOW-RESOURCES.md +++ b/docs/en/docs/sql-manual/sql-reference/Show-Statements/SHOW-RESOURCES.md @@ -42,7 +42,7 @@ SHOW RESOURCES WHERE [NAME [ = "your_resource_name" | LIKE "name_matcher"]] [RESOURCETYPE = ["SPARK"]] -] +] | [LIKE "pattern"] [ORDER BY...] [LIMIT limit][OFFSET offset]; ```` @@ -55,6 +55,7 @@ illustrate: 4. You can use ORDER BY to sort on any combination of columns 5. If LIMIT is specified, limit matching records are displayed. Otherwise show all 6. If OFFSET is specified, the query results are displayed starting at offset offset. By default the offset is 0. +7. If using LIKE, the WHERE clause will be ignored. ### Example @@ -62,19 +63,25 @@ illustrate: ```sql SHOW RESOURCES; - ```` + ``` -1. Display the specified Resource, the name contains the string "20140102", and display 10 attributes +2. Display the specified Resource, the name contains the string "20140102", and display 10 attributes ```sql SHOW RESOURCES WHERE NAME LIKE "2014_01_02" LIMIT 10; - ```` + ``` -1. Display the specified Resource, specify the name as "20140102" and sort by KEY in descending order +3. Display the specified Resource, specify the name as "20140102" and sort by KEY in descending order ```sql SHOW RESOURCES WHERE NAME = "20140102" ORDER BY `KEY` DESC; - ```` + ``` + +3. Using LIKE to match the resource + + ```sql + SHOW RESOURCES LIKE "jdbc%"; + ``` ### Keywords diff --git a/docs/en/docs/sql-manual/sql-reference/Show-Statements/SHOW-WORKLOAD-GROUPS.md b/docs/en/docs/sql-manual/sql-reference/Show-Statements/SHOW-WORKLOAD-GROUPS.md index 706e9c7b633..a6bc02ea81c 100644 --- a/docs/en/docs/sql-manual/sql-reference/Show-Statements/SHOW-WORKLOAD-GROUPS.md +++ b/docs/en/docs/sql-manual/sql-reference/Show-Statements/SHOW-WORKLOAD-GROUPS.md @@ -30,8 +30,6 @@ under the License. SHOW workload GROUPS -<version since="dev"></version> - ### Description This statement is used to display the resource groups for which the current user has usage_priv privileges. @@ -39,7 +37,7 @@ This statement is used to display the resource groups for which the current user grammar: ```sql -SHOW workload GROUPS; +SHOW WORKLOAD GROUPS [LIKE "pattern"]; ``` Description: @@ -63,8 +61,21 @@ This statement only does a simple display of workload groups, for a more complex +----------+--------+--------------------------+---------+ ``` +2. Show workload groups using pattern + + ```sql + mysql> show workload groups like "normal%"; + +----------+--------+--------------------------+---------+ + | Id | Name | Item | Value | + +----------+--------+--------------------------+---------+ + | 10343386 | normal | cpu_share | 10 | + | 10343386 | normal | memory_limit | 30% | + | 10343386 | normal | enable_memory_overcommit | true | + +----------+--------+--------------------------+---------+ + ``` + ### Keywords - SHOW, workload, GROUPS, GROUP + SHOW, WORKLOAD, GROUPS, GROUP ### Best Practice diff --git a/docs/images/ranger/ranger1.png b/docs/images/ranger/ranger1.png new file mode 100644 index 00000000000..64ba3f4a849 Binary files /dev/null and b/docs/images/ranger/ranger1.png differ diff --git a/docs/images/ranger/ranger2.png b/docs/images/ranger/ranger2.png new file mode 100644 index 00000000000..077808dd4b6 Binary files /dev/null and b/docs/images/ranger/ranger2.png differ diff --git a/docs/images/ranger/ranger3.png b/docs/images/ranger/ranger3.png new file mode 100644 index 00000000000..17c1748c08f Binary files /dev/null and b/docs/images/ranger/ranger3.png differ diff --git a/docs/images/ranger/ranger4.png b/docs/images/ranger/ranger4.png new file mode 100644 index 00000000000..5467527962f Binary files /dev/null and b/docs/images/ranger/ranger4.png differ diff --git a/docs/sidebars.json b/docs/sidebars.json index 95f18fd9ba8..6f2071d7524 100644 --- a/docs/sidebars.json +++ b/docs/sidebars.json @@ -1251,7 +1251,8 @@ "label": "User Privilege and Ldap", "items": [ "admin-manual/privilege-ldap/user-privilege", - "admin-manual/privilege-ldap/ldap" + "admin-manual/privilege-ldap/ldap", + "admin-manual/privilege-ldap/ranger" ] }, { diff --git a/docs/zh-CN/docs/admin-manual/privilege-ldap/ranger.md b/docs/zh-CN/docs/admin-manual/privilege-ldap/ranger.md new file mode 100644 index 00000000000..c2543c42699 --- /dev/null +++ b/docs/zh-CN/docs/admin-manual/privilege-ldap/ranger.md @@ -0,0 +1,249 @@ +--- +{ + "title": "集成 Apache Ranger", + "language": "zh-CN" +} +--- + +<!-- +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. +--> + +# 集成 Apache Ranger + +Apache Ranger是一个用来在Hadoop平台上进行监控,启用服务,以及全方位数据安全访问管理的安全框架。 + +在 2.1.0 版本中,Doris 支持通过集成 Apache Ranger,进行统一的权限管理。 + +> 注意: +> +> - 目前该功能是实验性功能,在 Ranger 中可配置的资源对象和权限可能会在之后的版本中有所变化。 +> +> - Apache Ranger 版本需在 2.1.0 以上。 + +## 安装步骤 + +### 安装 Doris Ranger 插件 + +1. 下载以下文件 + + - [ranger-doris-plugin-3.0.0-SNAPSHOT.jar](https://selectdb-doris-1308700295.cos.ap-beijing.myqcloud.com/ranger/ranger-doris-plugin-3.0.0-SNAPSHOT.jar) + - [mysql-connector-java-8.0.25.jar](https://selectdb-doris-1308700295.cos.ap-beijing.myqcloud.com/release/jdbc_driver/mysql-connector-java-8.0.25.jar) + +2. 将下载好的文件放到 Ranger 服务的 plugins 目录下,如: + + ``` + /usr/local/service/ranger/ews/webapp/WEB-INF/classes/ranger-plugins/doris/ranger-doris-plugin-3.0.0-SNAPSHOT.jar + /usr/local/service/ranger/ews/webapp/WEB-INF/classes/ranger-plugins/doris/mysql-connector-java-8.0.25.jar + ``` + +3. 重启 Ranger 服务。 + +4. 下载 [ranger-servicedef-doris.json](https://selectdb-doris-1308700295.cos.ap-beijing.myqcloud.com/ranger/ranger-servicedef-doris.json) + +5. 执行以下命令上传定义文件到 Ranger 服务: + + ``` + curl -u user:password -X POST \ + -H "Accept: application/json" \ + -H "Content-Type: application/json" \ + http://172.21.0.32:6080/service/plugins/definitions \ + -d...@ranger-servicedef-doris.json + ``` + + 其中用户名密码是登录 Ranger WebUI 所使用的用户名密码。 + + 服务地址端口可以再 `ranger-admin-site.xml` 配置文件的 `ranger.service.http.port` 配置项查看。 + + 如执行成功,会返回 Json 格式的服务定义,如: + + ``` + { + "id": 207, + "guid": "d3ff9e41-f9dd-4217-bb5f-3fa9996454b6", + "isEnabled": true, + "createdBy": "Admin", + "updatedBy": "Admin", + "createTime": 1705817398112, + "updateTime": 1705817398112, + "version": 1, + "name": "doris", + "displayName": "Apache Doris", + "implClass": "org.apache.ranger.services.doris.RangerServiceDoris", + "label": "Doris", + "description": "Apache Doris", + "options": { + "enableDenyAndExceptionsInPolicies": "true" + }, + ... + } + ``` + + 如想重新创建,则可以使用以下命令删除服务定义后,再重新上传: + + ``` + curl -v -u user:password -X DELETE \ + http://172.21.0.32:6080/service/plugins/definitions/207 + ``` + + 其中 `207` 是创建时返回的 id。删除前,需在 Ranger WebUI 界面删除已创建的 Doris 服务。 + + 也可以通过以下命令列举当前已添加的服务定义,以便获取 id: + + ``` + curl -v -u user:password -X GET \ + http://172.21.0.32:6080/service/plugins/definitions/ + ``` + +### 配置 Doris Ranger 插件 + +安装完毕后,打开 Ranger WebUI,可以再 Service Manger 界面中看到 Apache Doris 插件: + + + +点击插件旁边的 `+` 号添加一个 Doris 服务: + + + +Config Properties 部分参数含义如下: + +- `Username`/`Pasword`:Doris 集群的用户名密码,这里建议使用 Admin 用户。 +- `jdbc.driver_class`:连接 Doris 使用的 JDBC 驱动。`com.mysql.cj.jdbc.Driver` +- `jdbc.url`:Doris 集群的 JDBC url 连接串。`jdbc:mysql://172.21.0.101:9030?useSSL=false` +- 额外参数: + - `resource.lookup.timeout.value.in.ms`:获取元信息的超时时间,建议填写 `10000`,即 10 秒。 + +可以点击 `Test Connection` 检查是否可以联通。 + +之后点击 `Add` 添加服务。 + +之后,可以在 Service Manger 界面的 Apache Doris 插件中看到创建的服务,点击服务,即可开始配置 Ranger。 + +### 配置 Doris 集群 + +1. 在所有 FE 的 conf 目录创建 `ranger-doris-security.xml` 文件,内容如下: + + ``` + <?xml version="1.0" encoding="UTF-8"?> + <?xml-stylesheet type="text/xsl" href="configuration.xsl"?> + <configuration> + <property> + <name>ranger.plugin.doris.policy.cache.dir</name> + <value>/path/to/ranger/cache/</value> + </property> + <property> + <name>ranger.plugin.doris.policy.pollIntervalMs</name> + <value>30000</value> + </property> + <property> + <name>ranger.plugin.doris.policy.rest.client.connection.timeoutMs</name> + <value>60000</value> + </property> + <property> + <name>ranger.plugin.doris.policy.rest.client.read.timeoutMs</name> + <value>60000</value> + </property> + <property> + <name>ranger.plugin.doris.policy.rest.url</name> + <value>http://172.21.0.32:6080</value> + </property> + <property> + <name>ranger.plugin.doris.policy.source.impl</name> + <value>org.apache.ranger.admin.client.RangerAdminRESTClient</value> + </property> + <property> + <name>ranger.plugin.doris.service.name</name> + <value>doris</value> + </property> + </configuration> + ``` + + 其中需要将 `ranger.plugin.doris.policy.cache.dir` 和 `ranger.plugin.doris.policy.rest.url` 改为实际值。 + +2. 在所有 FE 的 conf 目录创建 `log4j.properties` 文件,内容如下: + + ``` + log4j.rootLogger = debug,stdout,D + + log4j.appender.stdout = org.apache.log4j.ConsoleAppender + log4j.appender.stdout.Target = System.out + log4j.appender.stdout.layout = org.apache.log4j.PatternLayout + log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n + + log4j.appender.D = org.apache.log4j.DailyRollingFileAppender + log4j.appender.D.File = /path/to/fe/log/ranger.log + log4j.appender.D.Append = true + log4j.appender.D.Threshold = INFO + log4j.appender.D.layout = org.apache.log4j.PatternLayout + log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n + ``` + + 其中 `log4j.appender.D.File` 改为实际值,用于存放 Ranger 插件的日志。 + +3. 在所有 FE 的 fe.conf 中添加配置: + + `access_controller_type=ranger-doris` + +4. 重启所有 FE 节点即可。 + +## 资源和权限 + +1. 目前 Ranger 中支持的 Doris 资源包括: + + - `Catalog` + - `Database` + - `Table` + - `Column` + - `Resource` + - `Workload Group` + +2. 目前 Ranger 中支持的 Doris 权限包括: + + - `SHOW` + - `SHOW_VIEW` + - `SHOW_RESOURCES` + - `SHOW_WORKLOAD_GROUP` + - `LOAD` + - `ALTER` + - `CREATE` + - `ALTER_CREATE` + - `ALTER_CREATE_DROP` + - `DROP` + - `SELECT` + +## 最佳实践 + +### 示例 1 + +1. 在 Doris 中创建 `user1`。 +2. 在 Doris 中,先使用 `admin` 用户创建一个 Catalog:`hive`。 +3. 在 Ranger 中创建 `user1`。 +4. 在 Ranger 中添加一个 Policy:`show_hive_catalog` + +  + +5. 使用 `user1` 登录 Doris,执行 `show catalogs`,只能看到 `hive` catalog。 +6. 在 Ranger 中添加一个 Policy:`select_hive_catalog` + +  + +7. 使用 `user1` 登录 Doris。该用户可以查看或查询 `hive` catalog 下,所有以 `tpch` 开头的 database 下的所有表。 + +## 常见问题 + +1. 暂不支持 Ranger 中的列权限、行权限以及 Data Mask 功能。 diff --git a/docs/zh-CN/docs/lakehouse/multi-catalog/hive.md b/docs/zh-CN/docs/lakehouse/multi-catalog/hive.md index b5eedd62747..8fedf622506 100644 --- a/docs/zh-CN/docs/lakehouse/multi-catalog/hive.md +++ b/docs/zh-CN/docs/lakehouse/multi-catalog/hive.md @@ -379,11 +379,15 @@ Doris 基于 Iceberg `FileIO` 接口实现了 Broker 查询 HMS Catalog Iceberg "io-impl" = "org.apache.doris.datasource.iceberg.broker.IcebergBrokerIO" ``` -## 使用 Ranger 进行权限校验 +## 集成 Apache Ranger Apache Ranger是一个用来在Hadoop平台上进行监控,启用服务,以及全方位数据安全访问管理的安全框架。 -目前doris支持ranger的库、表、列权限,不支持加密、行权限等。 +Doris 支持为指定的 External Hive Catalog 使用 Apache Ranger 进行鉴权。 + +目前支持 Ranger 的库、表、列的鉴权,暂不支持加密、行权限、Data Mask 等功能。 + +如需使用 Apache Ranger 为整个 Doris 集群服务进行鉴权,请参阅 [使用 Apache Ranger 鉴权](../../admin-manual/privilege-ldap/ranger.md) ### 环境配置 @@ -391,23 +395,14 @@ Apache Ranger是一个用来在Hadoop平台上进行监控,启用服务,以 1. 创建 Catalog 时增加: -```sql -"access_controller.properties.ranger.service.name" = "hive", -"access_controller.class" = "org.apache.doris.catalog.authorizer.RangerHiveAccessControllerFactory", -``` - ->注意: -> ->"access_controller.properties.ranger.service.name"指的是service的类型,例如hive,hdfs等 -> 如果`ranger-hive-security.xml`配置如下,"access_controller.properties.ranger.service.name"应设置为`hive`而不是`testhive` -> + ```sql + "access_controller.properties.ranger.service.name" = "hive", + "access_controller.class" = "org.apache.doris.catalog.authorizer.RangerHiveAccessControllerFactory", + ``` -```sql -<property> - <name>ranger.plugin.hive.service.name</name> - <value>testhive</value> -</property> -``` + >注意: + > + > `access_controller.properties.ranger.service.name` 指的是 service 的类型,例如 `hive`,`hdfs` 等。并不是配置文件中 `ranger.plugin.hive.service.name` 的值。 2. 配置所有 FE 环境: @@ -482,6 +477,7 @@ Apache Ranger是一个用来在Hadoop平台上进行监控,启用服务,以 4.在doris创建同名角色role1,并将role1分配给user1,user1将同时拥有db1.table1.col1和col2的查询权限 +5. Admin 和 Root 用户的权限不受Apache Ranger 的权限控制 ## 使用 Kerberos 进行认证 diff --git a/docs/zh-CN/docs/sql-manual/sql-reference/Show-Statements/SHOW-RESOURCES.md b/docs/zh-CN/docs/sql-manual/sql-reference/Show-Statements/SHOW-RESOURCES.md index 0ef51fbf85a..6969e06fd83 100644 --- a/docs/zh-CN/docs/sql-manual/sql-reference/Show-Statements/SHOW-RESOURCES.md +++ b/docs/zh-CN/docs/sql-manual/sql-reference/Show-Statements/SHOW-RESOURCES.md @@ -42,7 +42,7 @@ SHOW RESOURCES WHERE [NAME [ = "your_resource_name" | LIKE "name_matcher"]] [RESOURCETYPE = ["SPARK"]] -] +] | [LIKE "pattern"] [ORDER BY ...] [LIMIT limit][OFFSET offset]; ``` @@ -50,11 +50,12 @@ SHOW RESOURCES 说明: 1. 如果使用 NAME LIKE,则会匹配 RESOURCES 的 Name 包含 name_matcher 的Resource -2. 如果使用 NAME = ,则精确匹配指定的 Name +2. 如果使用 NAME = ,则精确匹配指定的 Name 3. 如果指定了 RESOURCETYPE,则匹配对应的 Resrouce 类型 4. 可以使用 ORDER BY 对任意列组合进行排序 5. 如果指定了 LIMIT,则显示 limit 条匹配记录。否则全部显示 6. 如果指定了 OFFSET,则从偏移量 offset 开始显示查询结果。默认情况下偏移量为0。 +7. 如果使用了 LIKE,则会忽略 WHERE 语句。 ### Example @@ -76,6 +77,12 @@ SHOW RESOURCES SHOW RESOURCES WHERE NAME = "20140102" ORDER BY `KEY` DESC; ``` +4. 使用 LIKE 进行模糊匹配 + + ```sql + SHOW RESOURCES LIKE "jdbc%"; + ``` + ### Keywords SHOW, RESOURCES diff --git a/docs/zh-CN/docs/sql-manual/sql-reference/Show-Statements/SHOW-WORKLOAD-GROUPS.md b/docs/zh-CN/docs/sql-manual/sql-reference/Show-Statements/SHOW-WORKLOAD-GROUPS.md index e6459c27b29..2415f8bcfc6 100644 --- a/docs/zh-CN/docs/sql-manual/sql-reference/Show-Statements/SHOW-WORKLOAD-GROUPS.md +++ b/docs/zh-CN/docs/sql-manual/sql-reference/Show-Statements/SHOW-WORKLOAD-GROUPS.md @@ -30,8 +30,6 @@ under the License. SHOW WORKLOAD GROUPS -<version since="dev"></version> - ### Description 该语句用于展示当前用户具有usage_priv权限的资源组。 @@ -39,7 +37,7 @@ SHOW WORKLOAD GROUPS 语法: ```sql -SHOW WORKLOAD GROUPS; +SHOW WORKLOAD GROUPS [LIKE "pattern"]; ``` 说明: @@ -63,6 +61,19 @@ SHOW WORKLOAD GROUPS; +----------+--------+--------------------------+---------+ ``` +2. 使用 LIKE 模糊匹配: + + ```sql + mysql> show workload groups like "normal%" + +----------+--------+--------------------------+---------+ + | Id | Name | Item | Value | + +----------+--------+--------------------------+---------+ + | 10343386 | normal | cpu_share | 10 | + | 10343386 | normal | memory_limit | 30% | + | 10343386 | normal | enable_memory_overcommit | true | + +----------+--------+--------------------------+---------+ + ``` + ### Keywords SHOW, WORKLOAD, GROUPS, GROUP diff --git a/fe/fe-common/src/main/java/org/apache/doris/common/Config.java b/fe/fe-common/src/main/java/org/apache/doris/common/Config.java index fe74d647d34..bc849e24bd5 100644 --- a/fe/fe-common/src/main/java/org/apache/doris/common/Config.java +++ b/fe/fe-common/src/main/java/org/apache/doris/common/Config.java @@ -2416,4 +2416,85 @@ public class Config extends ConfigBase { }) public static int label_num_threshold = 2000; + @ConfField(description = {"指定 internal catalog 的默认鉴权类", + "Specify the default authentication class of internal catalog"}, + options = {"default", "ranger-doris"}) + public static String access_controller_type = "default"; + + //========================================================================== + // begin of cloud config + //========================================================================== + + @ConfField + public static String cloud_unique_id = ""; + + public static boolean isCloudMode() { + return !cloud_unique_id.isEmpty(); + } + + public static boolean isNotCloudMode() { + return cloud_unique_id.isEmpty(); + } + + /** + * MetaService endpoint, ip:port, such as meta_service_endpoint = "192.0.0.10:8866" + */ + @ConfField + public static String meta_service_endpoint = ""; + + @ConfField(mutable = true) + public static boolean meta_service_connection_pooled = true; + + @ConfField(mutable = true) + public static int meta_service_connection_pool_size = 20; + + @ConfField(mutable = true) + public static int meta_service_rpc_retry_times = 200; + + // A connection will expire after a random time during [base, 2*base), so that the FE + // has a chance to connect to a new RS. Set zero to disable it. + @ConfField(mutable = true) + public static int meta_service_connection_age_base_minutes = 5; + + @ConfField(mutable = false) + public static boolean enable_sts_vpc = true; + + @ConfField(mutable = true) + public static int sts_duration = 3600; + + @ConfField(mutable = true) + public static int drop_rpc_retry_num = 200; + + @ConfField + public static int cloud_meta_service_rpc_failed_retry_times = 200; + + @ConfField + public static int default_get_version_from_ms_timeout_second = 3; + + @ConfField(mutable = true) + public static boolean enable_cloud_multi_replica = false; + + @ConfField(mutable = true) + public static int cloud_replica_num = 3; + + @ConfField(mutable = true) + public static int cloud_cold_read_percent = 10; // 10% + + // The original meta read lock is not enough to keep a snapshot of partition versions, + // so the execution of `createScanRangeLocations` are delayed to `Coordinator::exec`, + // to help to acquire a snapshot of partition versions. + @ConfField + public static boolean enable_cloud_snapshot_version = true; + + @ConfField + public static int cloud_cluster_check_interval_second = 10; + + @ConfField + public static String cloud_sql_server_cluster_name = "RESERVED_CLUSTER_NAME_FOR_SQL_SERVER"; + + @ConfField + public static String cloud_sql_server_cluster_id = "RESERVED_CLUSTER_ID_FOR_SQL_SERVER"; + //========================================================================== + // end of cloud config + //========================================================================== } diff --git a/fe/fe-core/src/main/cup/sql_parser.cup b/fe/fe-core/src/main/cup/sql_parser.cup index a782100ef23..ea0bfac3d51 100644 --- a/fe/fe-core/src/main/cup/sql_parser.cup +++ b/fe/fe-core/src/main/cup/sql_parser.cup @@ -4014,9 +4014,9 @@ show_param ::= {: RESULT = new ShowDbStmt(parser.wild, parser.where, null); :} - | KW_SCHEMAS KW_FROM ident:catalogName + | KW_SCHEMAS KW_FROM ident:catalogName opt_wild_where {: - RESULT = new ShowDbStmt(null, null, catalogName); + RESULT = new ShowDbStmt(parser.wild, parser.where, catalogName); :} /* Catalog */ | KW_CATALOGS opt_wild_where @@ -4172,11 +4172,11 @@ show_param ::= :} | KW_RESOURCES opt_wild_where order_by_clause:orderByClause limit_clause:limitClause {: - RESULT = new ShowResourcesStmt(parser.where, orderByClause, limitClause); + RESULT = new ShowResourcesStmt(parser.wild, parser.where, orderByClause, limitClause); :} - | KW_WORKLOAD KW_GROUPS + | KW_WORKLOAD KW_GROUPS opt_wild_where {: - RESULT = new ShowWorkloadGroupsStmt(); + RESULT = new ShowWorkloadGroupsStmt(parser.wild, parser.where); :} | KW_WORKLOAD KW_SCHEDULE KW_POLICY {: diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/AlterDatabaseRename.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/AlterDatabaseRename.java index c8e29bc9b18..2f7d2680473 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/analysis/AlterDatabaseRename.java +++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/AlterDatabaseRename.java @@ -17,7 +17,6 @@ package org.apache.doris.analysis; -import org.apache.doris.analysis.CompoundPredicate.Operator; import org.apache.doris.catalog.Env; import org.apache.doris.common.AnalysisException; import org.apache.doris.common.ErrorCode; @@ -25,9 +24,7 @@ import org.apache.doris.common.ErrorReport; import org.apache.doris.common.FeNameFormat; import org.apache.doris.common.UserException; import org.apache.doris.common.util.InternalDatabaseUtil; -import org.apache.doris.mysql.privilege.PrivBitSet; import org.apache.doris.mysql.privilege.PrivPredicate; -import org.apache.doris.mysql.privilege.Privilege; import org.apache.doris.qe.ConnectContext; import com.google.common.base.Strings; @@ -57,7 +54,7 @@ public class AlterDatabaseRename extends DdlStmt { } InternalDatabaseUtil.checkDatabase(dbName, ConnectContext.get()); if (!Env.getCurrentEnv().getAccessManager().checkDbPriv(ConnectContext.get(), dbName, - PrivPredicate.of(PrivBitSet.of(Privilege.ADMIN_PRIV, Privilege.ALTER_PRIV), Operator.OR))) { + PrivPredicate.ALTER)) { ErrorReport.reportAnalysisException(ErrorCode.ERR_DBACCESS_DENIED_ERROR, analyzer.getQualifiedUser(), dbName); } diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/AlterUserStmt.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/AlterUserStmt.java index 137699e67d9..6495f73fc94 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/analysis/AlterUserStmt.java +++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/AlterUserStmt.java @@ -22,7 +22,6 @@ import org.apache.doris.common.AnalysisException; import org.apache.doris.common.ErrorCode; import org.apache.doris.common.ErrorReport; import org.apache.doris.common.UserException; -import org.apache.doris.mysql.privilege.Auth.PrivLevel; import org.apache.doris.mysql.privilege.PasswordPolicy.FailedLoginPolicy; import org.apache.doris.mysql.privilege.PrivPredicate; import org.apache.doris.qe.ConnectContext; @@ -121,9 +120,7 @@ public class AlterUserStmt extends DdlStmt { throw new AnalysisException("Only support doing one type of operation at one time"); } - // check if current user has GRANT priv on GLOBAL or DATABASE level. - if (!Env.getCurrentEnv().getAccessManager().checkHasPriv(ConnectContext.get(), - PrivPredicate.GRANT, PrivLevel.GLOBAL, PrivLevel.DATABASE)) { + if (!Env.getCurrentEnv().getAccessManager().checkGlobalPriv(ConnectContext.get(), PrivPredicate.GRANT)) { ErrorReport.reportAnalysisException(ErrorCode.ERR_SPECIFIC_ACCESS_DENIED_ERROR, "GRANT"); } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/CreateUserStmt.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/CreateUserStmt.java index 9b69a0efbba..e3d5dc5556d 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/analysis/CreateUserStmt.java +++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/CreateUserStmt.java @@ -22,7 +22,6 @@ import org.apache.doris.common.ErrorCode; import org.apache.doris.common.ErrorReport; import org.apache.doris.common.FeNameFormat; import org.apache.doris.common.UserException; -import org.apache.doris.mysql.privilege.Auth.PrivLevel; import org.apache.doris.mysql.privilege.PrivPredicate; import org.apache.doris.mysql.privilege.Role; import org.apache.doris.qe.ConnectContext; @@ -127,9 +126,7 @@ public class CreateUserStmt extends DdlStmt { passwordOptions.analyze(); - // check if current user has GRANT priv on GLOBAL or DATABASE level. - if (!Env.getCurrentEnv().getAccessManager().checkHasPriv(ConnectContext.get(), - PrivPredicate.GRANT, PrivLevel.GLOBAL, PrivLevel.DATABASE)) { + if (!Env.getCurrentEnv().getAccessManager().checkGlobalPriv(ConnectContext.get(), PrivPredicate.GRANT)) { ErrorReport.reportAnalysisException(ErrorCode.ERR_SPECIFIC_ACCESS_DENIED_ERROR, "GRANT"); } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/RecoverDbStmt.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/RecoverDbStmt.java index 7a9a8ac6bef..f84059319c5 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/analysis/RecoverDbStmt.java +++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/RecoverDbStmt.java @@ -17,15 +17,12 @@ package org.apache.doris.analysis; -import org.apache.doris.analysis.CompoundPredicate.Operator; import org.apache.doris.catalog.Env; import org.apache.doris.common.AnalysisException; import org.apache.doris.common.ErrorCode; import org.apache.doris.common.ErrorReport; import org.apache.doris.common.UserException; -import org.apache.doris.mysql.privilege.PrivBitSet; import org.apache.doris.mysql.privilege.PrivPredicate; -import org.apache.doris.mysql.privilege.Privilege; import org.apache.doris.qe.ConnectContext; import com.google.common.base.Strings; @@ -63,8 +60,7 @@ public class RecoverDbStmt extends DdlStmt { } if (!Env.getCurrentEnv().getAccessManager().checkDbPriv(ConnectContext.get(), dbName, - PrivPredicate.of(PrivBitSet.of( - Privilege.ALTER_PRIV, Privilege.CREATE_PRIV, Privilege.ADMIN_PRIV), Operator.OR))) { + PrivPredicate.ALTER_CREATE)) { ErrorReport.reportAnalysisException( ErrorCode.ERR_DBACCESS_DENIED_ERROR, analyzer.getQualifiedUser(), dbName); } diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/RecoverPartitionStmt.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/RecoverPartitionStmt.java index 16d1efbc4e2..0bbb78104ba 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/analysis/RecoverPartitionStmt.java +++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/RecoverPartitionStmt.java @@ -17,16 +17,13 @@ package org.apache.doris.analysis; -import org.apache.doris.analysis.CompoundPredicate.Operator; import org.apache.doris.catalog.Env; import org.apache.doris.common.AnalysisException; import org.apache.doris.common.ErrorCode; import org.apache.doris.common.ErrorReport; import org.apache.doris.common.UserException; import org.apache.doris.common.util.Util; -import org.apache.doris.mysql.privilege.PrivBitSet; import org.apache.doris.mysql.privilege.PrivPredicate; -import org.apache.doris.mysql.privilege.Privilege; import org.apache.doris.qe.ConnectContext; import com.google.common.base.Strings; @@ -72,8 +69,7 @@ public class RecoverPartitionStmt extends DdlStmt { // disallow external catalog Util.prohibitExternalCatalog(dbTblName.getCtl(), this.getClass().getSimpleName()); if (!Env.getCurrentEnv().getAccessManager().checkTblPriv(ConnectContext.get(), dbTblName.getDb(), - dbTblName.getTbl(), PrivPredicate.of(PrivBitSet.of( - Privilege.ALTER_PRIV, Privilege.CREATE_PRIV, Privilege.ADMIN_PRIV), Operator.OR))) { + dbTblName.getTbl(), PrivPredicate.ALTER_CREATE)) { ErrorReport.reportAnalysisException(ErrorCode.ERR_TABLEACCESS_DENIED_ERROR, "RECOVERY", ConnectContext.get().getQualifiedUser(), ConnectContext.get().getRemoteIP(), diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/RecoverTableStmt.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/RecoverTableStmt.java index 24b9b294f0b..528a0e284ae 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/analysis/RecoverTableStmt.java +++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/RecoverTableStmt.java @@ -17,16 +17,13 @@ package org.apache.doris.analysis; -import org.apache.doris.analysis.CompoundPredicate.Operator; import org.apache.doris.catalog.Env; import org.apache.doris.common.AnalysisException; import org.apache.doris.common.ErrorCode; import org.apache.doris.common.ErrorReport; import org.apache.doris.common.UserException; import org.apache.doris.common.util.Util; -import org.apache.doris.mysql.privilege.PrivBitSet; import org.apache.doris.mysql.privilege.PrivPredicate; -import org.apache.doris.mysql.privilege.Privilege; import org.apache.doris.qe.ConnectContext; import com.google.common.base.Strings; @@ -67,13 +64,11 @@ public class RecoverTableStmt extends DdlStmt { Util.prohibitExternalCatalog(dbTblName.getCtl(), this.getClass().getSimpleName()); if (!Env.getCurrentEnv().getAccessManager().checkTblPriv( - ConnectContext.get(), dbTblName.getDb(), dbTblName.getTbl(), PrivPredicate.of( - PrivBitSet.of(Privilege.ALTER_PRIV, Privilege.CREATE_PRIV, Privilege.ADMIN_PRIV), - Operator.OR))) { + ConnectContext.get(), dbTblName.getDb(), dbTblName.getTbl(), PrivPredicate.ALTER_CREATE)) { ErrorReport.reportAnalysisException(ErrorCode.ERR_TABLEACCESS_DENIED_ERROR, "RECOVERY", - ConnectContext.get().getQualifiedUser(), - ConnectContext.get().getRemoteIP(), - dbTblName.getDb() + ": " + dbTblName.getTbl()); + ConnectContext.get().getQualifiedUser(), + ConnectContext.get().getRemoteIP(), + dbTblName.getDb() + ": " + dbTblName.getTbl()); } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/ShowCreateDbStmt.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/ShowCreateDbStmt.java index 4add663bc5a..0cadde094b8 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/analysis/ShowCreateDbStmt.java +++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/ShowCreateDbStmt.java @@ -17,7 +17,6 @@ package org.apache.doris.analysis; -import org.apache.doris.analysis.CompoundPredicate.Operator; import org.apache.doris.catalog.Column; import org.apache.doris.catalog.Env; import org.apache.doris.catalog.ScalarType; @@ -25,9 +24,7 @@ import org.apache.doris.common.AnalysisException; import org.apache.doris.common.ErrorCode; import org.apache.doris.common.ErrorReport; import org.apache.doris.common.UserException; -import org.apache.doris.mysql.privilege.PrivBitSet; import org.apache.doris.mysql.privilege.PrivPredicate; -import org.apache.doris.mysql.privilege.Privilege; import org.apache.doris.qe.ConnectContext; import org.apache.doris.qe.ShowResultSetMetaData; @@ -61,13 +58,9 @@ public class ShowCreateDbStmt extends ShowStmt { } if (!Env.getCurrentEnv().getAccessManager().checkDbPriv(ConnectContext.get(), db, - PrivPredicate.of(PrivBitSet.of(Privilege.ADMIN_PRIV, - Privilege.ALTER_PRIV, - Privilege.CREATE_PRIV, - Privilege.DROP_PRIV), - Operator.OR))) { + PrivPredicate.ALTER_CREATE_DROP)) { ErrorReport.reportAnalysisException(ErrorCode.ERR_DBACCESS_DENIED_ERROR, - ConnectContext.get().getQualifiedUser(), db); + ConnectContext.get().getQualifiedUser(), db); } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/ShowResourcesStmt.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/ShowResourcesStmt.java index f009b8d6875..97393334679 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/analysis/ShowResourcesStmt.java +++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/ShowResourcesStmt.java @@ -43,6 +43,7 @@ import java.util.Set; public class ShowResourcesStmt extends ShowStmt { private static final Logger LOG = LogManager.getLogger(ShowResourcesStmt.class); + private String pattern; private Expr whereClause; private LimitElement limitElement; private List<OrderByElement> orderByElements; @@ -56,7 +57,9 @@ public class ShowResourcesStmt extends ShowStmt { public ShowResourcesStmt() { } - public ShowResourcesStmt(Expr labelExpr, List<OrderByElement> orderByElements, LimitElement limitElement) { + public ShowResourcesStmt(String pattern, Expr labelExpr, + List<OrderByElement> orderByElements, LimitElement limitElement) { + this.pattern = pattern; this.whereClause = labelExpr; this.orderByElements = orderByElements; this.limitElement = limitElement; @@ -66,6 +69,10 @@ public class ShowResourcesStmt extends ShowStmt { this.isAccurateMatch = false; } + public String getPattern() { + return pattern; + } + public ArrayList<OrderByPair> getOrderByPairs() { return this.orderByPairs; } @@ -107,22 +114,26 @@ public class ShowResourcesStmt extends ShowStmt { public void analyze(Analyzer analyzer) throws UserException { super.analyze(analyzer); - // analyze where clause - boolean isValid; - if (whereClause instanceof CompoundPredicate) { - CompoundPredicate cp = (CompoundPredicate) whereClause; - if (cp.getOp() != org.apache.doris.analysis.CompoundPredicate.Operator.AND) { - throw new AnalysisException("Only allow compound predicate with operator AND"); + // If pattern is null, analyze where clause, + // otherwise, ignore where clause. + if (this.pattern == null) { + // analyze where clause + boolean isValid; + if (whereClause instanceof CompoundPredicate) { + CompoundPredicate cp = (CompoundPredicate) whereClause; + if (cp.getOp() != org.apache.doris.analysis.CompoundPredicate.Operator.AND) { + throw new AnalysisException("Only allow compound predicate with operator AND"); + } + isValid = isWhereClauseValid(cp.getChild(0)) && isWhereClauseValid(cp.getChild(1)); + } else { + isValid = isWhereClauseValid(whereClause); } - isValid = isWhereClauseValid(cp.getChild(0)) && isWhereClauseValid(cp.getChild(1)); - } else { - isValid = isWhereClauseValid(whereClause); - } - if (!isValid) { - throw new AnalysisException("Where clause should looks like: NAME = \"your_resource_name\"," - + " or NAME LIKE \"matcher\", " + " or RESOURCETYPE = \"resource_type\", " - + " or compound predicate with operator AND"); + if (!isValid) { + throw new AnalysisException("Where clause should looks like: NAME = \"your_resource_name\"," + + " or NAME LIKE \"matcher\", " + " or RESOURCETYPE = \"resource_type\", " + + " or compound predicate with operator AND"); + } } // order by @@ -203,9 +214,12 @@ public class ShowResourcesStmt extends ShowStmt { public String toSql() { StringBuilder sb = new StringBuilder(); sb.append("SHOW RESOURCES"); - - if (whereClause != null) { - sb.append(" WHERE ").append(whereClause.toSql()); + if (pattern != null) { + sb.append(" LIKE '").append(pattern).append("'"); + } else { + if (whereClause != null) { + sb.append(" WHERE ").append(whereClause.toSql()); + } } // Order By clause @@ -225,7 +239,6 @@ public class ShowResourcesStmt extends ShowStmt { if (getOffset() != -1L) { sb.append(" OFFSET ").append(getOffset()); } - return sb.toString(); } diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/ShowWorkloadGroupsStmt.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/ShowWorkloadGroupsStmt.java index bb0d0bb5669..c802411be81 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/analysis/ShowWorkloadGroupsStmt.java +++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/ShowWorkloadGroupsStmt.java @@ -19,6 +19,7 @@ package org.apache.doris.analysis; import org.apache.doris.catalog.Column; import org.apache.doris.catalog.ScalarType; +import org.apache.doris.common.AnalysisException; import org.apache.doris.common.UserException; import org.apache.doris.qe.ConnectContext; import org.apache.doris.qe.ShowResultSetMetaData; @@ -26,16 +27,34 @@ import org.apache.doris.resource.workloadgroup.WorkloadGroupMgr; public class ShowWorkloadGroupsStmt extends ShowStmt { - public ShowWorkloadGroupsStmt() {} + private String pattern; + // TODO: not supported yet + private Expr whereClause; + + public ShowWorkloadGroupsStmt(String pattern, Expr where) { + this.pattern = pattern; + this.whereClause = where; + } + + public String getPattern() { + return pattern; + } @Override public void analyze(Analyzer analyzer) throws UserException { super.analyze(analyzer); + if (this.whereClause != null) { + throw new AnalysisException("Where clause is not supported in show workload groups statement"); + } } @Override public String toSql() { - return "SHOW RESOURCE GROUPS"; + String sql = "SHOW WORKLOAD GROUPS"; + if (this.pattern != null) { + sql += " LIKE '" + pattern + "'"; + } + return sql; } @Override diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/ResourceMgr.java b/fe/fe-core/src/main/java/org/apache/doris/catalog/ResourceMgr.java index 5696f1cd258..0a0b43adf60 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/catalog/ResourceMgr.java +++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/ResourceMgr.java @@ -23,6 +23,7 @@ import org.apache.doris.analysis.DropResourceStmt; import org.apache.doris.catalog.Resource.ResourceType; import org.apache.doris.common.AnalysisException; import org.apache.doris.common.DdlException; +import org.apache.doris.common.PatternMatcher; import org.apache.doris.common.io.Text; import org.apache.doris.common.io.Writable; import org.apache.doris.common.proc.BaseProcResult; @@ -173,7 +174,8 @@ public class ResourceMgr implements Writable { .collect(Collectors.toList()); } - public List<List<Comparable>> getResourcesInfo(String name, boolean accurateMatch, Set<String> typeSets) { + public List<List<Comparable>> getResourcesInfo(PatternMatcher matcher, + String name, boolean accurateMatch, Set<String> typeSets) { List<List<String>> targetRows = procNode.fetchResult().getRows(); List<List<Comparable>> returnRows = Lists.newArrayList(); @@ -185,6 +187,10 @@ public class ResourceMgr implements Writable { String resourceName = row.get(0); String resourceType = row.get(1); + if (matcher != null && !matcher.match(resourceName)) { + continue; + } + if (name != null) { if (accurateMatch && !resourceName.equals(name)) { continue; diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/ranger/RangerAccessController.java b/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/ranger/RangerAccessController.java new file mode 100644 index 00000000000..1712b912cd4 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/ranger/RangerAccessController.java @@ -0,0 +1,69 @@ +// 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. + +package org.apache.doris.catalog.authorizer.ranger; + +import org.apache.doris.common.AuthorizationException; +import org.apache.doris.mysql.privilege.CatalogAccessController; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.ranger.plugin.policyengine.RangerAccessRequestImpl; +import org.apache.ranger.plugin.policyengine.RangerAccessResult; + +import java.util.Collection; + +public abstract class RangerAccessController implements CatalogAccessController { + private static final Logger LOG = LogManager.getLogger(RangerAccessController.class); + + protected static final String CLIENT_TYPE_DORIS = "doris"; + + protected static boolean checkRequestResult(RangerAccessRequestImpl request, + RangerAccessResult result, String name) { + if (result == null) { + LOG.warn("Error getting authorizer result, please check your ranger config. Make sure " + + "ranger policy engine is initialized. Request: {}", request); + return false; + } + + if (result.getIsAllowed()) { + LOG.debug("request {} match policy {}", request, result.getPolicyId()); + return true; + } else { + LOG.debug(String.format( + "Permission denied: user [%s] does not have privilege for [%s] command on [%s]", + result.getAccessRequest().getUser(), name, + result.getAccessRequest().getResource().getAsString())); + return false; + } + } + + + public static void checkRequestResults(Collection<RangerAccessResult> results, String name) + throws AuthorizationException { + for (RangerAccessResult result : results) { + LOG.debug("request {} match policy {}", result.getAccessRequest(), result.getPolicyId()); + if (!result.getIsAllowed()) { + LOG.debug(result.getReason()); + throw new AuthorizationException(String.format( + "Permission denied: user [%s] does not have privilege for [%s] command on [%s]", + result.getAccessRequest().getUser(), name, + result.getAccessRequest().getResource().getAsString().replaceAll("/", "."))); + } + } + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/ranger/doris/DorisAccessType.java b/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/ranger/doris/DorisAccessType.java new file mode 100644 index 00000000000..e71d6847e8a --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/ranger/doris/DorisAccessType.java @@ -0,0 +1,80 @@ +// 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. + +package org.apache.doris.catalog.authorizer.ranger.doris; + +import org.apache.doris.mysql.privilege.PrivPredicate; + +// Same as defined in PrivPredicate.java +public enum DorisAccessType { + SHOW, + SHOW_VIEW, + SHOW_RESOURCES, + SHOW_WORKLOAD_GROUP, + GRANT, + ADMIN, + LOAD, + ALTER, + CREATE, + ALTER_CREATE, + ALTER_CREATE_DROP, + DROP, + SELECT, + OPERATOR, + USAGE, + ALL, + NODE, + NONE; + + public static DorisAccessType toAccessType(PrivPredicate priv) { + if (priv == PrivPredicate.SHOW) { + return SHOW; + } else if (priv == PrivPredicate.SHOW_VIEW) { + return SHOW_VIEW; + } else if (priv == PrivPredicate.SHOW_RESOURCES) { + return SHOW_RESOURCES; + } else if (priv == PrivPredicate.SHOW_WORKLOAD_GROUP) { + return SHOW_WORKLOAD_GROUP; + } else if (priv == PrivPredicate.GRANT) { + return GRANT; + } else if (priv == PrivPredicate.ADMIN) { + return ADMIN; + } else if (priv == PrivPredicate.LOAD) { + return LOAD; + } else if (priv == PrivPredicate.ALTER) { + return ALTER; + } else if (priv == PrivPredicate.CREATE) { + return CREATE; + } else if (priv == PrivPredicate.ALTER_CREATE) { + return ALTER_CREATE; + } else if (priv == PrivPredicate.ALTER_CREATE_DROP) { + return ALTER_CREATE_DROP; + } else if (priv == PrivPredicate.DROP) { + return DROP; + } else if (priv == PrivPredicate.SELECT) { + return SELECT; + } else if (priv == PrivPredicate.OPERATOR) { + return OPERATOR; + } else if (priv == PrivPredicate.USAGE) { + return USAGE; + } else if (priv == PrivPredicate.ALL) { + return ALL; + } else { + return NONE; + } + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/HiveObjectType.java b/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/ranger/doris/DorisObjectType.java similarity index 83% copy from fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/HiveObjectType.java copy to fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/ranger/doris/DorisObjectType.java index 94f4f7e6842..dd7002117ef 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/HiveObjectType.java +++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/ranger/doris/DorisObjectType.java @@ -15,8 +15,8 @@ // specific language governing permissions and limitations // under the License. -package org.apache.doris.catalog.authorizer; +package org.apache.doris.catalog.authorizer.ranger.doris; -public enum HiveObjectType { - NONE, DATABASE, TABLE, VIEW, INDEX, COLUMN, FUNCTION; +public enum DorisObjectType { + NONE, CATALOG, DATABASE, TABLE, COLUMN, RESOURCE, WORKLOAD_GROUP } diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/ranger/doris/RangerDorisAccessController.java b/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/ranger/doris/RangerDorisAccessController.java new file mode 100644 index 00000000000..44e2686fe32 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/ranger/doris/RangerDorisAccessController.java @@ -0,0 +1,181 @@ +// 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. + +package org.apache.doris.catalog.authorizer.ranger.doris; + +import org.apache.doris.analysis.UserIdentity; +import org.apache.doris.catalog.Env; +import org.apache.doris.catalog.authorizer.ranger.RangerAccessController; +import org.apache.doris.catalog.authorizer.ranger.hive.RangerHiveResource; +import org.apache.doris.cluster.ClusterNamespace; +import org.apache.doris.common.AuthorizationException; +import org.apache.doris.mysql.privilege.PrivPredicate; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.ranger.plugin.policyengine.RangerAccessRequest; +import org.apache.ranger.plugin.policyengine.RangerAccessRequestImpl; +import org.apache.ranger.plugin.policyengine.RangerAccessResult; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +public class RangerDorisAccessController extends RangerAccessController { + private static final Logger LOG = LogManager.getLogger(RangerDorisAccessController.class); + private RangerDorisPlugin dorisPlugin; + // private static ScheduledThreadPoolExecutor logFlushTimer = ThreadPoolManager.newDaemonScheduledThreadPool(1, + // "ranger-doris-audit-log-flusher-timer", true); + // private RangerHiveAuditHandler auditHandler; + + public RangerDorisAccessController(String serviceName) { + dorisPlugin = new RangerDorisPlugin(serviceName); + // auditHandler = new RangerHiveAuditHandler(dorisPlugin.getConfig()); + // start a timed log flusher + // logFlushTimer.scheduleAtFixedRate(new RangerHiveAuditLogFlusher(auditHandler), 10, 20L, TimeUnit.SECONDS); + } + + private RangerAccessRequestImpl createRequest(UserIdentity currentUser, DorisAccessType accessType) { + RangerAccessRequestImpl request = new RangerAccessRequestImpl(); + request.setUser(ClusterNamespace.getNameFromFullName(currentUser.getQualifiedUser())); + Set<String> roles = Env.getCurrentEnv().getAuth().getRolesByUser(currentUser, false); + request.setUserRoles(roles.stream().map(role -> ClusterNamespace.getNameFromFullName(role)).collect( + Collectors.toSet())); + + request.setAction(accessType.name()); + request.setAccessType(accessType.name()); + request.setClientIPAddress(currentUser.getHost()); + request.setClusterType(CLIENT_TYPE_DORIS); + request.setClientType(CLIENT_TYPE_DORIS); + request.setAccessTime(new Date()); + + return request; + } + + private void checkPrivileges(UserIdentity currentUser, DorisAccessType accessType, + List<RangerDorisResource> dorisResources) throws AuthorizationException { + List<RangerAccessRequest> requests = new ArrayList<>(); + for (RangerDorisResource resource : dorisResources) { + RangerAccessRequestImpl request = createRequest(currentUser, accessType); + request.setResource(resource); + requests.add(request); + } + + Collection<RangerAccessResult> results = dorisPlugin.isAccessAllowed(requests); + checkRequestResults(results, accessType.name()); + } + + private boolean checkPrivilege(UserIdentity currentUser, DorisAccessType accessType, + RangerDorisResource resource) { + RangerAccessRequestImpl request = createRequest(currentUser, accessType); + request.setResource(resource); + + if (LOG.isDebugEnabled()) { + LOG.debug("ranger request: {}", request); + } + + RangerAccessResult result = dorisPlugin.isAccessAllowed(request); + return checkRequestResult(request, result, accessType.name()); + } + + public String getFilterExpr(UserIdentity currentUser, DorisAccessType accessType, + RangerHiveResource resource) { + RangerAccessRequestImpl request = createRequest(currentUser, accessType); + request.setResource(resource); + RangerAccessResult result = dorisPlugin.isAccessAllowed(request); + + return result.getFilterExpr(); + } + + public void getColumnMask(UserIdentity currentUser, DorisAccessType accessType, + RangerHiveResource resource) { + RangerAccessRequestImpl request = createRequest(currentUser, accessType); + request.setResource(resource); + RangerAccessResult result = dorisPlugin.isAccessAllowed(request); + + LOG.debug(String.format("maskType: %s, maskTypeDef: %s, maskedValue: %s", result.getMaskType(), + result.getMaskTypeDef(), result.getMaskedValue())); + } + + @Override + public boolean checkGlobalPriv(UserIdentity currentUser, PrivPredicate wanted) { + // ranger does not support global privilege, + // use internal privilege check instead + return Env.getCurrentEnv().getAuth().checkGlobalPriv(currentUser, wanted); + } + + @Override + public boolean checkCtlPriv(UserIdentity currentUser, String ctl, PrivPredicate wanted) { + RangerDorisResource resource = new RangerDorisResource(DorisObjectType.CATALOG, ctl); + return checkPrivilege(currentUser, DorisAccessType.toAccessType(wanted), resource); + } + + @Override + public boolean checkDbPriv(UserIdentity currentUser, String ctl, String db, PrivPredicate wanted) { + RangerDorisResource resource = new RangerDorisResource(DorisObjectType.DATABASE, ctl, + ClusterNamespace.getNameFromFullName(db)); + return checkPrivilege(currentUser, DorisAccessType.toAccessType(wanted), resource); + } + + @Override + public boolean checkTblPriv(UserIdentity currentUser, String ctl, String db, String tbl, PrivPredicate wanted) { + RangerDorisResource resource = new RangerDorisResource(DorisObjectType.TABLE, + ctl, ClusterNamespace.getNameFromFullName(db), tbl); + return checkPrivilege(currentUser, DorisAccessType.toAccessType(wanted), resource); + } + + @Override + public void checkColsPriv(UserIdentity currentUser, String ctl, String db, String tbl, Set<String> cols, + PrivPredicate wanted) throws AuthorizationException { + List<RangerDorisResource> resources = new ArrayList<>(); + for (String col : cols) { + RangerDorisResource resource = new RangerDorisResource(DorisObjectType.COLUMN, + ctl, ClusterNamespace.getNameFromFullName(db), tbl, col); + resources.add(resource); + } + + checkPrivileges(currentUser, DorisAccessType.toAccessType(wanted), resources); + } + + @Override + public boolean checkResourcePriv(UserIdentity currentUser, String resourceName, PrivPredicate wanted) { + RangerDorisResource resource = new RangerDorisResource(DorisObjectType.RESOURCE, resourceName); + return checkPrivilege(currentUser, DorisAccessType.toAccessType(wanted), resource); + } + + @Override + public boolean checkWorkloadGroupPriv(UserIdentity currentUser, String workloadGroupName, PrivPredicate wanted) { + RangerDorisResource resource = new RangerDorisResource(DorisObjectType.WORKLOAD_GROUP, workloadGroupName); + return checkPrivilege(currentUser, DorisAccessType.toAccessType(wanted), resource); + } + + // For test only + public static void main(String[] args) { + RangerDorisAccessController ac = new RangerDorisAccessController("doris"); + UserIdentity user = new UserIdentity("user1", "127.0.0.1"); + user.setIsAnalyzed(); + boolean res = ac.checkDbPriv(user, "internal", "db1", PrivPredicate.SHOW); + System.out.println("res: " + res); + user = new UserIdentity("user2", "127.0.0.1"); + user.setIsAnalyzed(); + res = ac.checkTblPriv(user, "internal", "db1", "tbl1", PrivPredicate.SELECT); + System.out.println("res: " + res); + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/RangerHivePlugin.java b/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/ranger/doris/RangerDorisPlugin.java similarity index 84% copy from fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/RangerHivePlugin.java copy to fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/ranger/doris/RangerDorisPlugin.java index b562eee5a90..34f098c8df8 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/RangerHivePlugin.java +++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/ranger/doris/RangerDorisPlugin.java @@ -15,12 +15,12 @@ // specific language governing permissions and limitations // under the License. -package org.apache.doris.catalog.authorizer; +package org.apache.doris.catalog.authorizer.ranger.doris; import org.apache.ranger.plugin.service.RangerBasePlugin; -public class RangerHivePlugin extends RangerBasePlugin { - public RangerHivePlugin(String serviceName) { +public class RangerDorisPlugin extends RangerBasePlugin { + public RangerDorisPlugin(String serviceName) { super(serviceName, null, null); super.init(); } diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/ranger/doris/RangerDorisResource.java b/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/ranger/doris/RangerDorisResource.java new file mode 100644 index 00000000000..648a76acdb3 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/ranger/doris/RangerDorisResource.java @@ -0,0 +1,80 @@ +// 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. + +package org.apache.doris.catalog.authorizer.ranger.doris; + +import org.apache.ranger.plugin.policyengine.RangerAccessResourceImpl; + +public class RangerDorisResource extends RangerAccessResourceImpl { + public static final String KEY_CATALOG = "catalog"; + public static final String KEY_DATABASE = "database"; + public static final String KEY_TABLE = "table"; + public static final String KEY_COLUMN = "column"; + public static final String KEY_RESOURCE = "resource"; + public static final String KEY_WORKLOAD_GROUP = "workload_group"; + + // FirstLevelResource => Catalog / Resource / WorkloadGroup + // SecondLevelResource => Database + // ThirdLevelResource => Table + // FourthLevelResource => Column + public RangerDorisResource(DorisObjectType objectType, String firstLevelResource) { + this(objectType, firstLevelResource, null, null, null); + } + + public RangerDorisResource(DorisObjectType objectType, String firstLevelResource, String secondLevelResource) { + this(objectType, firstLevelResource, secondLevelResource, null, null); + } + + public RangerDorisResource(DorisObjectType objectType, String firstLevelResource, String secondLevelResource, + String thirdLevelResource) { + this(objectType, firstLevelResource, secondLevelResource, thirdLevelResource, null); + } + + public RangerDorisResource(DorisObjectType objectType, String firstLevelResource, String secondLevelResource, + String thirdLevelResource, String fourthLevelResource) { + // set essential info according to objectType + switch (objectType) { + case CATALOG: + setValue(KEY_CATALOG, firstLevelResource); + break; + case DATABASE: + setValue(KEY_CATALOG, firstLevelResource); + setValue(KEY_DATABASE, secondLevelResource); + break; + case TABLE: + setValue(KEY_CATALOG, firstLevelResource); + setValue(KEY_DATABASE, secondLevelResource); + setValue(KEY_TABLE, thirdLevelResource); + break; + case COLUMN: + setValue(KEY_CATALOG, firstLevelResource); + setValue(KEY_DATABASE, secondLevelResource); + setValue(KEY_TABLE, thirdLevelResource); + setValue(KEY_COLUMN, fourthLevelResource); + break; + case RESOURCE: + setValue(KEY_RESOURCE, firstLevelResource); + break; + case WORKLOAD_GROUP: + setValue(KEY_WORKLOAD_GROUP, firstLevelResource); + break; + case NONE: + default: + break; + } + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/HiveAccessType.java b/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/ranger/hive/HiveAccessType.java similarity index 94% rename from fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/HiveAccessType.java rename to fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/ranger/hive/HiveAccessType.java index 233f3c463a5..6f7d3e18834 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/HiveAccessType.java +++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/ranger/hive/HiveAccessType.java @@ -15,7 +15,7 @@ // specific language governing permissions and limitations // under the License. -package org.apache.doris.catalog.authorizer; +package org.apache.doris.catalog.authorizer.ranger.hive; public enum HiveAccessType { NONE, CREATE, ALTER, DROP, INDEX, LOCK, SELECT, UPDATE, USE, READ, WRITE, ALL, SERVICEADMIN, diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/HiveObjectType.java b/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/ranger/hive/HiveObjectType.java similarity index 93% rename from fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/HiveObjectType.java rename to fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/ranger/hive/HiveObjectType.java index 94f4f7e6842..73510fd05ba 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/HiveObjectType.java +++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/ranger/hive/HiveObjectType.java @@ -15,7 +15,7 @@ // specific language governing permissions and limitations // under the License. -package org.apache.doris.catalog.authorizer; +package org.apache.doris.catalog.authorizer.ranger.hive; public enum HiveObjectType { NONE, DATABASE, TABLE, VIEW, INDEX, COLUMN, FUNCTION; diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/RangerHiveAccessController.java b/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/ranger/hive/RangerHiveAccessController.java similarity index 80% rename from fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/RangerHiveAccessController.java rename to fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/ranger/hive/RangerHiveAccessController.java index bd7abe652d6..bbe32735776 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/RangerHiveAccessController.java +++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/ranger/hive/RangerHiveAccessController.java @@ -15,16 +15,18 @@ // specific language governing permissions and limitations // under the License. -package org.apache.doris.catalog.authorizer; +package org.apache.doris.catalog.authorizer.ranger.hive; import org.apache.doris.analysis.UserIdentity; import org.apache.doris.catalog.Env; +import org.apache.doris.catalog.authorizer.ranger.RangerAccessController; import org.apache.doris.cluster.ClusterNamespace; import org.apache.doris.common.AuthorizationException; import org.apache.doris.common.ThreadPoolManager; -import org.apache.doris.mysql.privilege.CatalogAccessController; +import org.apache.doris.datasource.InternalCatalog; import org.apache.doris.mysql.privilege.PrivPredicate; +import com.google.common.collect.Maps; import org.apache.hadoop.hive.ql.security.authorization.plugin.HiveAccessControlException; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -43,8 +45,7 @@ import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; -public class RangerHiveAccessController implements CatalogAccessController { - public static final String CLIENT_TYPE_DORIS = "doris"; +public class RangerHiveAccessController extends RangerAccessController { private static final Logger LOG = LogManager.getLogger(RangerHiveAccessController.class); private static ScheduledThreadPoolExecutor logFlushTimer = ThreadPoolManager.newDaemonScheduledThreadPool(1, "ranger-hive-audit-log-flusher-timer", true); @@ -86,21 +87,11 @@ public class RangerHiveAccessController implements CatalogAccessController { for (RangerHiveResource resource : hiveResources) { RangerAccessRequestImpl request = createRequest(currentUser, accessType); request.setResource(resource); - requests.add(request); } Collection<RangerAccessResult> results = hivePlugin.isAccessAllowed(requests, auditHandler); - for (RangerAccessResult result : results) { - LOG.debug(String.format("request %s match policy %s", result.getAccessRequest(), result.getPolicyId())); - if (!result.getIsAllowed()) { - LOG.debug(result.getReason()); - throw new AuthorizationException(String.format( - "Permission denied: user [%s] does not have privilege for [%s] command on [%s]", - result.getAccessRequest().getUser(), accessType.name(), - result.getAccessRequest().getResource().getAsString().replaceAll("/", "."))); - } - } + checkRequestResults(results, accessType.name()); } private boolean checkPrivilege(UserIdentity currentUser, HiveAccessType accessType, @@ -109,23 +100,7 @@ public class RangerHiveAccessController implements CatalogAccessController { request.setResource(resource); RangerAccessResult result = hivePlugin.isAccessAllowed(request, auditHandler); - - if (result == null) { - LOG.warn(String.format("Error getting authorizer result, please check your ranger config. Make sure " - + "ranger policy engine is initialized. Request: %s", request)); - return false; - } - - if (result.getIsAllowed()) { - LOG.debug(String.format("request %s match policy %s", request, result.getPolicyId())); - return true; - } else { - LOG.debug(String.format( - "Permission denied: user [%s] does not have privilege for [%s] command on [%s]", - result.getAccessRequest().getUser(), accessType.name(), - result.getAccessRequest().getResource().getAsString())); - return false; - } + return checkRequestResult(request, result, accessType.name()); } public String getFilterExpr(UserIdentity currentUser, HiveAccessType accessType, @@ -147,7 +122,7 @@ public class RangerHiveAccessController implements CatalogAccessController { result.getMaskTypeDef(), result.getMaskedValue())); } - public HiveAccessType convertToAccessType(PrivPredicate predicate) { + private HiveAccessType convertToAccessType(PrivPredicate predicate) { if (predicate == PrivPredicate.SHOW) { return HiveAccessType.USE; } else if (predicate == PrivPredicate.SELECT) { @@ -167,6 +142,14 @@ public class RangerHiveAccessController implements CatalogAccessController { } } + @Override + public boolean checkGlobalPriv(UserIdentity currentUser, PrivPredicate wanted) { + // hive ranger plugin does not support global privilege + // use internal access controller to check + return Env.getCurrentEnv().getAccessManager().getAccessControllerOrDefault( + InternalCatalog.INTERNAL_CATALOG_NAME).checkGlobalPriv(currentUser, wanted); + } + @Override public boolean checkCtlPriv(UserIdentity currentUser, String ctl, PrivPredicate wanted) { return true; @@ -198,4 +181,27 @@ public class RangerHiveAccessController implements CatalogAccessController { checkPrivileges(currentUser, convertToAccessType(wanted), resources); } + + @Override + public boolean checkResourcePriv(UserIdentity currentUser, String resourceName, PrivPredicate wanted) { + return false; + } + + @Override + public boolean checkWorkloadGroupPriv(UserIdentity currentUser, String workloadGroupName, PrivPredicate wanted) { + return false; + } + + // For test only + public static void main(String[] args) { + Map<String, String> properties = Maps.newHashMap(); + properties.put("ranger.service.name", "hive"); + RangerHiveAccessController ac = new RangerHiveAccessController(properties); + UserIdentity user = new UserIdentity("user1", "127.0.0.1"); + user.setIsAnalyzed(); + boolean res = ac.checkDbPriv(user, "hive", "tpcds_bin_partitioned_orc_1", PrivPredicate.SHOW); + System.out.println("res: " + res); + res = ac.checkTblPriv(user, "internal", "tpch1", "customer", PrivPredicate.SELECT); + System.out.println("res: " + res); + } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/RangerHiveAccessControllerFactory.java b/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/ranger/hive/RangerHiveAccessControllerFactory.java similarity index 95% rename from fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/RangerHiveAccessControllerFactory.java rename to fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/ranger/hive/RangerHiveAccessControllerFactory.java index 3dc5e1c243c..3e9f11d9f8e 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/RangerHiveAccessControllerFactory.java +++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/ranger/hive/RangerHiveAccessControllerFactory.java @@ -15,7 +15,7 @@ // specific language governing permissions and limitations // under the License. -package org.apache.doris.catalog.authorizer; +package org.apache.doris.catalog.authorizer.ranger.hive; import org.apache.doris.mysql.privilege.AccessControllerFactory; import org.apache.doris.mysql.privilege.CatalogAccessController; diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/RangerHiveAuditHandler.java b/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/ranger/hive/RangerHiveAuditHandler.java similarity index 99% rename from fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/RangerHiveAuditHandler.java rename to fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/ranger/hive/RangerHiveAuditHandler.java index f9a5ac2babd..2b76c04f261 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/RangerHiveAuditHandler.java +++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/ranger/hive/RangerHiveAuditHandler.java @@ -15,7 +15,7 @@ // specific language governing permissions and limitations // under the License. -package org.apache.doris.catalog.authorizer; +package org.apache.doris.catalog.authorizer.ranger.hive; import org.apache.commons.lang3.StringUtils; import org.apache.hadoop.conf.Configuration; diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/RangerHiveAuditLogFlusher.java b/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/ranger/hive/RangerHiveAuditLogFlusher.java similarity index 96% rename from fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/RangerHiveAuditLogFlusher.java rename to fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/ranger/hive/RangerHiveAuditLogFlusher.java index 45cdfd44830..e8afda11462 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/RangerHiveAuditLogFlusher.java +++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/ranger/hive/RangerHiveAuditLogFlusher.java @@ -15,7 +15,7 @@ // specific language governing permissions and limitations // under the License. -package org.apache.doris.catalog.authorizer; +package org.apache.doris.catalog.authorizer.ranger.hive; import lombok.extern.slf4j.Slf4j; diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/RangerHiveAuthorizerProvider.java b/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/ranger/hive/RangerHiveAuthorizerProvider.java similarity index 96% rename from fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/RangerHiveAuthorizerProvider.java rename to fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/ranger/hive/RangerHiveAuthorizerProvider.java index 5415496e161..32d72c13296 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/RangerHiveAuthorizerProvider.java +++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/ranger/hive/RangerHiveAuthorizerProvider.java @@ -15,7 +15,7 @@ // specific language governing permissions and limitations // under the License. -package org.apache.doris.catalog.authorizer; +package org.apache.doris.catalog.authorizer.ranger.hive; import java.util.Map; diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/RangerHivePlugin.java b/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/ranger/hive/RangerHivePlugin.java similarity index 94% rename from fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/RangerHivePlugin.java rename to fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/ranger/hive/RangerHivePlugin.java index b562eee5a90..cf675b9a102 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/RangerHivePlugin.java +++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/ranger/hive/RangerHivePlugin.java @@ -15,7 +15,7 @@ // specific language governing permissions and limitations // under the License. -package org.apache.doris.catalog.authorizer; +package org.apache.doris.catalog.authorizer.ranger.hive; import org.apache.ranger.plugin.service.RangerBasePlugin; diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/RangerHiveResource.java b/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/ranger/hive/RangerHiveResource.java similarity index 98% rename from fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/RangerHiveResource.java rename to fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/ranger/hive/RangerHiveResource.java index 51f28b91ff8..ff20736a6fe 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/RangerHiveResource.java +++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/authorizer/ranger/hive/RangerHiveResource.java @@ -15,7 +15,7 @@ // specific language governing permissions and limitations // under the License. -package org.apache.doris.catalog.authorizer; +package org.apache.doris.catalog.authorizer.ranger.hive; import org.apache.ranger.plugin.policyengine.RangerAccessResourceImpl; diff --git a/fe/fe-core/src/main/java/org/apache/doris/httpv2/controller/BaseController.java b/fe/fe-core/src/main/java/org/apache/doris/httpv2/controller/BaseController.java index 9f36ba9b308..0027b6dd647 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/httpv2/controller/BaseController.java +++ b/fe/fe-core/src/main/java/org/apache/doris/httpv2/controller/BaseController.java @@ -17,7 +17,6 @@ package org.apache.doris.httpv2.controller; -import org.apache.doris.analysis.CompoundPredicate; import org.apache.doris.analysis.UserIdentity; import org.apache.doris.catalog.Env; import org.apache.doris.cluster.ClusterNamespace; @@ -27,9 +26,7 @@ import org.apache.doris.common.util.NetUtils; import org.apache.doris.httpv2.HttpAuthManager; import org.apache.doris.httpv2.HttpAuthManager.SessionValue; import org.apache.doris.httpv2.exception.UnauthorizedException; -import org.apache.doris.mysql.privilege.PrivBitSet; import org.apache.doris.mysql.privilege.PrivPredicate; -import org.apache.doris.mysql.privilege.Privilege; import org.apache.doris.qe.ConnectContext; import org.apache.doris.service.FrontendOptions; @@ -71,8 +68,7 @@ public class BaseController { UserIdentity currentUser = checkPassword(authInfo); if (checkAuth) { - checkGlobalAuth(currentUser, PrivPredicate.of(PrivBitSet.of(Privilege.ADMIN_PRIV, - Privilege.NODE_PRIV), CompoundPredicate.Operator.OR)); + checkGlobalAuth(currentUser, PrivPredicate.ADMIN); } SessionValue value = new SessionValue(); @@ -129,8 +125,7 @@ public class BaseController { } if (checkAuth && !Env.getCurrentEnv().getAccessManager().checkGlobalPriv(sessionValue.currentUser, - PrivPredicate.of(PrivBitSet.of(Privilege.ADMIN_PRIV, - Privilege.NODE_PRIV), CompoundPredicate.Operator.OR))) { + PrivPredicate.ADMIN)) { // need to check auth and check auth failed return null; } 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 8affdf4af67..2a64ddabe4c 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 @@ -21,11 +21,12 @@ import org.apache.doris.analysis.TableName; import org.apache.doris.analysis.UserIdentity; import org.apache.doris.catalog.AuthorizationInfo; import org.apache.doris.catalog.Env; +import org.apache.doris.catalog.authorizer.ranger.doris.RangerDorisAccessController; +import org.apache.doris.common.Config; import org.apache.doris.common.UserException; import org.apache.doris.datasource.CatalogIf; import org.apache.doris.datasource.ExternalCatalog; import org.apache.doris.datasource.InternalCatalog; -import org.apache.doris.mysql.privilege.Auth.PrivLevel; import org.apache.doris.qe.ConnectContext; import com.google.common.base.Preconditions; @@ -47,14 +48,18 @@ import java.util.Set; public class AccessControllerManager { private static final Logger LOG = LogManager.getLogger(AccessControllerManager.class); - private SystemAccessController sysAccessController; - private CatalogAccessController internalAccessController; + private Auth auth; + private CatalogAccessController defaultAccessController; private Map<String, CatalogAccessController> ctlToCtlAccessController = Maps.newConcurrentMap(); public AccessControllerManager(Auth auth) { - sysAccessController = new SystemAccessController(auth); - internalAccessController = new InternalCatalogAccessController(auth); - ctlToCtlAccessController.put(InternalCatalog.INTERNAL_CATALOG_NAME, internalAccessController); + this.auth = auth; + if (Config.access_controller_type.equalsIgnoreCase("ranger-doris")) { + defaultAccessController = new RangerDorisAccessController("doris"); + } else { + defaultAccessController = new InternalAccessController(auth); + } + ctlToCtlAccessController.put(InternalCatalog.INTERNAL_CATALOG_NAME, defaultAccessController); } public CatalogAccessController getAccessControllerOrDefault(String ctl) { @@ -68,7 +73,7 @@ public class AccessControllerManager { return ctlToCtlAccessController.get(ctl); } - return internalAccessController; + return defaultAccessController; } private synchronized void lazyLoadCtlAccessController(ExternalCatalog catalog) { @@ -77,11 +82,10 @@ public class AccessControllerManager { } catalog.initAccessController(false); if (!ctlToCtlAccessController.containsKey(catalog.getName())) { - ctlToCtlAccessController.put(catalog.getName(), internalAccessController); + ctlToCtlAccessController.put(catalog.getName(), defaultAccessController); } } - public boolean checkIfAccessControllerExist(String ctl) { return ctlToCtlAccessController.containsKey(ctl); } @@ -112,7 +116,7 @@ public class AccessControllerManager { } public Auth getAuth() { - return sysAccessController.getAuth(); + return this.auth; } // ==== Global ==== @@ -121,7 +125,7 @@ public class AccessControllerManager { } public boolean checkGlobalPriv(UserIdentity currentUser, PrivPredicate wanted) { - return sysAccessController.checkGlobalPriv(currentUser, wanted); + return defaultAccessController.checkGlobalPriv(currentUser, wanted); } // ==== Catalog ==== @@ -130,11 +134,10 @@ public class AccessControllerManager { } public boolean checkCtlPriv(UserIdentity currentUser, String ctl, PrivPredicate wanted) { - boolean hasGlobal = sysAccessController.checkGlobalPriv(currentUser, wanted); - // for checking catalog priv, always use InternalCatalogAccessController. - // because catalog priv is only saved in InternalCatalogAccessController. - return getAccessControllerOrDefault(InternalCatalog.INTERNAL_CATALOG_NAME).checkCtlPriv(hasGlobal, currentUser, - ctl, wanted); + boolean hasGlobal = checkGlobalPriv(currentUser, wanted); + // for checking catalog priv, always use InternalAccessController. + // because catalog priv is only saved in InternalAccessController. + return defaultAccessController.checkCtlPriv(hasGlobal, currentUser, ctl, wanted); } // ==== Database ==== @@ -151,7 +154,7 @@ public class AccessControllerManager { } public boolean checkDbPriv(UserIdentity currentUser, String ctl, String db, PrivPredicate wanted) { - boolean hasGlobal = sysAccessController.checkGlobalPriv(currentUser, wanted); + boolean hasGlobal = checkGlobalPriv(currentUser, wanted); return getAccessControllerOrDefault(ctl).checkDbPriv(hasGlobal, currentUser, ctl, db, wanted); } @@ -178,7 +181,7 @@ public class AccessControllerManager { } public boolean checkTblPriv(UserIdentity currentUser, String ctl, String db, String tbl, PrivPredicate wanted) { - boolean hasGlobal = sysAccessController.checkGlobalPriv(currentUser, wanted); + boolean hasGlobal = checkGlobalPriv(currentUser, wanted); return getAccessControllerOrDefault(ctl).checkTblPriv(hasGlobal, currentUser, ctl, db, tbl, wanted); } @@ -186,7 +189,7 @@ public class AccessControllerManager { public void checkColumnsPriv(UserIdentity currentUser, String ctl, HashMultimap<TableName, String> tableToColsMap, PrivPredicate wanted) throws UserException { - boolean hasGlobal = sysAccessController.checkGlobalPriv(currentUser, wanted); + boolean hasGlobal = checkGlobalPriv(currentUser, wanted); CatalogAccessController accessController = getAccessControllerOrDefault(ctl); for (TableName tableName : tableToColsMap.keySet()) { accessController.checkColsPriv(hasGlobal, currentUser, ctl, @@ -198,7 +201,7 @@ public class AccessControllerManager { public void checkColumnsPriv(UserIdentity currentUser, String ctl, String qualifiedDb, String tbl, Set<String> cols, PrivPredicate wanted) throws UserException { - boolean hasGlobal = sysAccessController.checkGlobalPriv(currentUser, wanted); + boolean hasGlobal = checkGlobalPriv(currentUser, wanted); CatalogAccessController accessController = getAccessControllerOrDefault(ctl); accessController.checkColsPriv(hasGlobal, currentUser, ctl, qualifiedDb, tbl, cols, wanted); @@ -216,7 +219,7 @@ public class AccessControllerManager { } public boolean checkResourcePriv(UserIdentity currentUser, String resourceName, PrivPredicate wanted) { - return sysAccessController.checkResourcePriv(currentUser, resourceName, wanted); + return defaultAccessController.checkResourcePriv(currentUser, resourceName, wanted); } public boolean checkWorkloadGroupPriv(ConnectContext ctx, String workloadGroupName, PrivPredicate wanted) { @@ -224,7 +227,7 @@ public class AccessControllerManager { } public boolean checkWorkloadGroupPriv(UserIdentity currentUser, String workloadGroupName, PrivPredicate wanted) { - return sysAccessController.checkWorkloadGroupPriv(currentUser, workloadGroupName, wanted); + return defaultAccessController.checkWorkloadGroupPriv(currentUser, workloadGroupName, wanted); } // ==== Other ==== @@ -245,12 +248,4 @@ public class AccessControllerManager { } return true; } - - /* - * Check if current user has certain privilege. - * This method will check the given privilege levels - */ - public boolean checkHasPriv(ConnectContext ctx, PrivPredicate priv, PrivLevel... levels) { - return sysAccessController.checkHasPriv(ctx, priv, levels); - } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/CatalogAccessController.java b/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/CatalogAccessController.java index 9f7c18b4393..de39252f95b 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/CatalogAccessController.java +++ b/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/CatalogAccessController.java @@ -29,6 +29,10 @@ public interface CatalogAccessController { return hasGlobal || res; } + // ==== Global ==== + boolean checkGlobalPriv(UserIdentity currentUser, PrivPredicate wanted); + + // ==== Catalog ==== boolean checkCtlPriv(UserIdentity currentUser, String ctl, PrivPredicate wanted); // ==== Database ==== @@ -61,6 +65,12 @@ public interface CatalogAccessController { } } + // ==== Resource ==== + boolean checkResourcePriv(UserIdentity currentUser, String resourceName, PrivPredicate wanted); + + // ==== Workload Group ==== + boolean checkWorkloadGroupPriv(UserIdentity currentUser, String workloadGroupName, PrivPredicate wanted); + void checkColsPriv(UserIdentity currentUser, String ctl, String db, String tbl, Set<String> cols, PrivPredicate wanted) throws AuthorizationException; } diff --git a/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/InternalCatalogAccessController.java b/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/InternalAccessController.java similarity index 72% rename from fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/InternalCatalogAccessController.java rename to fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/InternalAccessController.java index 175eb08f8e2..8faf58a56b9 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/InternalCatalogAccessController.java +++ b/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/InternalAccessController.java @@ -22,13 +22,18 @@ import org.apache.doris.common.AuthorizationException; import java.util.Set; -public class InternalCatalogAccessController implements CatalogAccessController { +public class InternalAccessController implements CatalogAccessController { private Auth auth; - public InternalCatalogAccessController(Auth auth) { + public InternalAccessController(Auth auth) { this.auth = auth; } + @Override + public boolean checkGlobalPriv(UserIdentity currentUser, PrivPredicate wanted) { + return auth.checkGlobalPriv(currentUser, wanted); + } + @Override public boolean checkCtlPriv(UserIdentity currentUser, String ctl, PrivPredicate wanted) { return auth.checkCtlPriv(currentUser, ctl, wanted); @@ -49,4 +54,14 @@ public class InternalCatalogAccessController implements CatalogAccessController PrivPredicate wanted) throws AuthorizationException { auth.checkColsPriv(currentUser, ctl, db, tbl, cols, wanted); } + + @Override + public boolean checkResourcePriv(UserIdentity currentUser, String resourceName, PrivPredicate wanted) { + return auth.checkResourcePriv(currentUser, resourceName, wanted); + } + + @Override + public boolean checkWorkloadGroupPriv(UserIdentity currentUser, String workloadGroupName, PrivPredicate wanted) { + return auth.checkWorkloadGroupPriv(currentUser, workloadGroupName, wanted); + } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/PrivPredicate.java b/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/PrivPredicate.java index 6aae0e1c9c9..191d27cbdd2 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/PrivPredicate.java +++ b/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/PrivPredicate.java @@ -61,22 +61,35 @@ public class PrivPredicate { // alter public static final PrivPredicate ALTER = PrivPredicate.of(PrivBitSet.of(Privilege.ADMIN_PRIV, - Privilege.ALTER_PRIV), + Privilege.ALTER_PRIV), Operator.OR); // create public static final PrivPredicate CREATE = PrivPredicate.of(PrivBitSet.of(Privilege.ADMIN_PRIV, - Privilege.CREATE_PRIV), + Privilege.CREATE_PRIV), + Operator.OR); + + // alter create + public static final PrivPredicate ALTER_CREATE = PrivPredicate.of(PrivBitSet.of(Privilege.ADMIN_PRIV, + Privilege.ALTER_PRIV, + Privilege.CREATE_PRIV), + Operator.OR); + + // alter create drop + public static final PrivPredicate ALTER_CREATE_DROP = PrivPredicate.of(PrivBitSet.of(Privilege.ADMIN_PRIV, + Privilege.ALTER_PRIV, + Privilege.CREATE_PRIV, + Privilege.DROP_PRIV), Operator.OR); // drop public static final PrivPredicate DROP = PrivPredicate.of(PrivBitSet.of(Privilege.ADMIN_PRIV, - Privilege.DROP_PRIV), + Privilege.DROP_PRIV), Operator.OR); // select public static final PrivPredicate SELECT = PrivPredicate.of(PrivBitSet.of(Privilege.ADMIN_PRIV, - Privilege.SELECT_PRIV), + Privilege.SELECT_PRIV), Operator.OR); // operator @@ -107,7 +120,7 @@ public class PrivPredicate { this.op = op; } - public static PrivPredicate of(PrivBitSet privs, Operator op) { + private static PrivPredicate of(PrivBitSet privs, Operator op) { final PrivPredicate predicate = new PrivPredicate(privs, op); return predicate; } diff --git a/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/SystemAccessController.java b/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/SystemAccessController.java deleted file mode 100644 index 59672f2a9c9..00000000000 --- a/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/SystemAccessController.java +++ /dev/null @@ -1,54 +0,0 @@ -// 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. - -package org.apache.doris.mysql.privilege; - -import org.apache.doris.analysis.UserIdentity; -import org.apache.doris.mysql.privilege.Auth.PrivLevel; -import org.apache.doris.qe.ConnectContext; - -public class SystemAccessController { - private Auth auth; - - public SystemAccessController(Auth auth) { - this.auth = auth; - } - - public Auth getAuth() { - return auth; - } - - public boolean checkGlobalPriv(UserIdentity currentUser, PrivPredicate wanted) { - return auth.checkGlobalPriv(currentUser, wanted); - } - - public boolean checkResourcePriv(UserIdentity currentUser, String resourceName, PrivPredicate wanted) { - return auth.checkResourcePriv(currentUser, resourceName, wanted); - } - - public boolean checkWorkloadGroupPriv(UserIdentity currentUser, String workloadGroupName, PrivPredicate wanted) { - return auth.checkWorkloadGroupPriv(currentUser, workloadGroupName, wanted); - } - - /* - * Check if current user has certain privilege. - * This method will check the given privilege levels - */ - public boolean checkHasPriv(ConnectContext ctx, PrivPredicate priv, PrivLevel... levels) { - return auth.checkHasPriv(ctx, priv, levels); - } -} diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/ShowExecutor.java b/fe/fe-core/src/main/java/org/apache/doris/qe/ShowExecutor.java index e006f3761f4..f3dac9eed3d 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/qe/ShowExecutor.java +++ b/fe/fe-core/src/main/java/org/apache/doris/qe/ShowExecutor.java @@ -1915,10 +1915,16 @@ public class ShowExecutor { } // Handle show resources - private void handleShowResources() { + private void handleShowResources() throws AnalysisException { ShowResourcesStmt showStmt = (ShowResourcesStmt) stmt; + PatternMatcher matcher = null; + if (showStmt.getPattern() != null) { + matcher = PatternMatcherWrapper.createMysqlPattern(showStmt.getPattern(), + CaseSensibility.RESOURCE.getCaseSensibility()); + } + List<List<Comparable>> resourcesInfos = Env.getCurrentEnv().getResourceMgr() - .getResourcesInfo(showStmt.getNameValue(), showStmt.isAccurateMatch(), showStmt.getTypeSet()); + .getResourcesInfo(matcher, showStmt.getNameValue(), showStmt.isAccurateMatch(), showStmt.getTypeSet()); // order the result of List<LoadInfo> by orderByPairs in show stmt List<OrderByPair> orderByPairs = showStmt.getOrderByPairs(); @@ -1959,10 +1965,14 @@ public class ShowExecutor { resultSet = new ShowResultSet(showStmt.getMetaData(), rows); } - private void handleShowWorkloadGroups() { + private void handleShowWorkloadGroups() throws AnalysisException { ShowWorkloadGroupsStmt showStmt = (ShowWorkloadGroupsStmt) stmt; - List<List<String>> workloadGroupsInfos = Env.getCurrentEnv().getWorkloadGroupMgr().getResourcesInfo(); - + PatternMatcher matcher = null; + if (showStmt.getPattern() != null) { + matcher = PatternMatcherWrapper.createMysqlPattern(showStmt.getPattern(), + CaseSensibility.WORKLOAD_GROUP.getCaseSensibility()); + } + List<List<String>> workloadGroupsInfos = Env.getCurrentEnv().getWorkloadGroupMgr().getResourcesInfo(matcher); resultSet = new ShowResultSet(showStmt.getMetaData(), workloadGroupsInfos); } diff --git a/fe/fe-core/src/main/java/org/apache/doris/resource/workloadgroup/WorkloadGroupMgr.java b/fe/fe-core/src/main/java/org/apache/doris/resource/workloadgroup/WorkloadGroupMgr.java index 85a87f610e1..31b4a81c1d4 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/resource/workloadgroup/WorkloadGroupMgr.java +++ b/fe/fe-core/src/main/java/org/apache/doris/resource/workloadgroup/WorkloadGroupMgr.java @@ -28,6 +28,7 @@ import org.apache.doris.common.DdlException; import org.apache.doris.common.ErrorCode; import org.apache.doris.common.ErrorReport; import org.apache.doris.common.Pair; +import org.apache.doris.common.PatternMatcher; import org.apache.doris.common.UserException; import org.apache.doris.common.io.Text; import org.apache.doris.common.io.Writable; @@ -55,6 +56,7 @@ import java.io.DataOutput; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Objects; @@ -413,13 +415,20 @@ public class WorkloadGroupMgr implements Writable, GsonPostProcessable { } } - public List<List<String>> getResourcesInfo() { + public List<List<String>> getResourcesInfo(PatternMatcher matcher) { UserIdentity currentUserIdentity = ConnectContext.get().getCurrentUserIdentity(); - return procNode.fetchResult(currentUserIdentity).getRows(); + List<List<String>> rows = procNode.fetchResult(currentUserIdentity).getRows(); + for (Iterator<List<String>> it = rows.iterator(); it.hasNext(); ) { + List<String> row = it.next(); + if (matcher != null && !matcher.match(row.get(1))) { + it.remove(); + } + } + return rows; } - public List<List<String>> getResourcesInfo(TUserIdentity tcurrentUserIdentity) { - UserIdentity currentUserIdentity = UserIdentity.fromThrift(tcurrentUserIdentity); + public List<List<String>> getResourcesInfo(TUserIdentity tCurrentUserIdentity) { + UserIdentity currentUserIdentity = UserIdentity.fromThrift(tCurrentUserIdentity); return procNode.fetchResult(currentUserIdentity).getRows(); } diff --git a/fe/fe-core/src/test/java/org/apache/doris/analysis/CreateUserStmtTest.java b/fe/fe-core/src/test/java/org/apache/doris/analysis/CreateUserStmtTest.java index d80f505f9bc..f27c00211a3 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/analysis/CreateUserStmtTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/analysis/CreateUserStmtTest.java @@ -20,7 +20,6 @@ package org.apache.doris.analysis; import org.apache.doris.common.AnalysisException; import org.apache.doris.common.UserException; import org.apache.doris.mysql.privilege.AccessControllerManager; -import org.apache.doris.mysql.privilege.Auth; import org.apache.doris.mysql.privilege.PrivPredicate; import org.apache.doris.qe.ConnectContext; @@ -46,12 +45,11 @@ public class CreateUserStmtTest { @Test public void testToString(@Injectable Analyzer analyzer, - @Mocked AccessControllerManager accessManager) throws UserException, AnalysisException { + @Mocked AccessControllerManager accessManager) throws UserException { new Expectations() { { - accessManager.checkHasPriv((ConnectContext) any, PrivPredicate.GRANT, Auth.PrivLevel.GLOBAL, Auth - .PrivLevel.DATABASE); + accessManager.checkGlobalPriv((ConnectContext) any, PrivPredicate.GRANT); result = true; } }; diff --git a/fe/fe-core/src/test/java/org/apache/doris/analysis/ShowResourcesStmtTest.java b/fe/fe-core/src/test/java/org/apache/doris/analysis/ShowResourcesStmtTest.java index 50cf5505ef7..7954182f51f 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/analysis/ShowResourcesStmtTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/analysis/ShowResourcesStmtTest.java @@ -51,28 +51,32 @@ public class ShowResourcesStmtTest { @Test public void testNormal() throws UserException, AnalysisException { - ShowResourcesStmt stmt = new ShowResourcesStmt(null, null, null); + ShowResourcesStmt stmt = new ShowResourcesStmt(null, null, null, null); stmt.analyze(analyzer); Assert.assertEquals("SHOW RESOURCES", stmt.toString()); } @Test public void testWhere() throws UserException, AnalysisException { - ShowResourcesStmt stmt = new ShowResourcesStmt(null, null, null); + ShowResourcesStmt stmt = new ShowResourcesStmt(null, null, null, null); stmt.analyze(analyzer); Assert.assertEquals("SHOW RESOURCES", stmt.toString()); SlotRef slotRef = new SlotRef(null, "name"); StringLiteral stringLiteral = new StringLiteral("abc"); BinaryPredicate binaryPredicate = new BinaryPredicate(Operator.EQ, slotRef, stringLiteral); - stmt = new ShowResourcesStmt(binaryPredicate, null, new LimitElement(10)); + stmt = new ShowResourcesStmt(null, binaryPredicate, null, new LimitElement(10)); stmt.analyze(analyzer); Assert.assertEquals("SHOW RESOURCES WHERE (`name` = \'abc\') LIMIT 10", stmt.toString()); LikePredicate likePredicate = new LikePredicate(org.apache.doris.analysis.LikePredicate.Operator.LIKE, slotRef, stringLiteral); - stmt = new ShowResourcesStmt(likePredicate, null, new LimitElement(10)); + stmt = new ShowResourcesStmt(null, likePredicate, null, new LimitElement(10)); stmt.analyze(analyzer); Assert.assertEquals("SHOW RESOURCES WHERE `name` LIKE \'abc\' LIMIT 10", stmt.toString()); + + stmt = new ShowResourcesStmt("m%", likePredicate, null, new LimitElement(10)); + stmt.analyze(analyzer); + Assert.assertEquals("SHOW RESOURCES LIKE \'m%\' LIMIT 10", stmt.toString()); } } diff --git a/fe/fe-core/src/test/java/org/apache/doris/datasource/ColumnPrivTest.java b/fe/fe-core/src/test/java/org/apache/doris/datasource/ColumnPrivTest.java index 2f9ac605a0b..8e8bbd11100 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/datasource/ColumnPrivTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/datasource/ColumnPrivTest.java @@ -258,6 +258,11 @@ public class ColumnPrivTest extends TestWithFeService { this.prop = prop; } + @Override + public boolean checkGlobalPriv(UserIdentity currentUser, PrivPredicate wanted) { + return false; + } + @Override public boolean checkCtlPriv(UserIdentity currentUser, String ctl, PrivPredicate wanted) { return false; @@ -268,6 +273,17 @@ public class ColumnPrivTest extends TestWithFeService { return false; } + @Override + public boolean checkResourcePriv(UserIdentity currentUser, String resourceName, PrivPredicate wanted) { + return false; + } + + @Override + public boolean checkWorkloadGroupPriv(UserIdentity currentUser, String workloadGroupName, + PrivPredicate wanted) { + return false; + } + @Override public boolean checkTblPriv(UserIdentity currentUser, String ctl, String db, String tbl, PrivPredicate wanted) { --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@doris.apache.org For additional commands, e-mail: commits-h...@doris.apache.org