diff --git a/presto-docs/src/main/sphinx/admin/session-property-managers.rst b/presto-docs/src/main/sphinx/admin/session-property-managers.rst index 5227827b4204..b434daa82342 100644 --- a/presto-docs/src/main/sphinx/admin/session-property-managers.rst +++ b/presto-docs/src/main/sphinx/admin/session-property-managers.rst @@ -69,20 +69,20 @@ These requirements can be expressed with the following rules: [ { "group": "global.*", - "sessionProperties": { + "session_properties": { "query_max_execution_time": "8h", } }, { "group": "global.interactive.*", - "sessionProperties": { + "session_properties": { "query_max_execution_time": "1h" } }, { "group": "global.pipeline.*", "clientTags": ["etl"], - "sessionProperties": { + "session_properties": { "scale_writers": "true", "writer_min_size": "1GB" } @@ -93,4 +93,4 @@ Limitations ----------- The session property manager only supports system session properties and does -not support catalog session properties. \ No newline at end of file +not support catalog session properties. diff --git a/presto-docs/src/main/sphinx/connector/hive-security.rst b/presto-docs/src/main/sphinx/connector/hive-security.rst index 9b760ada3494..0a5ff1fcda19 100644 --- a/presto-docs/src/main/sphinx/connector/hive-security.rst +++ b/presto-docs/src/main/sphinx/connector/hive-security.rst @@ -563,7 +563,7 @@ See below for an example. "privileges": ["SELECT"] } ], - "sessionProperties": [ + "session_properties": [ { "property": "force_local_scheduling", "allow": true diff --git a/presto-docs/src/main/sphinx/security/file-system-access-control.rst b/presto-docs/src/main/sphinx/security/file-system-access-control.rst index 2920dd9c90b8..5c6a556980c6 100644 --- a/presto-docs/src/main/sphinx/security/file-system-access-control.rst +++ b/presto-docs/src/main/sphinx/security/file-system-access-control.rst @@ -34,13 +34,67 @@ Presto restart. The refresh period is specified in the ``etc/access-control.prop security.refresh-period=1s +Catalog, Schema, and Table Access +--------------------------------- + +Access to catalogs, schemas, tables, and views is controlled by the catalog, schema, and table +rules. The catalog rules are course grained rules used to restrict all access or write +access to catalogs. They do not explicitly grant any specific schema or table permissions. +The table and schema rules are used to specify who can can create, drop, alter, select, insert, +delete, etc. for schemas and tables. + +For each rule set, permission is based on the first matching rule read from top to bottom. If +no rule matches, access is denied. If no rules are provided at all, then access is granted. + +The following table summarizes the permissions required for each SQL command: + +==================================== ========== ======= ==================== =================================================== +SQL Command Catalog Schema Table Note +==================================== ========== ======= ==================== =================================================== +SHOW CATALOGS Always allowed +SHOW SCHEMAS read-only any* any* Allowed if catalog is :ref:`visible` +SHOW TABLES read-only any* any* Allowed if schema :ref:`visible` +CREATE SCHEMA read-only owner +DROP SCHEMA all owner +SHOW CREATE SCHEMA all owner +ALTER SCHEMA ... RENAME TO all owner* Ownership is required on both old and new schemas +ALTER SCHEMA ... SET AUTHORIZATION all owner +CREATE TABLE all owner +DROP TABLE all owner +ALTER TABLE ... RENAME TO all owner* Ownership is required on both old and new tables +CREATE VIEW all owner +DROP VIEW all owner +ALTER VIEW ... RENAME TO all owner* Ownership is required on both old and new views +COMMENT ON TABLE all owner +COMMENT ON COLUMN all owner +ALTER TABLE ... ADD COLUMN all owner +ALTER TABLE ... DROP COLUMN all owner +ALTER TABLE ... RENAME COLUMN all owner +SHOW COLUMNS all any +SELECT FROM table read-only select +SELECT FROM view read-only select, grant_select +INSERT INTO all insert +DELETE FROM all delete +==================================== ========== ======= ==================== =================================================== + +.. _visibility: + +Visibility +^^^^^^^^^^ + +For a catalog, schema, or table to be visible in a ``SHOW`` command, the user must have +at least one permission on the item or any nested item. The nested items do not +need to already exist as any potential permission makes the item visible. Specifically: + +* catalog: Visible if user is the owner of any nested schema, has permissions on any nested + table, or has permissions to set session properties in the catalog. +* schema: Visible if the user is the owner of the schema, or has permissions on any nested table. +* table: Visible if the user has any permissions on the table. + Catalog Rules -------------- +^^^^^^^^^^^^^ -These rules govern the catalogs particular users can access. The user is -granted access to a catalog, based on the first matching rule read from top to -bottom. If no rule matches, access is denied. Each rule is composed of the -following fields: +Each catalog rule is composed of the following fields: * ``user`` (optional): regex to match against user name. Defaults to ``.*``. * ``group`` (optional): regex to match against group names. Defaults to ``.*``. @@ -56,6 +110,9 @@ specified in ``user`` attribute. For group names, a rule can be applied if at least one group name of this user matches the ``group`` regular expression. +The ``all`` value for ``allow`` means these rules do not restrict access in any way, +but the schema and table rules can restrict access. + .. note:: By default, all users have access to the ``system`` catalog. You can @@ -104,33 +161,24 @@ For group-based rules to match, users need to be assigned to groups by a :doc:`/develop/group-provider`. Schema Rules ------------- +^^^^^^^^^^^^ -These rules allow you to grant ownership of a schema. Having ownership of an -schema allows users to execute ``DROP SCHEMA``, ``ALTER SCHEMA`` (both renaming -and setting authorization) and ``SHOW CREATE SCHEMA``. The user is granted -ownership of a schema, based on the first matching rule read from top to -bottom. If no rule matches, ownership is not granted. Each rule is composed of -the following fields: +Each schema rule is composed of the following fields: * ``user`` (optional): regex to match against user name. Defaults to ``.*``. * ``group`` (optional): regex to match against group names. Defaults to ``.*``. +* ``catalog`` (optional): regex to match against catalog name. Defaults to ``.*``. * ``schema`` (optional): regex to match against schema name. Defaults to ``.*``. * ``owner`` (required): boolean indicating whether the user is to be considered an owner of the schema. Defaults to ``false``. For example, to provide ownership of all schemas to user ``admin``, treat all -users as owners of ``default`` schema and prevent user ``guest`` from ownership -of any schema, you can use the following rules: +users as owners of the ``default.default`` schema and prevent user ``guest`` from +ownership of any schema, you can use the following rules: .. code-block:: json { - "catalogs": [ - { - "allow": true - } - ], "schemas": [ { "user": "admin", @@ -142,6 +190,7 @@ of any schema, you can use the following rules: "owner": false }, { + "catalog": "default", "schema": "default", "owner": true } @@ -149,15 +198,13 @@ of any schema, you can use the following rules: } Table Rules ------------ +^^^^^^^^^^^ -These rules define the privileges for table access for users. If no table rules -are specified, all users are treated as having all privileges by default. The -user is granted privileges based on the first matching rule read from top to -bottom. Each rule is composed of the following fields: +Each table rule is composed of the following fields: * ``user`` (optional): regex to match against user name. Defaults to ``.*``. * ``group`` (optional): regex to match against group names. Defaults to ``.*``. +* ``catalog`` (optional): regex to match against catalog name. Defaults to ``.*``. * ``schema`` (optional): regex to match against schema name. Defaults to ``.*``. * ``table`` (optional): regex to match against table names. Defaults to ``.*``. * ``privileges`` (required): zero or more of ``SELECT``, ``INSERT``, @@ -171,16 +218,11 @@ The example below defines the following table access policy: * User ``admin`` has all privileges across all tables and schemas * User ``banned_user`` has no privileges -* All users have ``SELECT`` privileges on all tables in ``default`` schema +* All users have ``SELECT`` privileges on all tables in the ``default.default`` schema .. code-block:: json { - "catalogs": [ - { - "allow": true - } - ], "tables": [ { "user": "admin", @@ -191,6 +233,7 @@ The example below defines the following table access policy: "privileges": [] }, { + "catalog": "default", "schema": "default", "table": ".*", "privileges": ["SELECT"] @@ -198,6 +241,36 @@ The example below defines the following table access policy: ] } +.. _session_property_rules: + +Session Property Rules +---------------------- + +These rules control the ability of a user to set system and catalog session properties. The +user is granted or denied access, based on the first matching rule, read from top to bottom. +If no rules are specified, all users are allowed set any session property. If no rule matches, +setting the session property is denied. System session property rules are composed of the +following fields: + +* ``user`` (optional): regex to match against user name. Defaults to ``.*``. +* ``group`` (optional): regex to match against group names. Defaults to ``.*``. +* ``property`` (optional): regex to match against the property name. Defaults to ``.*``. +* ``allow`` (required): boolean indicating if the setting the session property should be allowed. + +The catalog session property rules have the additional field: + +* ``catalog`` (optional): regex to match against catalog name. Defaults to ``.*``. + +The example below defines the following table access policy: + +* User ``admin`` can set all session property +* User ``banned_user`` can not set any session properties +* All users can set the ``resource_overcommit`` system session property, and the + ``bucket_execution_enabled`` session property in the ``hive`` catalog. + +.. literalinclude:: session-property-access.json + :language: json + .. _query_rules: Query Rules @@ -245,8 +318,8 @@ defined, impersonation is not allowed. Each impersonation rule is composed of the following fields: -* ``originalUser`` (required): regex to match against the user requesting the impersonation. -* ``newUser`` (required): regex to match against the user that will be impersonated. +* ``original_user`` (required): regex to match against the user requesting the impersonation. +* ``new_user`` (required): regex to match against the user that will be impersonated. * ``allow`` (optional): boolean indicating if the authentication should be allowed. The following example allows the two admins, ``alice`` and ``bob``, to impersonate @@ -295,11 +368,6 @@ and Kerberos authentication: .. code-block:: json { - "catalogs": [ - { - "allow": true - } - ], "principals": [ { "principal": "(.*)", @@ -321,11 +389,6 @@ name, and allow ``alice`` and ``bob`` to use a group principal named as .. code-block:: json { - "catalogs": [ - { - "allow": true - } - ], "principals": [ { "principal": "([^/]+)/?.*@example.net", diff --git a/presto-docs/src/main/sphinx/security/query-access.json b/presto-docs/src/main/sphinx/security/query-access.json index bb034c69ec8b..e17a0ea66eed 100644 --- a/presto-docs/src/main/sphinx/security/query-access.json +++ b/presto-docs/src/main/sphinx/security/query-access.json @@ -1,9 +1,4 @@ { - "catalogs": [ - { - "allow": true - } - ], "queries": [ { "user": "admin", diff --git a/presto-docs/src/main/sphinx/security/session-property-access.json b/presto-docs/src/main/sphinx/security/session-property-access.json new file mode 100644 index 000000000000..5ba898b5ae81 --- /dev/null +++ b/presto-docs/src/main/sphinx/security/session-property-access.json @@ -0,0 +1,31 @@ +{ + "system_session_properties": [ + { + "user": "admin", + "allow": true + }, + { + "user": "banned_user", + "allow": false + }, + { + "property": "resource_overcommit", + "allow": true + } + ], + "catalog_session_properties": [ + { + "user": "admin", + "allow": true + }, + { + "user": "banned_user", + "allow": false + }, + { + "catalog": "hive", + "property": "bucket_execution_enabled", + "allow": true + } + ] +} diff --git a/presto-docs/src/main/sphinx/security/system-information-access.json b/presto-docs/src/main/sphinx/security/system-information-access.json index 54c38315da9b..16839958ff58 100644 --- a/presto-docs/src/main/sphinx/security/system-information-access.json +++ b/presto-docs/src/main/sphinx/security/system-information-access.json @@ -1,9 +1,4 @@ { - "catalogs": [ - { - "allow": true - } - ], "system_information": [ { "user": "admin", diff --git a/presto-docs/src/main/sphinx/security/user-impersonation.json b/presto-docs/src/main/sphinx/security/user-impersonation.json index b1ede7a911e9..d44832e8d1bc 100644 --- a/presto-docs/src/main/sphinx/security/user-impersonation.json +++ b/presto-docs/src/main/sphinx/security/user-impersonation.json @@ -1,27 +1,22 @@ { - "catalogs": [ - { - "allow": true - } - ], "impersonation": [ { - "originalUser": "alice", - "newUser": "bob", + "original_user": "alice", + "new_user": "bob", "allow": false }, { - "originalUser": "bob", - "newUser": "alice", + "original_user": "bob", + "new_user": "alice", "allow": false }, { - "originalUser": "alice|bob", - "newUser": ".*" + "original_user": "alice|bob", + "new_user": ".*" }, { - "originalUser": ".*", - "newUser": "test" + "original_user": ".*", + "new_user": "test" } ] } diff --git a/presto-hive/src/test/resources/io/prestosql/plugin/hive/security.json b/presto-hive/src/test/resources/io/prestosql/plugin/hive/security.json index 58bede69077a..3de620b36bb9 100644 --- a/presto-hive/src/test/resources/io/prestosql/plugin/hive/security.json +++ b/presto-hive/src/test/resources/io/prestosql/plugin/hive/security.json @@ -3,14 +3,14 @@ { "user": "hive", "privileges": [ - "SELECT" + "SELECT", + "OWNERSHIP" ] } ], "schemas": [ { - "user": "hive", - "owner": true + "owner": false } ] } diff --git a/presto-main/src/test/java/io/prestosql/security/TestFileBasedSystemAccessControl.java b/presto-main/src/test/java/io/prestosql/security/TestFileBasedSystemAccessControl.java index e5f72e68d7d5..5c918f479814 100644 --- a/presto-main/src/test/java/io/prestosql/security/TestFileBasedSystemAccessControl.java +++ b/presto-main/src/test/java/io/prestosql/security/TestFileBasedSystemAccessControl.java @@ -312,6 +312,7 @@ public void testTableOperations() accessControlManager.checkCanCreateTable(aliceContext, aliceTable); accessControlManager.checkCanDropTable(aliceContext, aliceTable); accessControlManager.checkCanSelectFromColumns(aliceContext, aliceTable, ImmutableSet.of()); + accessControlManager.checkCanCreateViewWithSelectFromColumns(aliceContext, aliceTable, ImmutableSet.of()); accessControlManager.checkCanInsertIntoTable(aliceContext, aliceTable); accessControlManager.checkCanDeleteFromTable(aliceContext, aliceTable); accessControlManager.checkCanAddColumns(aliceContext, aliceTable); @@ -320,6 +321,7 @@ public void testTableOperations() accessControlManager.checkCanCreateTable(aliceContext, staffTable); accessControlManager.checkCanDropTable(aliceContext, staffTable); accessControlManager.checkCanSelectFromColumns(aliceContext, staffTable, ImmutableSet.of()); + accessControlManager.checkCanCreateViewWithSelectFromColumns(aliceContext, staffTable, ImmutableSet.of()); accessControlManager.checkCanInsertIntoTable(aliceContext, staffTable); accessControlManager.checkCanDeleteFromTable(aliceContext, staffTable); accessControlManager.checkCanAddColumns(aliceContext, staffTable); @@ -328,6 +330,7 @@ public void testTableOperations() assertThrows(AccessDeniedException.class, () -> accessControlManager.checkCanCreateTable(bobContext, aliceTable)); assertThrows(AccessDeniedException.class, () -> accessControlManager.checkCanDropTable(bobContext, aliceTable)); assertThrows(AccessDeniedException.class, () -> accessControlManager.checkCanSelectFromColumns(bobContext, aliceTable, ImmutableSet.of())); + assertThrows(AccessDeniedException.class, () -> accessControlManager.checkCanCreateViewWithSelectFromColumns(bobContext, aliceTable, ImmutableSet.of())); assertThrows(AccessDeniedException.class, () -> accessControlManager.checkCanInsertIntoTable(bobContext, aliceTable)); assertThrows(AccessDeniedException.class, () -> accessControlManager.checkCanDeleteFromTable(bobContext, aliceTable)); assertThrows(AccessDeniedException.class, () -> accessControlManager.checkCanAddColumns(bobContext, aliceTable)); @@ -336,6 +339,7 @@ public void testTableOperations() accessControlManager.checkCanCreateTable(bobContext, staffTable); accessControlManager.checkCanDropTable(bobContext, staffTable); accessControlManager.checkCanSelectFromColumns(bobContext, staffTable, ImmutableSet.of()); + accessControlManager.checkCanCreateViewWithSelectFromColumns(bobContext, staffTable, ImmutableSet.of()); accessControlManager.checkCanInsertIntoTable(bobContext, staffTable); accessControlManager.checkCanDeleteFromTable(bobContext, staffTable); accessControlManager.checkCanAddColumns(bobContext, staffTable); @@ -344,6 +348,7 @@ public void testTableOperations() assertThrows(AccessDeniedException.class, () -> accessControlManager.checkCanCreateTable(nonAsciiContext, aliceTable)); assertThrows(AccessDeniedException.class, () -> accessControlManager.checkCanDropTable(nonAsciiContext, aliceTable)); assertThrows(AccessDeniedException.class, () -> accessControlManager.checkCanSelectFromColumns(nonAsciiContext, aliceTable, ImmutableSet.of())); + assertThrows(AccessDeniedException.class, () -> accessControlManager.checkCanCreateViewWithSelectFromColumns(nonAsciiContext, aliceTable, ImmutableSet.of())); assertThrows(AccessDeniedException.class, () -> accessControlManager.checkCanInsertIntoTable(nonAsciiContext, aliceTable)); assertThrows(AccessDeniedException.class, () -> accessControlManager.checkCanDeleteFromTable(nonAsciiContext, aliceTable)); assertThrows(AccessDeniedException.class, () -> accessControlManager.checkCanAddColumns(nonAsciiContext, aliceTable)); @@ -352,6 +357,7 @@ public void testTableOperations() assertThrows(AccessDeniedException.class, () -> accessControlManager.checkCanCreateTable(nonAsciiContext, staffTable)); assertThrows(AccessDeniedException.class, () -> accessControlManager.checkCanDropTable(nonAsciiContext, staffTable)); assertThrows(AccessDeniedException.class, () -> accessControlManager.checkCanSelectFromColumns(nonAsciiContext, staffTable, ImmutableSet.of())); + assertThrows(AccessDeniedException.class, () -> accessControlManager.checkCanCreateViewWithSelectFromColumns(nonAsciiContext, staffTable, ImmutableSet.of())); assertThrows(AccessDeniedException.class, () -> accessControlManager.checkCanInsertIntoTable(nonAsciiContext, staffTable)); assertThrows(AccessDeniedException.class, () -> accessControlManager.checkCanDeleteFromTable(nonAsciiContext, staffTable)); assertThrows(AccessDeniedException.class, () -> accessControlManager.checkCanAddColumns(nonAsciiContext, staffTable)); @@ -492,14 +498,6 @@ public void testViewOperationsReadOnly() accessControlManager.checkCanDropView(new SecurityContext(transactionId, alice, queryId), aliceView); })); - assertThrows(AccessDeniedException.class, () -> transaction(transactionManager, accessControlManager).execute(transactionId -> { - accessControlManager.checkCanCreateViewWithSelectFromColumns(new SecurityContext(transactionId, alice, queryId), aliceTable, ImmutableSet.of()); - })); - - assertThrows(AccessDeniedException.class, () -> transaction(transactionManager, accessControlManager).execute(transactionId -> { - accessControlManager.checkCanCreateViewWithSelectFromColumns(new SecurityContext(transactionId, alice, queryId), aliceView, ImmutableSet.of()); - })); - assertThrows(AccessDeniedException.class, () -> transaction(transactionManager, accessControlManager).execute(transactionId -> { accessControlManager.checkCanGrantTablePrivilege(new SecurityContext(transactionId, alice, queryId), SELECT, aliceTable, new PrestoPrincipal(USER, "grantee"), true); })); diff --git a/presto-main/src/test/resources/catalog_impersonation.json b/presto-main/src/test/resources/catalog_impersonation.json index 8bcf292a7f2e..9e8e555bc4fd 100644 --- a/presto-main/src/test/resources/catalog_impersonation.json +++ b/presto-main/src/test/resources/catalog_impersonation.json @@ -1,31 +1,26 @@ { - "catalogs": [ - { - "allow": true - } - ], "impersonation": [ { - "originalUser": "alice", - "newUser": "bob|charlie" + "original_user": "alice", + "new_user": "bob|charlie" }, { - "originalUser": "admin-test", - "newUser": ".*", + "original_user": "admin-test", + "new_user": ".*", "allow": false }, { - "originalUser": "admin(-.*)?", - "newUser": ".*" + "original_user": "admin(-.*)?", + "new_user": ".*" }, { - "originalUser": "invalid(-.*)?", - "newUser": ".*", + "original_user": "invalid(-.*)?", + "new_user": ".*", "allow": false }, { - "originalUser": ".*", - "newUser": "test" + "original_user": ".*", + "new_user": "test" } ] } diff --git a/presto-main/src/test/resources/catalog_principal.json b/presto-main/src/test/resources/catalog_principal.json index 6bf739521323..34b46fede63b 100644 --- a/presto-main/src/test/resources/catalog_principal.json +++ b/presto-main/src/test/resources/catalog_principal.json @@ -1,9 +1,4 @@ { - "catalogs": [ - { - "allow": true - } - ], "principals": [ { "principal": "(.*)", diff --git a/presto-main/src/test/resources/security-config-file-with-unknown-rules.json b/presto-main/src/test/resources/security-config-file-with-unknown-rules.json index e0c9043a9e6d..2c67aba467b7 100644 --- a/presto-main/src/test/resources/security-config-file-with-unknown-rules.json +++ b/presto-main/src/test/resources/security-config-file-with-unknown-rules.json @@ -1,5 +1,5 @@ { - "sessionProperties": [ + "session_properties": [ { "property": "force_local_scheduling", "allow": true diff --git a/presto-main/src/test/resources/system_information.json b/presto-main/src/test/resources/system_information.json index f1b0f7f4c10f..3efb6cc8b232 100644 --- a/presto-main/src/test/resources/system_information.json +++ b/presto-main/src/test/resources/system_information.json @@ -1,9 +1,4 @@ { - "catalogs": [ - { - "allow": true - } - ], "system_information": [ { "user": "admin", diff --git a/presto-plugin-toolkit/src/main/java/io/prestosql/plugin/base/security/AccessControlRules.java b/presto-plugin-toolkit/src/main/java/io/prestosql/plugin/base/security/AccessControlRules.java index 6ea5867157f4..1cb4a9ff70be 100644 --- a/presto-plugin-toolkit/src/main/java/io/prestosql/plugin/base/security/AccessControlRules.java +++ b/presto-plugin-toolkit/src/main/java/io/prestosql/plugin/base/security/AccessControlRules.java @@ -13,6 +13,7 @@ */ package io.prestosql.plugin.base.security; +import com.fasterxml.jackson.annotation.JsonAlias; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.collect.ImmutableList; @@ -30,11 +31,11 @@ public class AccessControlRules public AccessControlRules( @JsonProperty("schemas") Optional> schemaRules, @JsonProperty("tables") Optional> tableRules, - @JsonProperty("sessionProperties") Optional> sessionPropertyRules) + @JsonProperty("session_properties") @JsonAlias("sessionProperties") Optional> sessionPropertyRules) { - this.schemaRules = schemaRules.orElse(ImmutableList.of()); - this.tableRules = tableRules.orElse(ImmutableList.of()); - this.sessionPropertyRules = sessionPropertyRules.orElse(ImmutableList.of()); + this.schemaRules = schemaRules.orElse(ImmutableList.of(SchemaAccessControlRule.ALLOW_ALL)); + this.tableRules = tableRules.orElse(ImmutableList.of(TableAccessControlRule.ALLOW_ALL)); + this.sessionPropertyRules = sessionPropertyRules.orElse(ImmutableList.of(SessionPropertyAccessControlRule.ALLOW_ALL)); } public List getSchemaRules() diff --git a/presto-plugin-toolkit/src/main/java/io/prestosql/plugin/base/security/AnyCatalogPermissionsRule.java b/presto-plugin-toolkit/src/main/java/io/prestosql/plugin/base/security/AnyCatalogPermissionsRule.java new file mode 100644 index 000000000000..2765ffe10384 --- /dev/null +++ b/presto-plugin-toolkit/src/main/java/io/prestosql/plugin/base/security/AnyCatalogPermissionsRule.java @@ -0,0 +1,71 @@ +/* + * Licensed 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 io.prestosql.plugin.base.security; + +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.regex.Pattern; + +public class AnyCatalogPermissionsRule +{ + private final Optional userRegex; + private final Optional groupRegex; + private final Optional catalogRegex; + + public AnyCatalogPermissionsRule(Optional userRegex, Optional groupRegex, Optional catalogRegex) + { + this.userRegex = userRegex; + this.groupRegex = groupRegex; + this.catalogRegex = catalogRegex; + } + + public boolean match(String user, Set groups, String catalog) + { + return userRegex.map(regex -> regex.matcher(user).matches()).orElse(true) && + groupRegex.map(regex -> groups.stream().anyMatch(group -> regex.matcher(group).matches())).orElse(true) && + catalogRegex.map(regex -> regex.matcher(catalog).matches()).orElse(true); + } + + @Override + public boolean equals(Object o) + { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + AnyCatalogPermissionsRule that = (AnyCatalogPermissionsRule) o; + return patternEquals(userRegex, that.userRegex) && + patternEquals(groupRegex, that.groupRegex) && + patternEquals(catalogRegex, that.catalogRegex); + } + + private static boolean patternEquals(Optional left, Optional right) + { + if (left.isEmpty() || right.isEmpty()) { + return left.isEmpty() == right.isEmpty(); + } + Pattern leftPattern = left.get(); + Pattern rightPattern = right.get(); + return leftPattern.pattern().equals(rightPattern.pattern()) && leftPattern.flags() == rightPattern.flags(); + } + + @Override + public int hashCode() + { + return Objects.hash(userRegex, groupRegex, catalogRegex); + } +} diff --git a/presto-plugin-toolkit/src/main/java/io/prestosql/plugin/base/security/AnyCatalogSchemaPermissionsRule.java b/presto-plugin-toolkit/src/main/java/io/prestosql/plugin/base/security/AnyCatalogSchemaPermissionsRule.java new file mode 100644 index 000000000000..b95e4dcaf90e --- /dev/null +++ b/presto-plugin-toolkit/src/main/java/io/prestosql/plugin/base/security/AnyCatalogSchemaPermissionsRule.java @@ -0,0 +1,75 @@ +/* + * Licensed 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 io.prestosql.plugin.base.security; + +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.regex.Pattern; + +public class AnyCatalogSchemaPermissionsRule +{ + private final Optional userRegex; + private final Optional groupRegex; + private final Optional catalogRegex; + private final Optional schemaRegex; + + public AnyCatalogSchemaPermissionsRule(Optional userRegex, Optional groupRegex, Optional catalogRegex, Optional schemaRegex) + { + this.userRegex = userRegex; + this.groupRegex = groupRegex; + this.catalogRegex = catalogRegex; + this.schemaRegex = schemaRegex; + } + + public boolean match(String user, Set groups, String catalogName, String schemaName) + { + return userRegex.map(regex -> regex.matcher(user).matches()).orElse(true) && + groupRegex.map(regex -> groups.stream().anyMatch(group -> regex.matcher(group).matches())).orElse(true) && + catalogRegex.map(regex -> regex.matcher(catalogName).matches()).orElse(true) && + schemaRegex.map(regex -> regex.matcher(schemaName).matches()).orElse(true); + } + + @Override + public boolean equals(Object o) + { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + AnyCatalogSchemaPermissionsRule that = (AnyCatalogSchemaPermissionsRule) o; + return patternEquals(userRegex, that.userRegex) && + patternEquals(groupRegex, that.groupRegex) && + patternEquals(catalogRegex, that.catalogRegex) && + patternEquals(schemaRegex, that.schemaRegex); + } + + private static boolean patternEquals(Optional left, Optional right) + { + if (left.isEmpty() || right.isEmpty()) { + return left.isEmpty() == right.isEmpty(); + } + Pattern leftPattern = left.get(); + Pattern rightPattern = right.get(); + return leftPattern.pattern().equals(rightPattern.pattern()) && leftPattern.flags() == rightPattern.flags(); + } + + @Override + public int hashCode() + { + return Objects.hash(userRegex, groupRegex, catalogRegex, schemaRegex); + } +} diff --git a/presto-plugin-toolkit/src/main/java/io/prestosql/plugin/base/security/AnySchemaPermissionsRule.java b/presto-plugin-toolkit/src/main/java/io/prestosql/plugin/base/security/AnySchemaPermissionsRule.java new file mode 100644 index 000000000000..3abe607739e6 --- /dev/null +++ b/presto-plugin-toolkit/src/main/java/io/prestosql/plugin/base/security/AnySchemaPermissionsRule.java @@ -0,0 +1,71 @@ +/* + * Licensed 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 io.prestosql.plugin.base.security; + +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.regex.Pattern; + +public class AnySchemaPermissionsRule +{ + private final Optional userRegex; + private final Optional groupRegex; + private final Optional schemaRegex; + + public AnySchemaPermissionsRule(Optional userRegex, Optional groupRegex, Optional schemaRegex) + { + this.userRegex = userRegex; + this.groupRegex = groupRegex; + this.schemaRegex = schemaRegex; + } + + public boolean match(String user, Set groups, String schemaName) + { + return userRegex.map(regex -> regex.matcher(user).matches()).orElse(true) && + groupRegex.map(regex -> groups.stream().anyMatch(group -> regex.matcher(group).matches())).orElse(true) && + schemaRegex.map(regex -> regex.matcher(schemaName).matches()).orElse(true); + } + + @Override + public boolean equals(Object o) + { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + AnySchemaPermissionsRule that = (AnySchemaPermissionsRule) o; + return patternEquals(userRegex, that.userRegex) && + patternEquals(groupRegex, that.groupRegex) && + patternEquals(schemaRegex, that.schemaRegex); + } + + private static boolean patternEquals(Optional left, Optional right) + { + if (left.isEmpty() || right.isEmpty()) { + return left.isEmpty() == right.isEmpty(); + } + Pattern leftPattern = left.get(); + Pattern rightPattern = right.get(); + return leftPattern.pattern().equals(rightPattern.pattern()) && leftPattern.flags() == rightPattern.flags(); + } + + @Override + public int hashCode() + { + return Objects.hash(userRegex, groupRegex, schemaRegex); + } +} diff --git a/presto-plugin-toolkit/src/main/java/io/prestosql/plugin/base/security/CatalogAccessControlRule.java b/presto-plugin-toolkit/src/main/java/io/prestosql/plugin/base/security/CatalogAccessControlRule.java index dcdd883230dc..57d763af98ca 100644 --- a/presto-plugin-toolkit/src/main/java/io/prestosql/plugin/base/security/CatalogAccessControlRule.java +++ b/presto-plugin-toolkit/src/main/java/io/prestosql/plugin/base/security/CatalogAccessControlRule.java @@ -25,10 +25,17 @@ import java.util.Set; import java.util.regex.Pattern; +import static io.prestosql.plugin.base.security.CatalogAccessControlRule.AccessMode.ALL; import static java.util.Objects.requireNonNull; public class CatalogAccessControlRule { + public static final CatalogAccessControlRule ALLOW_ALL = new CatalogAccessControlRule( + ALL, + Optional.empty(), + Optional.empty(), + Optional.empty()); + private final AccessMode accessMode; private final Optional userRegex; private final Optional groupRegex; diff --git a/presto-plugin-toolkit/src/main/java/io/prestosql/plugin/base/security/CatalogSchemaAccessControlRule.java b/presto-plugin-toolkit/src/main/java/io/prestosql/plugin/base/security/CatalogSchemaAccessControlRule.java new file mode 100644 index 000000000000..c1c562c3d5ef --- /dev/null +++ b/presto-plugin-toolkit/src/main/java/io/prestosql/plugin/base/security/CatalogSchemaAccessControlRule.java @@ -0,0 +1,81 @@ +/* + * Licensed 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 io.prestosql.plugin.base.security; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.prestosql.spi.connector.CatalogSchemaName; + +import java.util.Optional; +import java.util.Set; +import java.util.regex.Pattern; + +import static java.util.Objects.requireNonNull; + +public class CatalogSchemaAccessControlRule +{ + public static final CatalogSchemaAccessControlRule ALLOW_ALL = new CatalogSchemaAccessControlRule(SchemaAccessControlRule.ALLOW_ALL, Optional.empty()); + + private final SchemaAccessControlRule schemaAccessControlRule; + private final Optional catalogRegex; + + @JsonCreator + public CatalogSchemaAccessControlRule( + @JsonProperty("owner") boolean owner, + @JsonProperty("user") Optional userRegex, + @JsonProperty("group") Optional groupRegex, + @JsonProperty("schema") Optional schemaRegex, + @JsonProperty("catalog") Optional catalogRegex) + { + this.schemaAccessControlRule = new SchemaAccessControlRule(owner, userRegex, groupRegex, schemaRegex); + this.catalogRegex = requireNonNull(catalogRegex, "catalogRegex is null"); + } + + private CatalogSchemaAccessControlRule(SchemaAccessControlRule schemaAccessControlRule, Optional catalogRegex) + { + this.schemaAccessControlRule = schemaAccessControlRule; + this.catalogRegex = catalogRegex; + } + + public Optional match(String user, Set groups, CatalogSchemaName schema) + { + if (!catalogRegex.map(regex -> regex.matcher(schema.getCatalogName()).matches()).orElse(true)) { + return Optional.empty(); + } + return schemaAccessControlRule.match(user, groups, schema.getSchemaName()); + } + + Optional toAnyCatalogPermissionsRule() + { + if (!schemaAccessControlRule.isOwner()) { + return Optional.empty(); + } + return Optional.of(new AnyCatalogPermissionsRule( + schemaAccessControlRule.getUserRegex(), + schemaAccessControlRule.getGroupRegex(), + catalogRegex)); + } + + Optional toAnyCatalogSchemaPermissionsRule() + { + if (!schemaAccessControlRule.isOwner()) { + return Optional.empty(); + } + return Optional.of(new AnyCatalogSchemaPermissionsRule( + schemaAccessControlRule.getUserRegex(), + schemaAccessControlRule.getGroupRegex(), + catalogRegex, + schemaAccessControlRule.getSchemaRegex())); + } +} diff --git a/presto-plugin-toolkit/src/main/java/io/prestosql/plugin/base/security/CatalogSessionPropertyAccessControlRule.java b/presto-plugin-toolkit/src/main/java/io/prestosql/plugin/base/security/CatalogSessionPropertyAccessControlRule.java new file mode 100644 index 000000000000..55ead88eb4b8 --- /dev/null +++ b/presto-plugin-toolkit/src/main/java/io/prestosql/plugin/base/security/CatalogSessionPropertyAccessControlRule.java @@ -0,0 +1,67 @@ +/* + * Licensed 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 io.prestosql.plugin.base.security; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.Optional; +import java.util.Set; +import java.util.regex.Pattern; + +import static java.util.Objects.requireNonNull; + +public class CatalogSessionPropertyAccessControlRule +{ + public static final CatalogSessionPropertyAccessControlRule ALLOW_ALL = new CatalogSessionPropertyAccessControlRule( + true, + Optional.empty(), + Optional.empty(), + Optional.empty(), + Optional.empty()); + + private final Optional catalogRegex; + private final SessionPropertyAccessControlRule sessionPropertyAccessControlRule; + + @JsonCreator + public CatalogSessionPropertyAccessControlRule( + @JsonProperty("allow") boolean allow, + @JsonProperty("user") Optional userRegex, + @JsonProperty("group") Optional groupRegex, + @JsonProperty("property") Optional propertyRegex, + @JsonProperty("catalog") Optional catalogRegex) + { + this.sessionPropertyAccessControlRule = new SessionPropertyAccessControlRule(allow, userRegex, groupRegex, propertyRegex); + this.catalogRegex = requireNonNull(catalogRegex, "catalogRegex is null"); + } + + public Optional match(String user, Set groups, String catalog, String property) + { + if (!catalogRegex.map(regex -> regex.matcher(catalog).matches()).orElse(true)) { + return Optional.empty(); + } + return sessionPropertyAccessControlRule.match(user, groups, property); + } + + Optional toAnyCatalogPermissionsRule() + { + if (!sessionPropertyAccessControlRule.isAllow()) { + return Optional.empty(); + } + return Optional.of(new AnyCatalogPermissionsRule( + sessionPropertyAccessControlRule.getUserRegex(), + sessionPropertyAccessControlRule.getGroupRegex(), + catalogRegex)); + } +} diff --git a/presto-plugin-toolkit/src/main/java/io/prestosql/plugin/base/security/CatalogTableAccessControlRule.java b/presto-plugin-toolkit/src/main/java/io/prestosql/plugin/base/security/CatalogTableAccessControlRule.java new file mode 100644 index 000000000000..8934e198e266 --- /dev/null +++ b/presto-plugin-toolkit/src/main/java/io/prestosql/plugin/base/security/CatalogTableAccessControlRule.java @@ -0,0 +1,83 @@ +/* + * Licensed 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 io.prestosql.plugin.base.security; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.prestosql.plugin.base.security.TableAccessControlRule.TablePrivilege; +import io.prestosql.spi.connector.CatalogSchemaTableName; + +import java.util.Optional; +import java.util.Set; +import java.util.regex.Pattern; + +import static java.util.Objects.requireNonNull; + +public class CatalogTableAccessControlRule +{ + public static final CatalogTableAccessControlRule ALLOW_ALL = new CatalogTableAccessControlRule(TableAccessControlRule.ALLOW_ALL, Optional.empty()); + + private final TableAccessControlRule tableAccessControlRule; + private final Optional catalogRegex; + + @JsonCreator + public CatalogTableAccessControlRule( + @JsonProperty("privileges") Set privileges, + @JsonProperty("user") Optional userRegex, + @JsonProperty("group") Optional groupRegex, + @JsonProperty("schema") Optional schemaRegex, + @JsonProperty("table") Optional tableRegex, + @JsonProperty("catalog") Optional catalogRegex) + { + this.tableAccessControlRule = new TableAccessControlRule(privileges, userRegex, groupRegex, schemaRegex, tableRegex); + this.catalogRegex = requireNonNull(catalogRegex, "catalogRegex is null"); + } + + public CatalogTableAccessControlRule(TableAccessControlRule tableAccessControlRule, Optional catalogRegex) + { + this.tableAccessControlRule = tableAccessControlRule; + this.catalogRegex = catalogRegex; + } + + public Optional> match(String user, Set groups, CatalogSchemaTableName table) + { + if (!catalogRegex.map(regex -> regex.matcher(table.getCatalogName()).matches()).orElse(true)) { + return Optional.empty(); + } + return tableAccessControlRule.match(user, groups, table.getSchemaTableName()); + } + + Optional toAnyCatalogPermissionsRule() + { + if (tableAccessControlRule.getPrivileges().isEmpty()) { + return Optional.empty(); + } + return Optional.of(new AnyCatalogPermissionsRule( + tableAccessControlRule.getUserRegex(), + tableAccessControlRule.getGroupRegex(), + catalogRegex)); + } + + Optional toAnyCatalogSchemaPermissionsRule() + { + if (tableAccessControlRule.getPrivileges().isEmpty()) { + return Optional.empty(); + } + return Optional.of(new AnyCatalogSchemaPermissionsRule( + tableAccessControlRule.getUserRegex(), + tableAccessControlRule.getGroupRegex(), + catalogRegex, + tableAccessControlRule.getSchemaRegex())); + } +} diff --git a/presto-plugin-toolkit/src/main/java/io/prestosql/plugin/base/security/FileBasedAccessControl.java b/presto-plugin-toolkit/src/main/java/io/prestosql/plugin/base/security/FileBasedAccessControl.java index 763bfcd16745..03db6477e940 100644 --- a/presto-plugin-toolkit/src/main/java/io/prestosql/plugin/base/security/FileBasedAccessControl.java +++ b/presto-plugin-toolkit/src/main/java/io/prestosql/plugin/base/security/FileBasedAccessControl.java @@ -35,6 +35,7 @@ import java.util.Set; import java.util.function.Predicate; +import static com.google.common.collect.ImmutableSet.toImmutableSet; import static io.prestosql.plugin.base.security.TableAccessControlRule.TablePrivilege.DELETE; import static io.prestosql.plugin.base.security.TableAccessControlRule.TablePrivilege.GRANT_SELECT; import static io.prestosql.plugin.base.security.TableAccessControlRule.TablePrivilege.INSERT; @@ -44,28 +45,34 @@ import static io.prestosql.spi.security.AccessDeniedException.denyAddColumn; import static io.prestosql.spi.security.AccessDeniedException.denyCommentColumn; import static io.prestosql.spi.security.AccessDeniedException.denyCommentTable; +import static io.prestosql.spi.security.AccessDeniedException.denyCreateRole; import static io.prestosql.spi.security.AccessDeniedException.denyCreateSchema; import static io.prestosql.spi.security.AccessDeniedException.denyCreateTable; import static io.prestosql.spi.security.AccessDeniedException.denyCreateView; import static io.prestosql.spi.security.AccessDeniedException.denyCreateViewWithSelect; import static io.prestosql.spi.security.AccessDeniedException.denyDeleteTable; import static io.prestosql.spi.security.AccessDeniedException.denyDropColumn; +import static io.prestosql.spi.security.AccessDeniedException.denyDropRole; import static io.prestosql.spi.security.AccessDeniedException.denyDropSchema; import static io.prestosql.spi.security.AccessDeniedException.denyDropTable; import static io.prestosql.spi.security.AccessDeniedException.denyDropView; +import static io.prestosql.spi.security.AccessDeniedException.denyGrantRoles; import static io.prestosql.spi.security.AccessDeniedException.denyGrantTablePrivilege; import static io.prestosql.spi.security.AccessDeniedException.denyInsertTable; import static io.prestosql.spi.security.AccessDeniedException.denyRenameColumn; import static io.prestosql.spi.security.AccessDeniedException.denyRenameSchema; import static io.prestosql.spi.security.AccessDeniedException.denyRenameTable; import static io.prestosql.spi.security.AccessDeniedException.denyRenameView; +import static io.prestosql.spi.security.AccessDeniedException.denyRevokeRoles; import static io.prestosql.spi.security.AccessDeniedException.denyRevokeTablePrivilege; import static io.prestosql.spi.security.AccessDeniedException.denySelectTable; import static io.prestosql.spi.security.AccessDeniedException.denySetCatalogSessionProperty; +import static io.prestosql.spi.security.AccessDeniedException.denySetRole; import static io.prestosql.spi.security.AccessDeniedException.denySetSchemaAuthorization; import static io.prestosql.spi.security.AccessDeniedException.denyShowColumns; import static io.prestosql.spi.security.AccessDeniedException.denyShowCreateSchema; import static io.prestosql.spi.security.AccessDeniedException.denyShowCreateTable; +import static io.prestosql.spi.security.AccessDeniedException.denyShowTables; public class FileBasedAccessControl implements ConnectorAccessControl @@ -75,6 +82,7 @@ public class FileBasedAccessControl private final List schemaRules; private final List tableRules; private final List sessionPropertyRules; + private final Set anySchemaPermissionsRules; @Inject public FileBasedAccessControl(FileBasedAccessControlConfig config) @@ -84,6 +92,18 @@ public FileBasedAccessControl(FileBasedAccessControlConfig config) this.schemaRules = rules.getSchemaRules(); this.tableRules = rules.getTableRules(); this.sessionPropertyRules = rules.getSessionPropertyRules(); + ImmutableSet.Builder anySchemaPermissionsRules = ImmutableSet.builder(); + schemaRules.stream() + .map(SchemaAccessControlRule::toAnySchemaPermissionsRule) + .filter(Optional::isPresent) + .map(Optional::get) + .forEach(anySchemaPermissionsRules::add); + tableRules.stream() + .map(TableAccessControlRule::toAnySchemaPermissionsRule) + .filter(Optional::isPresent) + .map(Optional::get) + .forEach(anySchemaPermissionsRules::add); + this.anySchemaPermissionsRules = anySchemaPermissionsRules.build(); } @Override @@ -126,7 +146,9 @@ public void checkCanShowSchemas(ConnectorSecurityContext context) @Override public Set filterSchemas(ConnectorSecurityContext context, Set schemaNames) { - return schemaNames; + return schemaNames.stream() + .filter(schemaName -> checkAnySchemaAccess(context, schemaName)) + .collect(toImmutableSet()); } @Override @@ -148,7 +170,8 @@ public void checkCanShowCreateTable(ConnectorSecurityContext context, SchemaTabl @Override public void checkCanCreateTable(ConnectorSecurityContext context, SchemaTableName tableName) { - if (!isSchemaOwner(context, tableName.getSchemaName())) { + // check if user will be an owner of the table after creation + if (!checkTablePermission(context, tableName, OWNERSHIP)) { denyCreateTable(tableName.toString()); } } @@ -164,12 +187,17 @@ public void checkCanDropTable(ConnectorSecurityContext context, SchemaTableName @Override public void checkCanShowTables(ConnectorSecurityContext context, String schemaName) { + if (!checkAnySchemaAccess(context, schemaName)) { + denyShowTables(schemaName); + } } @Override public Set filterTables(ConnectorSecurityContext context, Set tableNames) { - return tableNames; + return tableNames.stream() + .filter(tableName -> isSchemaOwner(context, tableName.getSchemaName()) || checkAnyTablePermission(context, tableName)) + .collect(toImmutableSet()); } @Override @@ -192,6 +220,7 @@ public List filterColumns(ConnectorSecurityContext context, Sche @Override public void checkCanRenameTable(ConnectorSecurityContext context, SchemaTableName tableName, SchemaTableName newTableName) { + // check if user owns the existing table, and if they will be an owner of the table after the rename if (!checkTablePermission(context, tableName, OWNERSHIP) || !checkTablePermission(context, newTableName, OWNERSHIP)) { denyRenameTable(tableName.toString(), newTableName.toString()); } @@ -265,7 +294,8 @@ public void checkCanDeleteFromTable(ConnectorSecurityContext context, SchemaTabl @Override public void checkCanCreateView(ConnectorSecurityContext context, SchemaTableName viewName) { - if (!isSchemaOwner(context, viewName.getSchemaName())) { + // check if user will be an owner of the view after creation + if (!checkTablePermission(context, viewName, OWNERSHIP)) { denyCreateView(viewName.toString()); } } @@ -273,6 +303,7 @@ public void checkCanCreateView(ConnectorSecurityContext context, SchemaTableName @Override public void checkCanRenameView(ConnectorSecurityContext context, SchemaTableName viewName, SchemaTableName newViewName) { + // check if user owns the existing view, and if they will be an owner of the view after the rename if (!checkTablePermission(context, viewName, OWNERSHIP) || !checkTablePermission(context, newViewName, OWNERSHIP)) { denyRenameView(viewName.toString(), newViewName.toString()); } @@ -309,62 +340,69 @@ public void checkCanSetCatalogSessionProperty(ConnectorSecurityContext context, @Override public void checkCanGrantTablePrivilege(ConnectorSecurityContext context, Privilege privilege, SchemaTableName tableName, PrestoPrincipal grantee, boolean grantOption) { - if (!checkTablePermission(context, tableName, OWNERSHIP)) { - denyGrantTablePrivilege(privilege.name(), tableName.toString()); - } + // file based rules are immutable + denyGrantTablePrivilege(privilege.toString(), tableName.toString()); } @Override public void checkCanRevokeTablePrivilege(ConnectorSecurityContext context, Privilege privilege, SchemaTableName tableName, PrestoPrincipal revokee, boolean grantOption) { - if (!checkTablePermission(context, tableName, OWNERSHIP)) { - denyRevokeTablePrivilege(privilege.name(), tableName.toString()); - } + // file based rules are immutable + denyRevokeTablePrivilege(privilege.toString(), tableName.toString()); } @Override public void checkCanCreateRole(ConnectorSecurityContext context, String role, Optional grantor) { + denyCreateRole(role); } @Override public void checkCanDropRole(ConnectorSecurityContext context, String role) { + denyDropRole(role); } @Override public void checkCanGrantRoles(ConnectorSecurityContext context, Set roles, Set grantees, boolean adminOption, Optional grantor, String catalogName) { + denyGrantRoles(roles, grantees); } @Override public void checkCanRevokeRoles(ConnectorSecurityContext context, Set roles, Set grantees, boolean adminOption, Optional grantor, String catalogName) { + denyRevokeRoles(roles, grantees); } @Override public void checkCanSetRole(ConnectorSecurityContext context, String role, String catalogName) { + denySetRole(role); } @Override public void checkCanShowRoleAuthorizationDescriptors(ConnectorSecurityContext context, String catalogName) { + // allow, no roles are supported so show will always be empty } @Override public void checkCanShowRoles(ConnectorSecurityContext context, String catalogName) { + // allow, no roles are supported so show will always be empty } @Override public void checkCanShowCurrentRoles(ConnectorSecurityContext context, String catalogName) { + // allow, no roles are supported so show will always be empty } @Override public void checkCanShowRoleGrants(ConnectorSecurityContext context, String catalogName) { + // allow, no roles are supported so show will always be empty } @Override @@ -401,9 +439,9 @@ private boolean checkAnyTablePermission(ConnectorSecurityContext context, Schema return checkTablePermission(context, tableName, privileges -> !privileges.isEmpty()); } - private boolean checkTablePermission(ConnectorSecurityContext context, SchemaTableName tableName, TablePrivilege... requiredPrivileges) + private boolean checkTablePermission(ConnectorSecurityContext context, SchemaTableName tableName, TablePrivilege requiredPrivilege) { - return checkTablePermission(context, tableName, privileges -> privileges.containsAll(ImmutableSet.copyOf(requiredPrivileges))); + return checkTablePermission(context, tableName, privileges -> privileges.contains(requiredPrivilege)); } private boolean checkTablePermission(ConnectorSecurityContext context, SchemaTableName tableName, Predicate> checkPrivileges) @@ -422,6 +460,12 @@ private boolean checkTablePermission(ConnectorSecurityContext context, SchemaTab return false; } + private boolean checkAnySchemaAccess(ConnectorSecurityContext context, String schemaName) + { + ConnectorIdentity identity = context.getIdentity(); + return anySchemaPermissionsRules.stream().anyMatch(rule -> rule.match(identity.getUser(), identity.getGroups(), schemaName)); + } + private boolean isSchemaOwner(ConnectorSecurityContext context, String schemaName) { ConnectorIdentity identity = context.getIdentity(); diff --git a/presto-plugin-toolkit/src/main/java/io/prestosql/plugin/base/security/FileBasedSystemAccessControl.java b/presto-plugin-toolkit/src/main/java/io/prestosql/plugin/base/security/FileBasedSystemAccessControl.java index 817e6f12067a..1b05a426cc74 100644 --- a/presto-plugin-toolkit/src/main/java/io/prestosql/plugin/base/security/FileBasedSystemAccessControl.java +++ b/presto-plugin-toolkit/src/main/java/io/prestosql/plugin/base/security/FileBasedSystemAccessControl.java @@ -20,6 +20,7 @@ import io.airlift.log.Logger; import io.airlift.units.Duration; import io.prestosql.plugin.base.security.CatalogAccessControlRule.AccessMode; +import io.prestosql.plugin.base.security.TableAccessControlRule.TablePrivilege; import io.prestosql.spi.PrestoException; import io.prestosql.spi.connector.CatalogSchemaName; import io.prestosql.spi.connector.CatalogSchemaRoutineName; @@ -52,6 +53,7 @@ import static io.prestosql.plugin.base.security.CatalogAccessControlRule.AccessMode.READ_ONLY; import static io.prestosql.plugin.base.security.FileBasedAccessControlConfig.SECURITY_REFRESH_PERIOD; import static io.prestosql.plugin.base.security.TableAccessControlRule.TablePrivilege.DELETE; +import static io.prestosql.plugin.base.security.TableAccessControlRule.TablePrivilege.GRANT_SELECT; import static io.prestosql.plugin.base.security.TableAccessControlRule.TablePrivilege.INSERT; import static io.prestosql.plugin.base.security.TableAccessControlRule.TablePrivilege.OWNERSHIP; import static io.prestosql.plugin.base.security.TableAccessControlRule.TablePrivilege.SELECT; @@ -80,11 +82,15 @@ import static io.prestosql.spi.security.AccessDeniedException.denyRenameView; import static io.prestosql.spi.security.AccessDeniedException.denyRevokeTablePrivilege; import static io.prestosql.spi.security.AccessDeniedException.denySelectTable; +import static io.prestosql.spi.security.AccessDeniedException.denySetCatalogSessionProperty; import static io.prestosql.spi.security.AccessDeniedException.denySetSchemaAuthorization; +import static io.prestosql.spi.security.AccessDeniedException.denySetSystemSessionProperty; import static io.prestosql.spi.security.AccessDeniedException.denySetUser; import static io.prestosql.spi.security.AccessDeniedException.denyShowColumns; import static io.prestosql.spi.security.AccessDeniedException.denyShowCreateSchema; import static io.prestosql.spi.security.AccessDeniedException.denyShowCreateTable; +import static io.prestosql.spi.security.AccessDeniedException.denyShowSchemas; +import static io.prestosql.spi.security.AccessDeniedException.denyShowTables; import static io.prestosql.spi.security.AccessDeniedException.denyViewQuery; import static io.prestosql.spi.security.AccessDeniedException.denyWriteSystemInformationAccess; import static java.lang.String.format; @@ -104,8 +110,12 @@ public class FileBasedSystemAccessControl private final Optional> impersonationRules; private final Optional> principalUserMatchRules; private final Optional> systemInformationRules; - private final Optional> schemaRules; - private final Optional> tableRules; + private final List schemaRules; + private final List tableRules; + private final List sessionPropertyRules; + private final List catalogSessionPropertyRules; + private final Set anyCatalogPermissionsRules; + private final Set anyCatalogSchemaPermissionsRules; private FileBasedSystemAccessControl( List catalogRules, @@ -113,8 +123,10 @@ private FileBasedSystemAccessControl( Optional> impersonationRules, Optional> principalUserMatchRules, Optional> systemInformationRules, - Optional> schemaRules, - Optional> tableRules) + List schemaRules, + List tableRules, + List sessionPropertyRules, + List catalogSessionPropertyRules) { this.catalogRules = catalogRules; this.queryAccessRules = queryAccessRules; @@ -123,6 +135,39 @@ private FileBasedSystemAccessControl( this.systemInformationRules = systemInformationRules; this.schemaRules = schemaRules; this.tableRules = tableRules; + this.sessionPropertyRules = sessionPropertyRules; + this.catalogSessionPropertyRules = catalogSessionPropertyRules; + + ImmutableSet.Builder anyCatalogPermissionsRules = ImmutableSet.builder(); + schemaRules.stream() + .map(CatalogSchemaAccessControlRule::toAnyCatalogPermissionsRule) + .filter(Optional::isPresent) + .map(Optional::get) + .forEach(anyCatalogPermissionsRules::add); + tableRules.stream() + .map(CatalogTableAccessControlRule::toAnyCatalogPermissionsRule) + .filter(Optional::isPresent) + .map(Optional::get) + .forEach(anyCatalogPermissionsRules::add); + catalogSessionPropertyRules.stream() + .map(CatalogSessionPropertyAccessControlRule::toAnyCatalogPermissionsRule) + .filter(Optional::isPresent) + .map(Optional::get) + .forEach(anyCatalogPermissionsRules::add); + this.anyCatalogPermissionsRules = anyCatalogPermissionsRules.build(); + + ImmutableSet.Builder anyCatalogSchemaPermissionsRules = ImmutableSet.builder(); + schemaRules.stream() + .map(CatalogSchemaAccessControlRule::toAnyCatalogSchemaPermissionsRule) + .filter(Optional::isPresent) + .map(Optional::get) + .forEach(anyCatalogSchemaPermissionsRules::add); + tableRules.stream() + .map(CatalogTableAccessControlRule::toAnyCatalogSchemaPermissionsRule) + .filter(Optional::isPresent) + .map(Optional::get) + .forEach(anyCatalogSchemaPermissionsRules::add); + this.anyCatalogSchemaPermissionsRules = anyCatalogSchemaPermissionsRules.build(); } public static class Factory @@ -170,36 +215,44 @@ public SystemAccessControl create(Map config) return create(configFileName); } - private PrestoException invalidRefreshPeriodException(Map config, String configFileName) + private static PrestoException invalidRefreshPeriodException(Map config, String configFileName) { return new PrestoException( CONFIGURATION_INVALID, format("Invalid duration value '%s' for property '%s' in '%s'", config.get(SECURITY_REFRESH_PERIOD), SECURITY_REFRESH_PERIOD, configFileName)); } - private SystemAccessControl create(String configFileName) + private static SystemAccessControl create(String configFileName) { FileBasedSystemAccessControlRules rules = parseJson(Paths.get(configFileName), FileBasedSystemAccessControlRules.class); - - ImmutableList.Builder catalogRulesBuilder = ImmutableList.builder(); - catalogRulesBuilder.addAll(rules.getCatalogRules()); - - // Hack to allow Presto Admin to access the "system" catalog for retrieving server status. - // todo Change userRegex from ".*" to one particular user that Presto Admin will be restricted to run as - catalogRulesBuilder.add(new CatalogAccessControlRule( - ALL, - Optional.of(Pattern.compile(".*")), - Optional.empty(), - Optional.of(Pattern.compile("system")))); - + List catalogAccessControlRules; + if (rules.getCatalogRules().isPresent()) { + ImmutableList.Builder catalogRulesBuilder = ImmutableList.builder(); + catalogRulesBuilder.addAll(rules.getCatalogRules().get()); + + // Hack to allow Presto Admin to access the "system" catalog for retrieving server status. + // todo Change userRegex from ".*" to one particular user that Presto Admin will be restricted to run as + catalogRulesBuilder.add(new CatalogAccessControlRule( + ALL, + Optional.of(Pattern.compile(".*")), + Optional.empty(), + Optional.of(Pattern.compile("system")))); + catalogAccessControlRules = catalogRulesBuilder.build(); + } + else { + // if no rules are defined then all access is allowed + catalogAccessControlRules = ImmutableList.of(CatalogAccessControlRule.ALLOW_ALL); + } return new FileBasedSystemAccessControl( - catalogRulesBuilder.build(), + catalogAccessControlRules, rules.getQueryAccessRules(), rules.getImpersonationRules(), rules.getPrincipalUserMatchRules(), rules.getSystemInformationRules(), - rules.getSchemaRules(), - rules.getTableRules()); + rules.getSchemaRules().orElse(ImmutableList.of(CatalogSchemaAccessControlRule.ALLOW_ALL)), + rules.getTableRules().orElse(ImmutableList.of(CatalogTableAccessControlRule.ALLOW_ALL)), + rules.getSessionPropertyRules().orElse(ImmutableList.of(SessionPropertyAccessControlRule.ALLOW_ALL)), + rules.getCatalogSessionPropertyRules().orElse(ImmutableList.of(CatalogSessionPropertyAccessControlRule.ALLOW_ALL))); } } @@ -260,9 +313,6 @@ public void checkCanSetUser(Optional principal, String userName) @Override public void checkCanExecuteQuery(SystemSecurityContext context) { - if (queryAccessRules.isEmpty()) { - return; - } if (!canAccessQuery(context.getIdentity(), QueryAccessRule.AccessMode.EXECUTE)) { denyViewQuery(); } @@ -271,9 +321,6 @@ public void checkCanExecuteQuery(SystemSecurityContext context) @Override public void checkCanViewQueryOwnedBy(SystemSecurityContext context, String queryOwner) { - if (queryAccessRules.isEmpty()) { - return; - } if (!canAccessQuery(context.getIdentity(), QueryAccessRule.AccessMode.VIEW)) { denyViewQuery(); } @@ -294,9 +341,6 @@ public Set filterViewQueryOwnedBy(SystemSecurityContext context, Set> accessMode = rule.match(identity.getUser()); - if (accessMode.isPresent()) { - return accessMode.get().contains(requiredAccess); - } + if (queryAccessRules.isEmpty()) { + return true; + } + for (QueryAccessRule rule : queryAccessRules.get()) { + Optional> accessMode = rule.match(identity.getUser()); + if (accessMode.isPresent()) { + return accessMode.get().contains(requiredAccess); } } return false; @@ -345,12 +390,22 @@ private boolean checkCanSystemInformation(Identity identity, SystemInformationRu @Override public void checkCanSetSystemSessionProperty(SystemSecurityContext context, String propertyName) { + Identity identity = context.getIdentity(); + boolean allowed = sessionPropertyRules.stream() + .map(rule -> rule.match(identity.getUser(), identity.getGroups(), propertyName)) + .filter(Optional::isPresent) + .map(Optional::get) + .findFirst() + .orElse(false); + if (!allowed) { + denySetSystemSessionProperty(propertyName); + } } @Override public void checkCanAccessCatalog(SystemSecurityContext context, String catalogName) { - if (!canAccessCatalog(context.getIdentity(), catalogName, READ_ONLY)) { + if (!canAccessCatalog(context, catalogName, READ_ONLY)) { denyCatalogAccess(catalogName); } } @@ -360,28 +415,17 @@ public Set filterCatalogs(SystemSecurityContext context, Set cat { ImmutableSet.Builder filteredCatalogs = ImmutableSet.builder(); for (String catalog : catalogs) { - if (canAccessCatalog(context.getIdentity(), catalog, READ_ONLY)) { + if (checkAnyCatalogAccess(context, catalog)) { filteredCatalogs.add(catalog); } } return filteredCatalogs.build(); } - private boolean canAccessCatalog(Identity identity, String catalogName, AccessMode requiredAccess) - { - for (CatalogAccessControlRule rule : catalogRules) { - Optional accessMode = rule.match(identity.getUser(), identity.getGroups(), catalogName); - if (accessMode.isPresent()) { - return accessMode.get().implies(requiredAccess); - } - } - return false; - } - @Override public void checkCanCreateSchema(SystemSecurityContext context, CatalogSchemaName schema) { - if (!canAccessCatalog(context.getIdentity(), schema.getCatalogName(), ALL)) { + if (!isSchemaOwner(context, schema)) { denyCreateSchema(schema.toString()); } } @@ -389,82 +433,64 @@ public void checkCanCreateSchema(SystemSecurityContext context, CatalogSchemaNam @Override public void checkCanDropSchema(SystemSecurityContext context, CatalogSchemaName schema) { - if (!canAccessCatalog(context.getIdentity(), schema.getCatalogName(), ALL)) { + if (!isSchemaOwner(context, schema)) { denyDropSchema(schema.toString()); } - - if (!isSchemaOwner(context, schema.getSchemaName())) { - denyDropSchema(schema.getSchemaName()); - } } @Override public void checkCanRenameSchema(SystemSecurityContext context, CatalogSchemaName schema, String newSchemaName) { - if (!canAccessCatalog(context.getIdentity(), schema.getCatalogName(), ALL)) { + if (!isSchemaOwner(context, schema) || !isSchemaOwner(context, new CatalogSchemaName(schema.getCatalogName(), newSchemaName))) { denyRenameSchema(schema.toString(), newSchemaName); } - - if (!isSchemaOwner(context, schema.getSchemaName()) || !isSchemaOwner(context, newSchemaName)) { - denyRenameSchema(schema.getSchemaName(), newSchemaName); - } } @Override public void checkCanSetSchemaAuthorization(SystemSecurityContext context, CatalogSchemaName schema, PrestoPrincipal principal) { - if (!canAccessCatalog(context.getIdentity(), schema.getCatalogName(), ALL)) { + if (!isSchemaOwner(context, schema)) { denySetSchemaAuthorization(schema.toString(), principal); } - - if (!isSchemaOwner(context, schema.getSchemaName())) { - denySetSchemaAuthorization(schema.getSchemaName(), principal); - } } @Override public void checkCanShowSchemas(SystemSecurityContext context, String catalogName) { + if (!checkAnyCatalogAccess(context, catalogName)) { + denyShowSchemas(); + } } @Override public Set filterSchemas(SystemSecurityContext context, String catalogName, Set schemaNames) { - if (!canAccessCatalog(context.getIdentity(), catalogName, READ_ONLY)) { - return ImmutableSet.of(); - } - - return schemaNames; + return schemaNames.stream() + .filter(schemaName -> checkAnySchemaAccess(context, catalogName, schemaName)) + .collect(toImmutableSet()); } @Override public void checkCanShowCreateTable(SystemSecurityContext context, CatalogSchemaTableName table) { - if (!canAccessCatalog(context.getIdentity(), table.getCatalogName(), ALL)) { + if (!checkTablePermission(context, table, OWNERSHIP)) { denyShowCreateTable(table.toString()); } - - if (!checkTablePermission(context, table.getSchemaTableName(), OWNERSHIP)) { - denyShowCreateTable(table.getSchemaTableName().getTableName()); - } } @Override public void checkCanShowCreateSchema(SystemSecurityContext context, CatalogSchemaName schemaName) { - if (!canAccessCatalog(context.getIdentity(), schemaName.getCatalogName(), ALL)) { + if (!isSchemaOwner(context, schemaName)) { denyShowCreateSchema(schemaName.toString()); } - - if (!isSchemaOwner(context, schemaName.getSchemaName())) { - denyShowCreateSchema(schemaName.getSchemaName()); - } } @Override public void checkCanCreateTable(SystemSecurityContext context, CatalogSchemaTableName table) { - if (!canAccessCatalog(context.getIdentity(), table.getCatalogName(), ALL)) { + // check if user will be an owner of the table after creation + if (!checkTablePermission(context, table, OWNERSHIP)) { denyCreateTable(table.toString()); } } @@ -472,43 +498,32 @@ public void checkCanCreateTable(SystemSecurityContext context, CatalogSchemaTabl @Override public void checkCanDropTable(SystemSecurityContext context, CatalogSchemaTableName table) { - if (!canAccessCatalog(context.getIdentity(), table.getCatalogName(), ALL)) { + if (!checkTablePermission(context, table, OWNERSHIP)) { denyDropTable(table.toString()); } - - if (!checkTablePermission(context, table.getSchemaTableName(), OWNERSHIP)) { - denyDropTable(table.getSchemaTableName().getTableName()); - } } @Override public void checkCanRenameTable(SystemSecurityContext context, CatalogSchemaTableName table, CatalogSchemaTableName newTable) { - if (!canAccessCatalog(context.getIdentity(), table.getCatalogName(), ALL)) { + // check if user is an owner current table and will be an owner of the renamed table + if (!checkTablePermission(context, table, OWNERSHIP) || !checkTablePermission(context, newTable, OWNERSHIP)) { denyRenameTable(table.toString(), newTable.toString()); } - - if (!checkTablePermission(context, table.getSchemaTableName(), OWNERSHIP) || !checkTablePermission(context, newTable.getSchemaTableName(), OWNERSHIP)) { - denyRenameTable(table.getSchemaTableName().getTableName(), newTable.getSchemaTableName().getTableName()); - } } @Override public void checkCanSetTableComment(SystemSecurityContext context, CatalogSchemaTableName table) { - if (!canAccessCatalog(context.getIdentity(), table.getCatalogName(), ALL)) { + if (!checkTablePermission(context, table, OWNERSHIP)) { denyCommentTable(table.toString()); } - - if (!checkTablePermission(context, table.getSchemaTableName(), OWNERSHIP)) { - denyCommentTable(table.getSchemaTableName().getTableName()); - } } @Override public void checkCanSetColumnComment(SystemSecurityContext context, CatalogSchemaTableName table) { - if (!canAccessCatalog(context.getIdentity(), table.getCatalogName(), ALL)) { + if (!checkTablePermission(context, table, OWNERSHIP)) { denyCommentColumn(table.toString()); } } @@ -516,34 +531,32 @@ public void checkCanSetColumnComment(SystemSecurityContext context, CatalogSchem @Override public void checkCanShowTables(SystemSecurityContext context, CatalogSchemaName schema) { + if (!checkAnySchemaAccess(context, schema.getCatalogName(), schema.getSchemaName())) { + denyShowTables(schema.toString()); + } } @Override public Set filterTables(SystemSecurityContext context, String catalogName, Set tableNames) { - if (!canAccessCatalog(context.getIdentity(), catalogName, READ_ONLY)) { - return ImmutableSet.of(); - } - - return tableNames; + return tableNames.stream() + .filter(tableName -> isSchemaOwner(context, new CatalogSchemaName(catalogName, tableName.getSchemaName())) || + checkAnyTablePermission(context, new CatalogSchemaTableName(catalogName, tableName))) + .collect(toImmutableSet()); } @Override public void checkCanShowColumns(SystemSecurityContext context, CatalogSchemaTableName table) { - if (!checkAnyTablePermission(context, table.getSchemaTableName())) { - denyShowColumns(table.getSchemaTableName().getTableName()); + if (!checkAnyTablePermission(context, table)) { + denyShowColumns(table.toString()); } } @Override public List filterColumns(SystemSecurityContext context, CatalogSchemaTableName tableName, List columns) { - if (!canAccessCatalog(context.getIdentity(), tableName.getCatalogName(), READ_ONLY)) { - return ImmutableList.of(); - } - - if (!checkAnyTablePermission(context, tableName.getSchemaTableName())) { + if (!checkAnyTablePermission(context, tableName)) { return ImmutableList.of(); } @@ -553,75 +566,56 @@ public List filterColumns(SystemSecurityContext context, Catalog @Override public void checkCanAddColumn(SystemSecurityContext context, CatalogSchemaTableName table) { - if (!canAccessCatalog(context.getIdentity(), table.getCatalogName(), ALL)) { + if (!checkTablePermission(context, table, OWNERSHIP)) { denyAddColumn(table.toString()); } - - if (!checkTablePermission(context, table.getSchemaTableName(), OWNERSHIP)) { - denyAddColumn(table.getSchemaTableName().getTableName()); - } } @Override public void checkCanDropColumn(SystemSecurityContext context, CatalogSchemaTableName table) { - if (!canAccessCatalog(context.getIdentity(), table.getCatalogName(), ALL)) { + if (!checkTablePermission(context, table, OWNERSHIP)) { denyDropColumn(table.toString()); } - - if (!checkTablePermission(context, table.getSchemaTableName(), OWNERSHIP)) { - denyDropColumn(table.getSchemaTableName().getTableName()); - } } @Override public void checkCanRenameColumn(SystemSecurityContext context, CatalogSchemaTableName table) { - if (!canAccessCatalog(context.getIdentity(), table.getCatalogName(), ALL)) { + if (!checkTablePermission(context, table, OWNERSHIP)) { denyRenameColumn(table.toString()); } - - if (!checkTablePermission(context, table.getSchemaTableName(), OWNERSHIP)) { - denyRenameColumn(table.getSchemaTableName().getTableName()); - } } @Override public void checkCanSelectFromColumns(SystemSecurityContext context, CatalogSchemaTableName table, Set columns) { - if (!checkTablePermission(context, table.getSchemaTableName(), SELECT)) { - denySelectTable(table.getSchemaTableName().getTableName()); + if (!checkTablePermission(context, table, SELECT)) { + denySelectTable(table.toString()); } } @Override public void checkCanInsertIntoTable(SystemSecurityContext context, CatalogSchemaTableName table) { - if (!canAccessCatalog(context.getIdentity(), table.getCatalogName(), ALL)) { + if (!checkTablePermission(context, table, INSERT)) { denyInsertTable(table.toString()); } - - if (!checkTablePermission(context, table.getSchemaTableName(), INSERT)) { - denyInsertTable(table.getSchemaTableName().getTableName()); - } } @Override public void checkCanDeleteFromTable(SystemSecurityContext context, CatalogSchemaTableName table) { - if (!canAccessCatalog(context.getIdentity(), table.getCatalogName(), ALL)) { + if (!checkTablePermission(context, table, DELETE)) { denyDeleteTable(table.toString()); } - - if (!checkTablePermission(context, table.getSchemaTableName(), DELETE)) { - denyDeleteTable(table.getSchemaTableName().getTableName()); - } } @Override public void checkCanCreateView(SystemSecurityContext context, CatalogSchemaTableName view) { - if (!canAccessCatalog(context.getIdentity(), view.getCatalogName(), ALL)) { + // check if user will be an owner of the view after creation + if (!checkTablePermission(context, view, OWNERSHIP)) { denyCreateView(view.toString()); } } @@ -629,7 +623,8 @@ public void checkCanCreateView(SystemSecurityContext context, CatalogSchemaTable @Override public void checkCanRenameView(SystemSecurityContext context, CatalogSchemaTableName view, CatalogSchemaTableName newView) { - if (!canAccessCatalog(context.getIdentity(), view.getCatalogName(), ALL)) { + // check if user owns the existing view, and if they will be an owner of the view after the rename + if (!checkTablePermission(context, view, OWNERSHIP) || !checkTablePermission(context, newView, OWNERSHIP)) { denyRenameView(view.toString(), newView.toString()); } } @@ -637,7 +632,7 @@ public void checkCanRenameView(SystemSecurityContext context, CatalogSchemaTable @Override public void checkCanDropView(SystemSecurityContext context, CatalogSchemaTableName view) { - if (!canAccessCatalog(context.getIdentity(), view.getCatalogName(), ALL)) { + if (!checkTablePermission(context, view, OWNERSHIP)) { denyDropView(view.toString()); } } @@ -645,7 +640,11 @@ public void checkCanDropView(SystemSecurityContext context, CatalogSchemaTableNa @Override public void checkCanCreateViewWithSelectFromColumns(SystemSecurityContext context, CatalogSchemaTableName table, Set columns) { - if (!canAccessCatalog(context.getIdentity(), table.getCatalogName(), ALL)) { + // TODO: implement column level permissions + if (!checkTablePermission(context, table, SELECT)) { + denySelectTable(table.toString()); + } + if (!checkTablePermission(context, table, GRANT_SELECT)) { denyCreateViewWithSelect(table.toString(), context.getIdentity()); } } @@ -658,29 +657,31 @@ public void checkCanGrantExecuteFunctionPrivilege(SystemSecurityContext context, @Override public void checkCanSetCatalogSessionProperty(SystemSecurityContext context, String catalogName, String propertyName) { + Identity identity = context.getIdentity(); + boolean allowed = canAccessCatalog(context, catalogName, READ_ONLY) && catalogSessionPropertyRules.stream() + .map(rule -> rule.match(identity.getUser(), identity.getGroups(), catalogName, propertyName)) + .filter(Optional::isPresent) + .map(Optional::get) + .findFirst() + .orElse(false); + if (!allowed) { + denySetCatalogSessionProperty(propertyName); + } } @Override public void checkCanGrantTablePrivilege(SystemSecurityContext context, Privilege privilege, CatalogSchemaTableName table, PrestoPrincipal grantee, boolean grantOption) { - if (!canAccessCatalog(context.getIdentity(), table.getCatalogName(), ALL)) { - denyGrantTablePrivilege(privilege.toString(), table.toString()); - } - - if (!checkTablePermission(context, table.getSchemaTableName(), OWNERSHIP)) { - denyGrantTablePrivilege(privilege.name(), table.getSchemaTableName().getTableName()); + if (!checkTablePermission(context, table, OWNERSHIP)) { + denyGrantTablePrivilege(privilege.name(), table.toString()); } } @Override public void checkCanRevokeTablePrivilege(SystemSecurityContext context, Privilege privilege, CatalogSchemaTableName table, PrestoPrincipal revokee, boolean grantOption) { - if (!canAccessCatalog(context.getIdentity(), table.getCatalogName(), ALL)) { - denyRevokeTablePrivilege(privilege.toString(), table.toString()); - } - - if (!checkTablePermission(context, table.getSchemaTableName(), OWNERSHIP)) { - denyRevokeTablePrivilege(privilege.name(), table.getSchemaTableName().getTableName()); + if (!checkTablePermission(context, table, OWNERSHIP)) { + denyRevokeTablePrivilege(privilege.name(), table.toString()); } } @@ -717,15 +718,41 @@ public Optional getColumnMask(SystemSecurityContext context, Cat return Optional.empty(); } - private boolean isSchemaOwner(SystemSecurityContext context, String schemaName) + private boolean checkAnyCatalogAccess(SystemSecurityContext context, String catalogName) { - if (schemaRules.isEmpty()) { - return true; + Identity identity = context.getIdentity(); + return canAccessCatalog(context, catalogName, READ_ONLY) && + anyCatalogPermissionsRules.stream().anyMatch(rule -> rule.match(identity.getUser(), identity.getGroups(), catalogName)); + } + + private boolean canAccessCatalog(SystemSecurityContext context, String catalogName, AccessMode requiredAccess) + { + Identity identity = context.getIdentity(); + for (CatalogAccessControlRule rule : catalogRules) { + Optional accessMode = rule.match(identity.getUser(), identity.getGroups(), catalogName); + if (accessMode.isPresent()) { + return accessMode.get().implies(requiredAccess); + } + } + return false; + } + + private boolean checkAnySchemaAccess(SystemSecurityContext context, String catalogName, String schemaName) + { + Identity identity = context.getIdentity(); + return canAccessCatalog(context, catalogName, READ_ONLY) && + anyCatalogSchemaPermissionsRules.stream().anyMatch(rule -> rule.match(identity.getUser(), identity.getGroups(), catalogName, schemaName)); + } + + private boolean isSchemaOwner(SystemSecurityContext context, CatalogSchemaName schema) + { + if (!canAccessCatalog(context, schema.getCatalogName(), ALL)) { + return false; } Identity identity = context.getIdentity(); - for (SchemaAccessControlRule rule : schemaRules.get()) { - Optional owner = rule.match(identity.getUser(), identity.getGroups(), schemaName); + for (CatalogSchemaAccessControlRule rule : schemaRules) { + Optional owner = rule.match(identity.getUser(), identity.getGroups(), schema); if (owner.isPresent()) { return owner.get(); } @@ -733,29 +760,34 @@ private boolean isSchemaOwner(SystemSecurityContext context, String schemaName) return false; } - private boolean checkAnyTablePermission(SystemSecurityContext context, SchemaTableName tableName) + private boolean checkAnyTablePermission(SystemSecurityContext context, CatalogSchemaTableName table) { - return checkTablePermission(context, tableName, privileges -> !privileges.isEmpty()); + return checkTablePermission(context, table, READ_ONLY, privileges -> !privileges.isEmpty()); } - private boolean checkTablePermission(SystemSecurityContext context, SchemaTableName tableName, TableAccessControlRule.TablePrivilege... requiredPrivileges) + private boolean checkTablePermission(SystemSecurityContext context, CatalogSchemaTableName table, TableAccessControlRule.TablePrivilege requiredPrivilege) { - return checkTablePermission(context, tableName, privileges -> privileges.containsAll(ImmutableSet.copyOf(requiredPrivileges))); + AccessMode requiredCatalogAccess = requiredPrivilege == SELECT || requiredPrivilege == GRANT_SELECT ? READ_ONLY : ALL; + return checkTablePermission(context, table, requiredCatalogAccess, privileges -> privileges.contains(requiredPrivilege)); } - private boolean checkTablePermission(SystemSecurityContext context, SchemaTableName tableName, Predicate> checkPrivileges) + private boolean checkTablePermission( + SystemSecurityContext context, + CatalogSchemaTableName table, + AccessMode requiredCatalogAccess, + Predicate> checkPrivileges) { - if (tableRules.isEmpty()) { - return true; + if (!canAccessCatalog(context, table.getCatalogName(), requiredCatalogAccess)) { + return false; } - if (INFORMATION_SCHEMA_NAME.equals(tableName.getSchemaName())) { + if (INFORMATION_SCHEMA_NAME.equals(table.getSchemaTableName().getSchemaName())) { return true; } Identity identity = context.getIdentity(); - for (TableAccessControlRule rule : tableRules.get()) { - Optional> tablePrivileges = rule.match(identity.getUser(), identity.getGroups(), tableName); + for (CatalogTableAccessControlRule rule : tableRules) { + Optional> tablePrivileges = rule.match(identity.getUser(), identity.getGroups(), table); if (tablePrivileges.isPresent()) { return checkPrivileges.test(tablePrivileges.get()); } diff --git a/presto-plugin-toolkit/src/main/java/io/prestosql/plugin/base/security/FileBasedSystemAccessControlRules.java b/presto-plugin-toolkit/src/main/java/io/prestosql/plugin/base/security/FileBasedSystemAccessControlRules.java index 461f378592c2..afc6964bd484 100644 --- a/presto-plugin-toolkit/src/main/java/io/prestosql/plugin/base/security/FileBasedSystemAccessControlRules.java +++ b/presto-plugin-toolkit/src/main/java/io/prestosql/plugin/base/security/FileBasedSystemAccessControlRules.java @@ -22,13 +22,15 @@ public class FileBasedSystemAccessControlRules { - private final List catalogRules; + private final Optional> catalogRules; private final Optional> queryAccessRules; private final Optional> impersonationRules; private final Optional> principalUserMatchRules; private final Optional> systemInformationRules; - private final Optional> schemaRules; - private final Optional> tableRules; + private final Optional> schemaRules; + private final Optional> tableRules; + private final Optional> sessionPropertyRules; + private final Optional> catalogSessionPropertyRules; @JsonCreator public FileBasedSystemAccessControlRules( @@ -37,19 +39,23 @@ public FileBasedSystemAccessControlRules( @JsonProperty("impersonation") Optional> impersonationRules, @JsonProperty("principals") Optional> principalUserMatchRules, @JsonProperty("system_information") Optional> systemInformationRules, - @JsonProperty("schemas") Optional> schemaAccessControlRules, - @JsonProperty("tables") Optional> tableAccessControlRules) + @JsonProperty("schemas") Optional> schemaAccessControlRules, + @JsonProperty("tables") Optional> tableAccessControlRules, + @JsonProperty("system_session_properties") Optional> sessionPropertyRules, + @JsonProperty("catalog_session_properties") Optional> catalogSessionPropertyRules) { - this.catalogRules = catalogRules.map(ImmutableList::copyOf).orElse(ImmutableList.of()); + this.catalogRules = catalogRules.map(ImmutableList::copyOf); this.queryAccessRules = queryAccessRules.map(ImmutableList::copyOf); this.principalUserMatchRules = principalUserMatchRules.map(ImmutableList::copyOf); this.impersonationRules = impersonationRules.map(ImmutableList::copyOf); this.systemInformationRules = systemInformationRules.map(ImmutableList::copyOf); this.schemaRules = schemaAccessControlRules.map(ImmutableList::copyOf); this.tableRules = tableAccessControlRules.map(ImmutableList::copyOf); + this.sessionPropertyRules = sessionPropertyRules; + this.catalogSessionPropertyRules = catalogSessionPropertyRules; } - public List getCatalogRules() + public Optional> getCatalogRules() { return catalogRules; } @@ -74,13 +80,23 @@ public Optional> getSystemInformationRules() return systemInformationRules; } - public Optional> getSchemaRules() + public Optional> getSchemaRules() { return schemaRules; } - public Optional> getTableRules() + public Optional> getTableRules() { return tableRules; } + + public Optional> getSessionPropertyRules() + { + return sessionPropertyRules; + } + + public Optional> getCatalogSessionPropertyRules() + { + return catalogSessionPropertyRules; + } } diff --git a/presto-plugin-toolkit/src/main/java/io/prestosql/plugin/base/security/ImpersonationRule.java b/presto-plugin-toolkit/src/main/java/io/prestosql/plugin/base/security/ImpersonationRule.java index a2457c66e807..fb2d3d7d1449 100644 --- a/presto-plugin-toolkit/src/main/java/io/prestosql/plugin/base/security/ImpersonationRule.java +++ b/presto-plugin-toolkit/src/main/java/io/prestosql/plugin/base/security/ImpersonationRule.java @@ -13,6 +13,7 @@ */ package io.prestosql.plugin.base.security; +import com.fasterxml.jackson.annotation.JsonAlias; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; @@ -32,8 +33,8 @@ public class ImpersonationRule @JsonCreator public ImpersonationRule( - @JsonProperty("originalUser") Pattern originalUserPattern, - @JsonProperty("newUser") Pattern newUserPattern, + @JsonProperty("original_user") @JsonAlias("originalUser") Pattern originalUserPattern, + @JsonProperty("new_user") @JsonAlias("newUser") Pattern newUserPattern, @JsonProperty("allow") Boolean allow) { this.originalUserPattern = requireNonNull(originalUserPattern, "originalUserPattern is null"); diff --git a/presto-plugin-toolkit/src/main/java/io/prestosql/plugin/base/security/SchemaAccessControlRule.java b/presto-plugin-toolkit/src/main/java/io/prestosql/plugin/base/security/SchemaAccessControlRule.java index 62562896e4d8..0e452eb179ee 100644 --- a/presto-plugin-toolkit/src/main/java/io/prestosql/plugin/base/security/SchemaAccessControlRule.java +++ b/presto-plugin-toolkit/src/main/java/io/prestosql/plugin/base/security/SchemaAccessControlRule.java @@ -24,6 +24,12 @@ public class SchemaAccessControlRule { + public static final SchemaAccessControlRule ALLOW_ALL = new SchemaAccessControlRule( + true, + Optional.empty(), + Optional.empty(), + Optional.empty()); + private final boolean owner; private final Optional userRegex; private final Optional groupRegex; @@ -51,4 +57,32 @@ public Optional match(String user, Set groups, String schema) } return Optional.empty(); } + + Optional toAnySchemaPermissionsRule() + { + if (!owner) { + return Optional.empty(); + } + return Optional.of(new AnySchemaPermissionsRule(userRegex, groupRegex, schemaRegex)); + } + + boolean isOwner() + { + return owner; + } + + Optional getUserRegex() + { + return userRegex; + } + + Optional getGroupRegex() + { + return groupRegex; + } + + Optional getSchemaRegex() + { + return schemaRegex; + } } diff --git a/presto-plugin-toolkit/src/main/java/io/prestosql/plugin/base/security/SessionPropertyAccessControlRule.java b/presto-plugin-toolkit/src/main/java/io/prestosql/plugin/base/security/SessionPropertyAccessControlRule.java index 3d94c14594ca..ce6bc81e0270 100644 --- a/presto-plugin-toolkit/src/main/java/io/prestosql/plugin/base/security/SessionPropertyAccessControlRule.java +++ b/presto-plugin-toolkit/src/main/java/io/prestosql/plugin/base/security/SessionPropertyAccessControlRule.java @@ -24,6 +24,12 @@ public class SessionPropertyAccessControlRule { + public static final SessionPropertyAccessControlRule ALLOW_ALL = new SessionPropertyAccessControlRule( + true, + Optional.empty(), + Optional.empty(), + Optional.empty()); + private final boolean allow; private final Optional userRegex; private final Optional groupRegex; @@ -42,6 +48,21 @@ public SessionPropertyAccessControlRule( this.propertyRegex = requireNonNull(propertyRegex, "propertyRegex is null"); } + boolean isAllow() + { + return allow; + } + + Optional getUserRegex() + { + return userRegex; + } + + Optional getGroupRegex() + { + return groupRegex; + } + public Optional match(String user, Set groups, String property) { if (userRegex.map(regex -> regex.matcher(user).matches()).orElse(true) && diff --git a/presto-plugin-toolkit/src/main/java/io/prestosql/plugin/base/security/TableAccessControlRule.java b/presto-plugin-toolkit/src/main/java/io/prestosql/plugin/base/security/TableAccessControlRule.java index 4b38c1ee2415..b7f2c6358dab 100644 --- a/presto-plugin-toolkit/src/main/java/io/prestosql/plugin/base/security/TableAccessControlRule.java +++ b/presto-plugin-toolkit/src/main/java/io/prestosql/plugin/base/security/TableAccessControlRule.java @@ -26,6 +26,13 @@ public class TableAccessControlRule { + public static final TableAccessControlRule ALLOW_ALL = new TableAccessControlRule( + ImmutableSet.copyOf(TablePrivilege.values()), + Optional.empty(), + Optional.empty(), + Optional.empty(), + Optional.empty()); + private final Set privileges; private final Optional userRegex; private final Optional groupRegex; @@ -58,6 +65,34 @@ public Optional> match(String user, Set groups, Sche return Optional.empty(); } + Optional toAnySchemaPermissionsRule() + { + if (privileges.isEmpty()) { + return Optional.empty(); + } + return Optional.of(new AnySchemaPermissionsRule(userRegex, groupRegex, schemaRegex)); + } + + Set getPrivileges() + { + return privileges; + } + + Optional getUserRegex() + { + return userRegex; + } + + Optional getGroupRegex() + { + return groupRegex; + } + + Optional getSchemaRegex() + { + return schemaRegex; + } + public enum TablePrivilege { SELECT, INSERT, DELETE, OWNERSHIP, GRANT_SELECT diff --git a/presto-plugin-toolkit/src/test/java/io/prestosql/plugin/base/security/TestFileBasedAccessControl.java b/presto-plugin-toolkit/src/test/java/io/prestosql/plugin/base/security/TestFileBasedAccessControl.java index 3574873d8e8e..47078fb539ee 100644 --- a/presto-plugin-toolkit/src/test/java/io/prestosql/plugin/base/security/TestFileBasedAccessControl.java +++ b/presto-plugin-toolkit/src/test/java/io/prestosql/plugin/base/security/TestFileBasedAccessControl.java @@ -25,10 +25,12 @@ import io.prestosql.spi.security.ConnectorIdentity; import io.prestosql.spi.security.PrestoPrincipal; import io.prestosql.spi.security.PrincipalType; +import io.prestosql.spi.security.Privilege; import io.prestosql.spi.type.VarcharType; import org.testng.Assert.ThrowingRunnable; import org.testng.annotations.Test; +import java.util.Optional; import java.util.Set; import static io.prestosql.spi.testing.InterfaceTestUtils.assertAllMethodsOverridden; @@ -43,6 +45,66 @@ public class TestFileBasedAccessControl private static final ConnectorSecurityContext BOB = user("bob", ImmutableSet.of("staff")); private static final ConnectorSecurityContext CHARLIE = user("charlie", ImmutableSet.of("guests")); private static final ConnectorSecurityContext JOE = user("joe", ImmutableSet.of()); + private static final ConnectorSecurityContext UNKNOWN = user("unknown", ImmutableSet.of()); + + @Test + public void testEmptyFile() + { + ConnectorAccessControl accessControl = createAccessControl("empty.json"); + + accessControl.checkCanCreateSchema(UNKNOWN, "unknown"); + accessControl.checkCanDropSchema(UNKNOWN, "unknown"); + accessControl.checkCanRenameSchema(UNKNOWN, "unknown", "new_unknown"); + accessControl.checkCanSetSchemaAuthorization(UNKNOWN, "unknown", new PrestoPrincipal(PrincipalType.ROLE, "some_role")); + accessControl.checkCanShowCreateSchema(UNKNOWN, "unknown"); + + accessControl.checkCanSelectFromColumns(UNKNOWN, new SchemaTableName("unknown", "unknown"), ImmutableSet.of()); + accessControl.checkCanShowColumns(UNKNOWN, new SchemaTableName("unknown", "unknown")); + accessControl.checkCanInsertIntoTable(UNKNOWN, new SchemaTableName("unknown", "unknown")); + accessControl.checkCanDeleteFromTable(UNKNOWN, new SchemaTableName("unknown", "unknown")); + + accessControl.checkCanCreateTable(UNKNOWN, new SchemaTableName("unknown", "unknown")); + accessControl.checkCanDropTable(UNKNOWN, new SchemaTableName("unknown", "unknown")); + accessControl.checkCanRenameTable(UNKNOWN, + new SchemaTableName("unknown", "unknown"), + new SchemaTableName("unknown", "new_unknown")); + + accessControl.checkCanSetCatalogSessionProperty(UNKNOWN, "anything"); + + Set tables = ImmutableSet.builder() + .add(new SchemaTableName("secret", "any")) + .add(new SchemaTableName("any", "any")) + .build(); + assertEquals(accessControl.filterTables(UNKNOWN, tables), tables); + + // permissions management APIs are hard coded to deny + PrestoPrincipal someUser = new PrestoPrincipal(PrincipalType.USER, "some_user"); + assertDenied(() -> accessControl.checkCanGrantTablePrivilege(ADMIN, Privilege.SELECT, new SchemaTableName("any", "any"), someUser, false)); + assertDenied(() -> accessControl.checkCanRevokeTablePrivilege(ADMIN, Privilege.SELECT, new SchemaTableName("any", "any"), someUser, false)); + assertDenied(() -> accessControl.checkCanCreateRole(ADMIN, "role", Optional.empty())); + assertDenied(() -> accessControl.checkCanDropRole(ADMIN, "role")); + assertDenied(() -> accessControl.checkCanGrantRoles( + ADMIN, + ImmutableSet.of("test"), + ImmutableSet.of(someUser), + false, + Optional.empty(), + "any")); + assertDenied(() -> accessControl.checkCanRevokeRoles( + ADMIN, + ImmutableSet.of("test"), + ImmutableSet.of(someUser), + false, + Optional.empty(), + "any")); + assertDenied(() -> accessControl.checkCanSetRole(ADMIN, "role", "any")); + + // showing roles and permissions is hard coded to allow + accessControl.checkCanShowRoleAuthorizationDescriptors(UNKNOWN, "any"); + accessControl.checkCanShowRoles(UNKNOWN, "any"); + accessControl.checkCanShowCurrentRoles(UNKNOWN, "any"); + accessControl.checkCanShowRoleGrants(UNKNOWN, "any"); + } @Test public void testSchemaRules() @@ -95,21 +157,6 @@ public void testSchemaRules() accessControl.checkCanRenameSchema(CHARLIE, "authenticated", "authenticated"); assertDenied(() -> accessControl.checkCanRenameSchema(CHARLIE, "test", "new_schema")); - accessControl.checkCanCreateTable(ADMIN, new SchemaTableName("bob", "test")); - accessControl.checkCanCreateTable(ADMIN, new SchemaTableName("test", "test")); - accessControl.checkCanCreateTable(ADMIN, new SchemaTableName("authenticated", "test")); - assertDenied(() -> accessControl.checkCanCreateTable(ADMIN, new SchemaTableName("secret", "test"))); - - accessControl.checkCanCreateTable(BOB, new SchemaTableName("bob", "test")); - assertDenied(() -> accessControl.checkCanCreateTable(BOB, new SchemaTableName("test", "test"))); - accessControl.checkCanCreateTable(BOB, new SchemaTableName("authenticated", "test")); - assertDenied(() -> accessControl.checkCanCreateTable(BOB, new SchemaTableName("secret", "test"))); - - assertDenied(() -> accessControl.checkCanCreateTable(CHARLIE, new SchemaTableName("bob", "test"))); - assertDenied(() -> accessControl.checkCanCreateTable(CHARLIE, new SchemaTableName("test", "test"))); - accessControl.checkCanCreateTable(CHARLIE, new SchemaTableName("authenticated", "test")); - assertDenied(() -> accessControl.checkCanCreateTable(CHARLIE, new SchemaTableName("secret", "test"))); - accessControl.checkCanSetSchemaAuthorization(ADMIN, "test", new PrestoPrincipal(PrincipalType.ROLE, "some_role")); accessControl.checkCanSetSchemaAuthorization(ADMIN, "test", new PrestoPrincipal(PrincipalType.USER, "some_user")); accessControl.checkCanSetSchemaAuthorization(BOB, "bob", new PrestoPrincipal(PrincipalType.ROLE, "some_role")); @@ -140,6 +187,7 @@ public void testTableRules() accessControl.checkCanSelectFromColumns(ALICE, new SchemaTableName("test", "test"), ImmutableSet.of()); accessControl.checkCanSelectFromColumns(ALICE, new SchemaTableName("bobschema", "bobtable"), ImmutableSet.of()); accessControl.checkCanSelectFromColumns(ALICE, new SchemaTableName("bobschema", "bobtable"), ImmutableSet.of("bobcolumn")); + accessControl.checkCanShowColumns(ALICE, new SchemaTableName("bobschema", "bobtable")); assertEquals( accessControl.filterColumns(ALICE, new SchemaTableName("bobschema", "bobtable"), ImmutableList.of(column("a"))), @@ -149,18 +197,32 @@ public void testTableRules() assertEquals( accessControl.filterColumns(BOB, new SchemaTableName("bobschema", "bobtable"), ImmutableList.of(column("a"))), ImmutableList.of(column("a"))); + accessControl.checkCanInsertIntoTable(BOB, new SchemaTableName("bobschema", "bobtable")); accessControl.checkCanDeleteFromTable(BOB, new SchemaTableName("bobschema", "bobtable")); accessControl.checkCanSelectFromColumns(CHARLIE, new SchemaTableName("bobschema", "bobtable"), ImmutableSet.of()); accessControl.checkCanSelectFromColumns(CHARLIE, new SchemaTableName("bobschema", "bobtable"), ImmutableSet.of("bobcolumn")); accessControl.checkCanInsertIntoTable(CHARLIE, new SchemaTableName("bobschema", "bobtable")); accessControl.checkCanSelectFromColumns(JOE, new SchemaTableName("bobschema", "bobtable"), ImmutableSet.of()); + + accessControl.checkCanCreateTable(ADMIN, new SchemaTableName("bob", "test")); + accessControl.checkCanCreateTable(ADMIN, new SchemaTableName("test", "test")); + accessControl.checkCanCreateTable(ADMIN, new SchemaTableName("authenticated", "test")); + assertDenied(() -> accessControl.checkCanCreateTable(ADMIN, new SchemaTableName("secret", "test"))); + + accessControl.checkCanCreateTable(ALICE, new SchemaTableName("aliceschema", "test")); + assertDenied(() -> accessControl.checkCanCreateTable(ALICE, new SchemaTableName("test", "test"))); + assertDenied(() -> accessControl.checkCanCreateTable(CHARLIE, new SchemaTableName("aliceschema", "test"))); + assertDenied(() -> accessControl.checkCanCreateTable(CHARLIE, new SchemaTableName("test", "test"))); + accessControl.checkCanCreateViewWithSelectFromColumns(BOB, new SchemaTableName("bobschema", "bobtable"), ImmutableSet.of()); accessControl.checkCanDropTable(ADMIN, new SchemaTableName("bobschema", "bobtable")); + accessControl.checkCanRenameTable(ADMIN, new SchemaTableName("bobschema", "bobtable"), new SchemaTableName("aliceschema", "newbobtable")); accessControl.checkCanRenameTable(ALICE, new SchemaTableName("aliceschema", "alicetable"), new SchemaTableName("aliceschema", "newalicetable")); accessControl.checkCanRenameView(ADMIN, new SchemaTableName("bobschema", "bobview"), new SchemaTableName("aliceschema", "newbobview")); accessControl.checkCanRenameView(ALICE, new SchemaTableName("aliceschema", "aliceview"), new SchemaTableName("aliceschema", "newaliceview")); + assertDenied(() -> accessControl.checkCanInsertIntoTable(ALICE, new SchemaTableName("bobschema", "bobtable"))); assertDenied(() -> accessControl.checkCanDropTable(BOB, new SchemaTableName("bobschema", "bobtable"))); assertDenied(() -> accessControl.checkCanRenameTable(BOB, new SchemaTableName("bobschema", "bobtable"), new SchemaTableName("bobschema", "newbobtable"))); @@ -173,15 +235,54 @@ public void testTableRules() assertDenied(() -> accessControl.checkCanRenameView(ALICE, new SchemaTableName("aliceschema", "alicetable"), new SchemaTableName("bobschema", "newalicetable"))); } + @Test + public void testTableFilter() + { + ConnectorAccessControl accessControl = createAccessControl("table-filter.json"); + Set tables = ImmutableSet.builder() + .add(new SchemaTableName("restricted", "any")) + .add(new SchemaTableName("secret", "any")) + .add(new SchemaTableName("aliceschema", "any")) + .add(new SchemaTableName("aliceschema", "bobtable")) + .add(new SchemaTableName("bobschema", "bob_any")) + .add(new SchemaTableName("bobschema", "any")) + .add(new SchemaTableName("any", "any")) + .build(); + assertEquals(accessControl.filterTables(ALICE, tables), ImmutableSet.builder() + .add(new SchemaTableName("aliceschema", "any")) + .add(new SchemaTableName("aliceschema", "bobtable")) + .build()); + assertEquals(accessControl.filterTables(BOB, tables), ImmutableSet.builder() + .add(new SchemaTableName("aliceschema", "bobtable")) + .add(new SchemaTableName("bobschema", "bob_any")) + .build()); + assertEquals(accessControl.filterTables(ADMIN, tables), ImmutableSet.builder() + .add(new SchemaTableName("secret", "any")) + .add(new SchemaTableName("aliceschema", "any")) + .add(new SchemaTableName("aliceschema", "bobtable")) + .add(new SchemaTableName("bobschema", "bob_any")) + .add(new SchemaTableName("bobschema", "any")) + .add(new SchemaTableName("any", "any")) + .build()); + } + @Test public void testNoTableRules() { ConnectorAccessControl accessControl = createAccessControl("no-access.json"); assertDenied(() -> accessControl.checkCanShowColumns(BOB, new SchemaTableName("bobschema", "bobtable"))); - accessControl.checkCanShowTables(BOB, "bobschema"); + assertDenied(() -> accessControl.checkCanShowTables(BOB, "bobschema")); assertEquals( accessControl.filterColumns(BOB, new SchemaTableName("bobschema", "bobtable"), ImmutableList.of(column("a"))), ImmutableList.of()); + + Set tables = ImmutableSet.builder() + .add(new SchemaTableName("restricted", "any")) + .add(new SchemaTableName("secret", "any")) + .add(new SchemaTableName("any", "any")) + .build(); + assertEquals(accessControl.filterTables(ALICE, tables), ImmutableSet.of()); + assertEquals(accessControl.filterTables(BOB, tables), ImmutableSet.of()); } @Test @@ -208,6 +309,44 @@ public void testInvalidRules() .hasMessageContaining("Invalid JSON"); } + @Test + public void testFilterSchemas() + { + ConnectorAccessControl accessControl = createAccessControl("visibility.json"); + + ImmutableSet allSchemas = ImmutableSet.of("specific-schema", "alice-schema", "bob-schema", "unknown"); + assertEquals(accessControl.filterSchemas(ADMIN, allSchemas), allSchemas); + assertEquals(accessControl.filterSchemas(ALICE, allSchemas), ImmutableSet.of("specific-schema", "alice-schema")); + assertEquals(accessControl.filterSchemas(BOB, allSchemas), ImmutableSet.of("specific-schema", "bob-schema")); + assertEquals(accessControl.filterSchemas(CHARLIE, allSchemas), ImmutableSet.of("specific-schema")); + } + + @Test + public void testSchemaRulesForCheckCanShowTables() + { + ConnectorAccessControl accessControl = createAccessControl("visibility.json"); + accessControl.checkCanShowTables(ADMIN, "specific-schema"); + accessControl.checkCanShowTables(ADMIN, "bob-schema"); + accessControl.checkCanShowTables(ADMIN, "alice-schema"); + accessControl.checkCanShowTables(ADMIN, "secret"); + accessControl.checkCanShowTables(ADMIN, "any"); + accessControl.checkCanShowTables(ALICE, "specific-schema"); + accessControl.checkCanShowTables(ALICE, "alice-schema"); + assertDenied(() -> accessControl.checkCanShowTables(ALICE, "bob-schema")); + assertDenied(() -> accessControl.checkCanShowTables(ALICE, "secret")); + assertDenied(() -> accessControl.checkCanShowTables(ALICE, "any")); + accessControl.checkCanShowTables(BOB, "specific-schema"); + accessControl.checkCanShowTables(BOB, "bob-schema"); + assertDenied(() -> accessControl.checkCanShowTables(BOB, "alice-schema")); + assertDenied(() -> accessControl.checkCanShowTables(BOB, "secret")); + assertDenied(() -> accessControl.checkCanShowTables(BOB, "any")); + accessControl.checkCanShowTables(CHARLIE, "specific-schema"); + assertDenied(() -> accessControl.checkCanShowTables(CHARLIE, "bob-schema")); + assertDenied(() -> accessControl.checkCanShowTables(CHARLIE, "alice-schema")); + assertDenied(() -> accessControl.checkCanShowTables(CHARLIE, "secret")); + assertDenied(() -> accessControl.checkCanShowTables(CHARLIE, "any")); + } + @Test public void testEverythingImplemented() { diff --git a/presto-plugin-toolkit/src/test/java/io/prestosql/plugin/base/security/TestFileBasedSystemAccessControl.java b/presto-plugin-toolkit/src/test/java/io/prestosql/plugin/base/security/TestFileBasedSystemAccessControl.java index 41ec83d6a853..49fbcde20b54 100644 --- a/presto-plugin-toolkit/src/test/java/io/prestosql/plugin/base/security/TestFileBasedSystemAccessControl.java +++ b/presto-plugin-toolkit/src/test/java/io/prestosql/plugin/base/security/TestFileBasedSystemAccessControl.java @@ -16,10 +16,12 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Sets; import io.prestosql.spi.QueryId; import io.prestosql.spi.connector.CatalogSchemaName; import io.prestosql.spi.connector.CatalogSchemaTableName; import io.prestosql.spi.connector.ColumnMetadata; +import io.prestosql.spi.connector.SchemaTableName; import io.prestosql.spi.security.AccessDeniedException; import io.prestosql.spi.security.Identity; import io.prestosql.spi.security.PrestoPrincipal; @@ -61,7 +63,6 @@ public class TestFileBasedSystemAccessControl private static final Identity bob = Identity.forUser("bob").withGroups(ImmutableSet.of("staff")).build(); private static final Identity admin = Identity.forUser("admin").withGroups(ImmutableSet.of("admin", "staff")).build(); private static final Identity nonAsciiUser = Identity.ofUser("\u0194\u0194\u0194"); - private static final Set allCatalogs = ImmutableSet.of("secret", "open-to-all", "all-allowed", "alice-catalog", "allowed-absent", "\u0200\u0200\u0200"); private static final CatalogSchemaTableName aliceView = new CatalogSchemaTableName("alice-catalog", "schema", "view"); private static final Optional queryId = Optional.empty(); @@ -72,12 +73,16 @@ public class TestFileBasedSystemAccessControl private static final SystemSecurityContext CHARLIE = new SystemSecurityContext(charlie, queryId); private static final SystemSecurityContext ALICE = new SystemSecurityContext(alice, queryId); private static final SystemSecurityContext JOE = new SystemSecurityContext(joe, queryId); + private static final SystemSecurityContext UNKNOWN = new SystemSecurityContext(Identity.ofUser("some-unknown-user-id"), queryId); + private static final String SHOWN_SCHEMAS_ACCESS_DENIED_MESSAGE = "Access Denied: Cannot show schemas"; + private static final String CREATE_SCHEMA_ACCESS_DENIED_MESSAGE = "Access Denied: Cannot create schema .*"; private static final String DROP_SCHEMA_ACCESS_DENIED_MESSAGE = "Access Denied: Cannot drop schema .*"; private static final String RENAME_SCHEMA_ACCESS_DENIED_MESSAGE = "Access Denied: Cannot rename schema from .* to .*"; private static final String AUTH_SCHEMA_ACCESS_DENIED_MESSAGE = "Access Denied: Cannot set authorization for schema .* to .*"; private static final String SHOW_CREATE_SCHEMA_ACCESS_DENIED_MESSAGE = "Access Denied: Cannot show create schema for .*"; + private static final String SHOWN_TABLES_ACCESS_DENIED_MESSAGE = "Access Denied: Cannot show tables of .*"; private static final String SELECT_TABLE_ACCESS_DENIED_MESSAGE = "Access Denied: Cannot select from table .*"; private static final String SHOW_COLUMNS_ACCESS_DENIED_MESSAGE = "Access Denied: Cannot show columns of table .*"; private static final String ADD_COLUMNS_ACCESS_DENIED_MESSAGE = "Access Denied: Cannot add a column to table .*"; @@ -92,6 +97,69 @@ public class TestFileBasedSystemAccessControl private static final String GRANT_DELETE_PRIVILEGE_ACCESS_DENIED_MESSAGE = "Access Denied: Cannot grant privilege DELETE on table .*"; private static final String REVOKE_DELETE_PRIVILEGE_ACCESS_DENIED_MESSAGE = "Access Denied: Cannot revoke privilege DELETE on table .*"; + private static final String SET_SYSTEM_SESSION_PROPERTY_ACCESS_DENIED_MESSAGE = "Access Denied: Cannot set system session property .*"; + private static final String SET_CATALOG_SESSION_PROPERTY_ACCESS_DENIED_MESSAGE = "Access Denied: Cannot set catalog session property .*"; + + @Test + public void testEmptyFile() + { + SystemAccessControl accessControl = newFileBasedSystemAccessControl("empty.json"); + + accessControl.checkCanCreateSchema(UNKNOWN, new CatalogSchemaName("some-catalog", "unknown")); + accessControl.checkCanDropSchema(UNKNOWN, new CatalogSchemaName("some-catalog", "unknown")); + accessControl.checkCanRenameSchema(UNKNOWN, new CatalogSchemaName("some-catalog", "unknown"), "new_unknown"); + accessControl.checkCanSetSchemaAuthorization(UNKNOWN, + new CatalogSchemaName("some-catalog", "unknown"), + new PrestoPrincipal(PrincipalType.ROLE, "some_role")); + accessControl.checkCanShowCreateSchema(UNKNOWN, new CatalogSchemaName("some-catalog", "unknown")); + + accessControl.checkCanSelectFromColumns(UNKNOWN, new CatalogSchemaTableName("some-catalog", "unknown", "unknown"), ImmutableSet.of()); + accessControl.checkCanShowColumns(UNKNOWN, new CatalogSchemaTableName("some-catalog", "unknown", "unknown")); + accessControl.checkCanInsertIntoTable(UNKNOWN, new CatalogSchemaTableName("some-catalog", "unknown", "unknown")); + accessControl.checkCanDeleteFromTable(UNKNOWN, new CatalogSchemaTableName("some-catalog", "unknown", "unknown")); + + accessControl.checkCanCreateTable(UNKNOWN, new CatalogSchemaTableName("some-catalog", "unknown", "unknown")); + accessControl.checkCanDropTable(UNKNOWN, new CatalogSchemaTableName("some-catalog", "unknown", "unknown")); + accessControl.checkCanRenameTable(UNKNOWN, + new CatalogSchemaTableName("some-catalog", "unknown", "unknown"), + new CatalogSchemaTableName("some-catalog", "unknown", "new_unknown")); + + accessControl.checkCanSetUser(Optional.empty(), "unknown"); + accessControl.checkCanSetUser(Optional.of(new KerberosPrincipal("stuff@example.com")), "unknown"); + + accessControl.checkCanSetSystemSessionProperty(UNKNOWN, "anything"); + accessControl.checkCanSetCatalogSessionProperty(UNKNOWN, "unknown", "anything"); + + accessControl.checkCanExecuteQuery(UNKNOWN); + accessControl.checkCanViewQueryOwnedBy(UNKNOWN, "anyone"); + accessControl.checkCanKillQueryOwnedBy(UNKNOWN, "anyone"); + + // system information access is denied by default + assertThrows(AccessDeniedException.class, () -> accessControl.checkCanReadSystemInformation(UNKNOWN)); + assertThrows(AccessDeniedException.class, () -> accessControl.checkCanWriteSystemInformation(UNKNOWN)); + } + + @Test + public void testSchemaRulesForCheckCanCreateSchema() + { + SystemAccessControl accessControl = newFileBasedSystemAccessControl("file-based-system-access-schema.json"); + + accessControl.checkCanCreateSchema(ADMIN, new CatalogSchemaName("some-catalog", "bob")); + accessControl.checkCanCreateSchema(ADMIN, new CatalogSchemaName("some-catalog", "staff")); + accessControl.checkCanCreateSchema(ADMIN, new CatalogSchemaName("some-catalog", "authenticated")); + accessControl.checkCanCreateSchema(ADMIN, new CatalogSchemaName("some-catalog", "test")); + + accessControl.checkCanCreateSchema(BOB, new CatalogSchemaName("some-catalog", "bob")); + accessControl.checkCanCreateSchema(BOB, new CatalogSchemaName("some-catalog", "staff")); + accessControl.checkCanCreateSchema(BOB, new CatalogSchemaName("some-catalog", "authenticated")); + assertAccessDenied(() -> accessControl.checkCanCreateSchema(BOB, new CatalogSchemaName("some-catalog", "test")), CREATE_SCHEMA_ACCESS_DENIED_MESSAGE); + + accessControl.checkCanCreateSchema(CHARLIE, new CatalogSchemaName("some-catalog", "authenticated")); + assertAccessDenied(() -> accessControl.checkCanCreateSchema(CHARLIE, new CatalogSchemaName("some-catalog", "bob")), CREATE_SCHEMA_ACCESS_DENIED_MESSAGE); + assertAccessDenied(() -> accessControl.checkCanCreateSchema(CHARLIE, new CatalogSchemaName("some-catalog", "staff")), CREATE_SCHEMA_ACCESS_DENIED_MESSAGE); + assertAccessDenied(() -> accessControl.checkCanCreateSchema(CHARLIE, new CatalogSchemaName("some-catalog", "test")), CREATE_SCHEMA_ACCESS_DENIED_MESSAGE); + } + @Test public void testSchemaRulesForCheckCanDropSchema() { @@ -180,8 +248,8 @@ public void testTableRulesForCheckCanSelectFromColumns() accessControl.checkCanSelectFromColumns(CHARLIE, new CatalogSchemaTableName("some-catalog", "bobschema", "bobtable"), ImmutableSet.of()); accessControl.checkCanSelectFromColumns(CHARLIE, new CatalogSchemaTableName("some-catalog", "bobschema", "bobtable"), ImmutableSet.of("bobcolumn")); accessControl.checkCanSelectFromColumns(JOE, new CatalogSchemaTableName("some-catalog", "bobschema", "bobtable"), ImmutableSet.of()); - assertAccessDenied(() -> accessControl.checkCanSelectFromColumns(ADMIN, new CatalogSchemaTableName("some-catalog", "secret", "secret"), ImmutableSet.of()), SELECT_TABLE_ACCESS_DENIED_MESSAGE); - assertAccessDenied(() -> accessControl.checkCanSelectFromColumns(JOE, new CatalogSchemaTableName("some-catalog", "secret", "secret"), ImmutableSet.of()), SELECT_TABLE_ACCESS_DENIED_MESSAGE); + assertAccessDenied(() -> accessControl.checkCanSelectFromColumns(ADMIN, new CatalogSchemaTableName("secret", "secret", "secret"), ImmutableSet.of()), SELECT_TABLE_ACCESS_DENIED_MESSAGE); + assertAccessDenied(() -> accessControl.checkCanSelectFromColumns(JOE, new CatalogSchemaTableName("secret", "secret", "secret"), ImmutableSet.of()), SELECT_TABLE_ACCESS_DENIED_MESSAGE); } @Test @@ -198,7 +266,7 @@ public void testTableRulesForCheckCanShowColumnsWithNoAccess() { SystemAccessControl accessControl = newFileBasedSystemAccessControl("file-based-system-no-access.json"); assertAccessDenied(() -> accessControl.checkCanShowColumns(BOB, new CatalogSchemaTableName("some-catalog", "bobschema", "bobtable")), SHOW_COLUMNS_ACCESS_DENIED_MESSAGE); - accessControl.checkCanShowTables(BOB, new CatalogSchemaName("some-catalog", "bobschema")); + assertAccessDenied(() -> accessControl.checkCanShowTables(BOB, new CatalogSchemaName("some-catalog", "bobschema")), SHOWN_TABLES_ACCESS_DENIED_MESSAGE); } @Test @@ -214,6 +282,51 @@ public void testTableRulesForFilterColumns() ImmutableList.of(column("a"))); } + @Test + public void testTableFilter() + { + SystemAccessControl accessControl = newFileBasedSystemAccessControl("file-based-system-access-table-filter.json"); + Set tables = ImmutableSet.builder() + .add(new SchemaTableName("restricted", "any")) + .add(new SchemaTableName("secret", "any")) + .add(new SchemaTableName("aliceschema", "any")) + .add(new SchemaTableName("aliceschema", "bobtable")) + .add(new SchemaTableName("bobschema", "bob_any")) + .add(new SchemaTableName("bobschema", "any")) + .add(new SchemaTableName("any", "any")) + .build(); + assertEquals(accessControl.filterTables(ALICE, "any", tables), ImmutableSet.builder() + .add(new SchemaTableName("aliceschema", "any")) + .add(new SchemaTableName("aliceschema", "bobtable")) + .build()); + assertEquals(accessControl.filterTables(BOB, "any", tables), ImmutableSet.builder() + .add(new SchemaTableName("aliceschema", "bobtable")) + .add(new SchemaTableName("bobschema", "bob_any")) + .build()); + assertEquals(accessControl.filterTables(ADMIN, "any", tables), ImmutableSet.builder() + .add(new SchemaTableName("secret", "any")) + .add(new SchemaTableName("aliceschema", "any")) + .add(new SchemaTableName("aliceschema", "bobtable")) + .add(new SchemaTableName("bobschema", "bob_any")) + .add(new SchemaTableName("bobschema", "any")) + .add(new SchemaTableName("any", "any")) + .build()); + } + + @Test + public void testTableFilterNoAccess() + { + SystemAccessControl accessControl = newFileBasedSystemAccessControl("file-based-system-no-access.json"); + + Set tables = ImmutableSet.builder() + .add(new SchemaTableName("restricted", "any")) + .add(new SchemaTableName("secret", "any")) + .add(new SchemaTableName("any", "any")) + .build(); + assertEquals(accessControl.filterTables(ALICE, "any", tables), ImmutableSet.of()); + assertEquals(accessControl.filterTables(BOB, "any", tables), ImmutableSet.of()); + } + @Test public void testTableRulesForFilterColumnsWithNoAccess() { @@ -502,17 +615,235 @@ public void testSchemaOperations() } @Test - public void testCatalogOperations() - { - SystemAccessControl accessControl = newFileBasedSystemAccessControl("catalog.json"); - - assertEquals(accessControl.filterCatalogs(new SystemSecurityContext(admin, queryId), allCatalogs), allCatalogs); - Set aliceCatalogs = ImmutableSet.of("open-to-all", "alice-catalog", "all-allowed"); - assertEquals(accessControl.filterCatalogs(new SystemSecurityContext(alice, queryId), allCatalogs), aliceCatalogs); - Set bobCatalogs = ImmutableSet.of("open-to-all", "all-allowed"); - assertEquals(accessControl.filterCatalogs(new SystemSecurityContext(bob, queryId), allCatalogs), bobCatalogs); - Set nonAsciiUserCatalogs = ImmutableSet.of("open-to-all", "all-allowed", "\u0200\u0200\u0200"); - assertEquals(accessControl.filterCatalogs(new SystemSecurityContext(nonAsciiUser, queryId), allCatalogs), nonAsciiUserCatalogs); + public void testSessionPropertyRules() + { + SystemAccessControl accessControl = newFileBasedSystemAccessControl("file-based-system-access-session-property.json"); + + accessControl.checkCanSetSystemSessionProperty(ADMIN, "dangerous"); + accessControl.checkCanSetSystemSessionProperty(ADMIN, "any"); + accessControl.checkCanSetSystemSessionProperty(ALICE, "safe"); + accessControl.checkCanSetSystemSessionProperty(ALICE, "unsafe"); + accessControl.checkCanSetSystemSessionProperty(ALICE, "staff"); + accessControl.checkCanSetSystemSessionProperty(BOB, "safe"); + accessControl.checkCanSetSystemSessionProperty(BOB, "staff"); + assertAccessDenied(() -> accessControl.checkCanSetSystemSessionProperty(BOB, "unsafe"), SET_SYSTEM_SESSION_PROPERTY_ACCESS_DENIED_MESSAGE); + assertAccessDenied(() -> accessControl.checkCanSetSystemSessionProperty(ALICE, "dangerous"), SET_SYSTEM_SESSION_PROPERTY_ACCESS_DENIED_MESSAGE); + assertAccessDenied(() -> accessControl.checkCanSetSystemSessionProperty(CHARLIE, "safe"), SET_SYSTEM_SESSION_PROPERTY_ACCESS_DENIED_MESSAGE); + assertAccessDenied(() -> accessControl.checkCanSetSystemSessionProperty(CHARLIE, "staff"), SET_SYSTEM_SESSION_PROPERTY_ACCESS_DENIED_MESSAGE); + assertAccessDenied(() -> accessControl.checkCanSetSystemSessionProperty(JOE, "staff"), SET_SYSTEM_SESSION_PROPERTY_ACCESS_DENIED_MESSAGE); + + accessControl.checkCanSetCatalogSessionProperty(ADMIN, "any", "dangerous"); + accessControl.checkCanSetCatalogSessionProperty(ADMIN, "alice-catalog", "dangerous"); + accessControl.checkCanSetCatalogSessionProperty(ADMIN, "any", "any"); + accessControl.checkCanSetCatalogSessionProperty(ALICE, "alice-catalog", "safe"); + accessControl.checkCanSetCatalogSessionProperty(ALICE, "alice-catalog", "unsafe"); + accessControl.checkCanSetCatalogSessionProperty(ALICE, "staff-catalog", "staff"); + accessControl.checkCanSetCatalogSessionProperty(BOB, "bob-catalog", "safe"); + accessControl.checkCanSetCatalogSessionProperty(BOB, "staff-catalog", "staff"); + assertAccessDenied(() -> accessControl.checkCanSetCatalogSessionProperty(BOB, "bob-catalog", "any"), SET_CATALOG_SESSION_PROPERTY_ACCESS_DENIED_MESSAGE); + assertAccessDenied(() -> accessControl.checkCanSetCatalogSessionProperty(BOB, "alice-catalog", "any"), SET_CATALOG_SESSION_PROPERTY_ACCESS_DENIED_MESSAGE); + assertAccessDenied(() -> accessControl.checkCanSetCatalogSessionProperty(BOB, "staff-catalog", "any"), SET_CATALOG_SESSION_PROPERTY_ACCESS_DENIED_MESSAGE); + assertAccessDenied(() -> accessControl.checkCanSetCatalogSessionProperty(ALICE, "alice-catalog", "dangerous"), SET_CATALOG_SESSION_PROPERTY_ACCESS_DENIED_MESSAGE); + assertAccessDenied(() -> accessControl.checkCanSetCatalogSessionProperty(CHARLIE, "bob-catalog", "safe"), SET_CATALOG_SESSION_PROPERTY_ACCESS_DENIED_MESSAGE); + assertAccessDenied(() -> accessControl.checkCanSetCatalogSessionProperty(CHARLIE, "staff-catalog", "staff"), SET_CATALOG_SESSION_PROPERTY_ACCESS_DENIED_MESSAGE); + assertAccessDenied(() -> accessControl.checkCanSetCatalogSessionProperty(JOE, "staff-catalog", "staff"), SET_CATALOG_SESSION_PROPERTY_ACCESS_DENIED_MESSAGE); + } + + @Test + public void testSessionPropertyDocsExample() + { + String rulesFile = new File("../presto-docs/src/main/sphinx/security/session-property-access.json").getAbsolutePath(); + SystemAccessControl accessControl = newFileBasedSystemAccessControl(ImmutableMap.of("security.config-file", rulesFile)); + SystemSecurityContext bannedUser = new SystemSecurityContext(Identity.ofUser("banned_user"), queryId); + + accessControl.checkCanSetSystemSessionProperty(ADMIN, "any"); + assertAccessDenied(() -> accessControl.checkCanSetSystemSessionProperty(ALICE, "any"), SET_SYSTEM_SESSION_PROPERTY_ACCESS_DENIED_MESSAGE); + assertAccessDenied(() -> accessControl.checkCanSetSystemSessionProperty(bannedUser, "any"), SET_SYSTEM_SESSION_PROPERTY_ACCESS_DENIED_MESSAGE); + + accessControl.checkCanSetSystemSessionProperty(ADMIN, "resource_overcommit"); + accessControl.checkCanSetSystemSessionProperty(ALICE, "resource_overcommit"); + assertAccessDenied(() -> accessControl.checkCanSetSystemSessionProperty(bannedUser, "resource_overcommit"), SET_SYSTEM_SESSION_PROPERTY_ACCESS_DENIED_MESSAGE); + + accessControl.checkCanSetCatalogSessionProperty(ADMIN, "hive", "any"); + assertAccessDenied(() -> accessControl.checkCanSetCatalogSessionProperty(ALICE, "hive", "any"), SET_CATALOG_SESSION_PROPERTY_ACCESS_DENIED_MESSAGE); + assertAccessDenied(() -> accessControl.checkCanSetCatalogSessionProperty(bannedUser, "hive", "any"), SET_CATALOG_SESSION_PROPERTY_ACCESS_DENIED_MESSAGE); + + accessControl.checkCanSetCatalogSessionProperty(ADMIN, "hive", "bucket_execution_enabled"); + accessControl.checkCanSetCatalogSessionProperty(ALICE, "hive", "bucket_execution_enabled"); + assertAccessDenied(() -> accessControl.checkCanSetCatalogSessionProperty(bannedUser, "hive", "bucket_execution_enabled"), SET_CATALOG_SESSION_PROPERTY_ACCESS_DENIED_MESSAGE); + } + + @Test + public void testFilterCatalogs() + { + SystemAccessControl accessControl = newFileBasedSystemAccessControl("file-based-system-access-visibility.json"); + Set allCatalogs = ImmutableSet.of( + "alice-catalog", + "bob-catalog", + "specific-catalog", + "secret", + "hidden", + "open-to-all", + "blocked-catalog", + "unknown"); + + assertEquals(accessControl.filterCatalogs(ADMIN, allCatalogs), Sets.difference(allCatalogs, ImmutableSet.of("blocked-catalog"))); + Set aliceCatalogs = ImmutableSet.of("specific-catalog", "alice-catalog"); + assertEquals(accessControl.filterCatalogs(ALICE, allCatalogs), aliceCatalogs); + Set bobCatalogs = ImmutableSet.of("specific-catalog", "alice-catalog", "bob-catalog"); + assertEquals(accessControl.filterCatalogs(BOB, allCatalogs), bobCatalogs); + Set charlieCatalogs = ImmutableSet.of("specific-catalog"); + assertEquals(accessControl.filterCatalogs(CHARLIE, allCatalogs), charlieCatalogs); + } + + @Test + public void testSchemaRulesForCheckCanShowSchemas() + { + SystemAccessControl accessControl = newFileBasedSystemAccessControl("file-based-system-access-visibility.json"); + + accessControl.checkCanShowSchemas(ADMIN, "specific-catalog"); + accessControl.checkCanShowSchemas(ADMIN, "session-catalog"); + accessControl.checkCanShowSchemas(ADMIN, "secret"); + accessControl.checkCanShowSchemas(ADMIN, "hidden"); + accessControl.checkCanShowSchemas(ADMIN, "open-to-all"); + assertAccessDenied(() -> accessControl.checkCanShowSchemas(ADMIN, "blocked-catalog"), SHOWN_SCHEMAS_ACCESS_DENIED_MESSAGE); + accessControl.checkCanShowSchemas(ADMIN, "unknown"); + + accessControl.checkCanShowSchemas(ALICE, "specific-catalog"); + accessControl.checkCanShowSchemas(ALICE, "session-catalog"); + accessControl.checkCanShowSchemas(ALICE, "alice-catalog"); + accessControl.checkCanShowSchemas(ALICE, "alice-catalog-session"); + assertAccessDenied(() -> accessControl.checkCanShowSchemas(ALICE, "bob-catalog"), SHOWN_SCHEMAS_ACCESS_DENIED_MESSAGE); + assertAccessDenied(() -> accessControl.checkCanShowSchemas(ALICE, "bob-catalog-session"), SHOWN_SCHEMAS_ACCESS_DENIED_MESSAGE); + assertAccessDenied(() -> accessControl.checkCanShowSchemas(ALICE, "secret"), SHOWN_SCHEMAS_ACCESS_DENIED_MESSAGE); + assertAccessDenied(() -> accessControl.checkCanShowSchemas(ALICE, "hidden"), SHOWN_SCHEMAS_ACCESS_DENIED_MESSAGE); + assertAccessDenied(() -> accessControl.checkCanShowSchemas(ALICE, "open-to-all"), SHOWN_SCHEMAS_ACCESS_DENIED_MESSAGE); + assertAccessDenied(() -> accessControl.checkCanShowSchemas(ALICE, "blocked-catalog"), SHOWN_SCHEMAS_ACCESS_DENIED_MESSAGE); + assertAccessDenied(() -> accessControl.checkCanShowSchemas(ALICE, "unknown"), SHOWN_SCHEMAS_ACCESS_DENIED_MESSAGE); + + accessControl.checkCanShowSchemas(BOB, "specific-catalog"); + accessControl.checkCanShowSchemas(BOB, "session-catalog"); + accessControl.checkCanShowSchemas(BOB, "bob-catalog"); + accessControl.checkCanShowSchemas(BOB, "bob-catalog-session"); + accessControl.checkCanShowSchemas(BOB, "alice-catalog"); + assertAccessDenied(() -> accessControl.checkCanShowSchemas(BOB, "alice-catalog-session"), SHOWN_SCHEMAS_ACCESS_DENIED_MESSAGE); + assertAccessDenied(() -> accessControl.checkCanShowSchemas(BOB, "secret"), SHOWN_SCHEMAS_ACCESS_DENIED_MESSAGE); + assertAccessDenied(() -> accessControl.checkCanShowSchemas(BOB, "hidden"), SHOWN_SCHEMAS_ACCESS_DENIED_MESSAGE); + assertAccessDenied(() -> accessControl.checkCanShowSchemas(BOB, "open-to-all"), SHOWN_SCHEMAS_ACCESS_DENIED_MESSAGE); + assertAccessDenied(() -> accessControl.checkCanShowSchemas(BOB, "blocked-catalog"), SHOWN_SCHEMAS_ACCESS_DENIED_MESSAGE); + assertAccessDenied(() -> accessControl.checkCanShowSchemas(BOB, "unknown"), SHOWN_SCHEMAS_ACCESS_DENIED_MESSAGE); + + accessControl.checkCanShowSchemas(CHARLIE, "session-catalog"); + accessControl.checkCanShowSchemas(CHARLIE, "specific-catalog"); + assertAccessDenied(() -> accessControl.checkCanShowSchemas(CHARLIE, "alice-catalog-session"), SHOWN_SCHEMAS_ACCESS_DENIED_MESSAGE); + assertAccessDenied(() -> accessControl.checkCanShowSchemas(CHARLIE, "alice-catalog"), SHOWN_SCHEMAS_ACCESS_DENIED_MESSAGE); + assertAccessDenied(() -> accessControl.checkCanShowSchemas(CHARLIE, "bob-catalog-session"), SHOWN_SCHEMAS_ACCESS_DENIED_MESSAGE); + assertAccessDenied(() -> accessControl.checkCanShowSchemas(CHARLIE, "bob-catalog"), SHOWN_SCHEMAS_ACCESS_DENIED_MESSAGE); + assertAccessDenied(() -> accessControl.checkCanShowSchemas(CHARLIE, "secret"), SHOWN_SCHEMAS_ACCESS_DENIED_MESSAGE); + assertAccessDenied(() -> accessControl.checkCanShowSchemas(CHARLIE, "hidden"), SHOWN_SCHEMAS_ACCESS_DENIED_MESSAGE); + assertAccessDenied(() -> accessControl.checkCanShowSchemas(CHARLIE, "open-to-all"), SHOWN_SCHEMAS_ACCESS_DENIED_MESSAGE); + assertAccessDenied(() -> accessControl.checkCanShowSchemas(CHARLIE, "blocked-catalog"), SHOWN_SCHEMAS_ACCESS_DENIED_MESSAGE); + assertAccessDenied(() -> accessControl.checkCanShowSchemas(CHARLIE, "unknown"), SHOWN_SCHEMAS_ACCESS_DENIED_MESSAGE); + } + + @Test + public void testFilterSchemas() + { + SystemAccessControl accessControl = newFileBasedSystemAccessControl("file-based-system-access-visibility.json"); + + assertEquals(accessControl.filterSchemas(ADMIN, "specific-catalog", ImmutableSet.of("specific-schema", "unknown")), ImmutableSet.of("specific-schema", "unknown")); + assertEquals(accessControl.filterSchemas(ALICE, "specific-catalog", ImmutableSet.of("specific-schema", "unknown")), ImmutableSet.of("specific-schema")); + assertEquals(accessControl.filterSchemas(BOB, "specific-catalog", ImmutableSet.of("specific-schema", "unknown")), ImmutableSet.of("specific-schema")); + assertEquals(accessControl.filterSchemas(CHARLIE, "specific-catalog", ImmutableSet.of("specific-schema", "unknown")), ImmutableSet.of("specific-schema")); + + assertEquals(accessControl.filterSchemas(ADMIN, "alice-catalog", ImmutableSet.of("alice-schema", "bob-schema", "unknown")), ImmutableSet.of("alice-schema", "bob-schema", "unknown")); + assertEquals(accessControl.filterSchemas(ALICE, "alice-catalog", ImmutableSet.of("alice-schema", "bob-schema", "unknown")), ImmutableSet.of("alice-schema")); + assertEquals(accessControl.filterSchemas(BOB, "alice-catalog", ImmutableSet.of("alice-schema", "bob-schema", "unknown")), ImmutableSet.of("bob-schema")); + assertEquals(accessControl.filterSchemas(CHARLIE, "alice-catalog", ImmutableSet.of("alice-schema", "bob-schema", "unknown")), ImmutableSet.of()); + + assertEquals(accessControl.filterSchemas(ADMIN, "bob-catalog", ImmutableSet.of("bob-schema", "unknown")), ImmutableSet.of("bob-schema", "unknown")); + assertEquals(accessControl.filterSchemas(ALICE, "bob-catalog", ImmutableSet.of("bob-schema", "unknown")), ImmutableSet.of()); + assertEquals(accessControl.filterSchemas(BOB, "bob-catalog", ImmutableSet.of("bob-schema", "unknown")), ImmutableSet.of("bob-schema")); + assertEquals(accessControl.filterSchemas(CHARLIE, "bob-catalog", ImmutableSet.of("bob-schema", "unknown")), ImmutableSet.of()); + + assertEquals(accessControl.filterSchemas(ADMIN, "secret", ImmutableSet.of("unknown")), ImmutableSet.of("unknown")); + assertEquals(accessControl.filterSchemas(ALICE, "secret", ImmutableSet.of("unknown")), ImmutableSet.of()); + assertEquals(accessControl.filterSchemas(BOB, "secret", ImmutableSet.of("unknown")), ImmutableSet.of()); + assertEquals(accessControl.filterSchemas(CHARLIE, "secret", ImmutableSet.of("unknown")), ImmutableSet.of()); + + assertEquals(accessControl.filterSchemas(ADMIN, "hidden", ImmutableSet.of("unknown")), ImmutableSet.of("unknown")); + assertEquals(accessControl.filterSchemas(ALICE, "hidden", ImmutableSet.of("unknown")), ImmutableSet.of()); + assertEquals(accessControl.filterSchemas(BOB, "hidden", ImmutableSet.of("unknown")), ImmutableSet.of()); + assertEquals(accessControl.filterSchemas(CHARLIE, "hidden", ImmutableSet.of("unknown")), ImmutableSet.of()); + + assertEquals(accessControl.filterSchemas(ADMIN, "open-to-all", ImmutableSet.of("unknown")), ImmutableSet.of("unknown")); + assertEquals(accessControl.filterSchemas(ALICE, "open-to-all", ImmutableSet.of("unknown")), ImmutableSet.of()); + assertEquals(accessControl.filterSchemas(BOB, "open-to-all", ImmutableSet.of("unknown")), ImmutableSet.of()); + assertEquals(accessControl.filterSchemas(CHARLIE, "open-to-all", ImmutableSet.of("unknown")), ImmutableSet.of()); + + assertEquals(accessControl.filterSchemas(ADMIN, "blocked-catalog", ImmutableSet.of("unknown")), ImmutableSet.of()); + assertEquals(accessControl.filterSchemas(ALICE, "blocked-catalog", ImmutableSet.of("unknown")), ImmutableSet.of()); + assertEquals(accessControl.filterSchemas(BOB, "blocked-catalog", ImmutableSet.of("unknown")), ImmutableSet.of()); + assertEquals(accessControl.filterSchemas(CHARLIE, "blocked-catalog", ImmutableSet.of("unknown")), ImmutableSet.of()); + + assertEquals(accessControl.filterSchemas(ADMIN, "unknown", ImmutableSet.of("unknown")), ImmutableSet.of("unknown")); + assertEquals(accessControl.filterSchemas(ALICE, "unknown", ImmutableSet.of("unknown")), ImmutableSet.of()); + assertEquals(accessControl.filterSchemas(BOB, "unknown", ImmutableSet.of("unknown")), ImmutableSet.of()); + assertEquals(accessControl.filterSchemas(CHARLIE, "unknown", ImmutableSet.of("unknown")), ImmutableSet.of()); + + assertEquals(accessControl.filterSchemas(ADMIN, "session-catalog", ImmutableSet.of("session-schema", "unknown")), ImmutableSet.of("session-schema", "unknown")); + assertEquals(accessControl.filterSchemas(ALICE, "session-catalog", ImmutableSet.of("session-schema", "unknown")), ImmutableSet.of()); + assertEquals(accessControl.filterSchemas(BOB, "session-catalog", ImmutableSet.of("session-schema", "unknown")), ImmutableSet.of()); + assertEquals(accessControl.filterSchemas(CHARLIE, "session-catalog", ImmutableSet.of("session-schema", "unknown")), ImmutableSet.of()); + } + + @Test + public void testSchemaRulesForCheckCanShowTables() + { + SystemAccessControl accessControl = newFileBasedSystemAccessControl("file-based-system-access-visibility.json"); + + accessControl.checkCanShowTables(ADMIN, new CatalogSchemaName("specific-catalog", "specific-schema")); + accessControl.checkCanShowTables(ADMIN, new CatalogSchemaName("bob-catalog", "bob-schema")); + accessControl.checkCanShowTables(ADMIN, new CatalogSchemaName("bob-catalog", "any")); + accessControl.checkCanShowTables(ADMIN, new CatalogSchemaName("alice-catalog", "alice-schema")); + accessControl.checkCanShowTables(ADMIN, new CatalogSchemaName("alice-catalog", "any")); + accessControl.checkCanShowTables(ADMIN, new CatalogSchemaName("secret", "secret")); + accessControl.checkCanShowTables(ADMIN, new CatalogSchemaName("hidden", "any")); + accessControl.checkCanShowTables(ADMIN, new CatalogSchemaName("open-to-all", "any")); + assertAccessDenied(() -> accessControl.checkCanShowTables(ADMIN, new CatalogSchemaName("blocked-catalog", "any")), SHOWN_TABLES_ACCESS_DENIED_MESSAGE); + accessControl.checkCanShowTables(ADMIN, new CatalogSchemaName("unknown", "any")); + + accessControl.checkCanShowTables(ALICE, new CatalogSchemaName("specific-catalog", "specific-schema")); + accessControl.checkCanShowTables(ALICE, new CatalogSchemaName("alice-catalog", "alice-schema")); + assertAccessDenied(() -> accessControl.checkCanShowTables(ALICE, new CatalogSchemaName("bob-catalog", "bob-schema")), SHOWN_TABLES_ACCESS_DENIED_MESSAGE); + assertAccessDenied(() -> accessControl.checkCanShowTables(ALICE, new CatalogSchemaName("secret", "secret")), SHOWN_TABLES_ACCESS_DENIED_MESSAGE); + assertAccessDenied(() -> accessControl.checkCanShowTables(ALICE, new CatalogSchemaName("hidden", "any")), SHOWN_TABLES_ACCESS_DENIED_MESSAGE); + assertAccessDenied(() -> accessControl.checkCanShowTables(ALICE, new CatalogSchemaName("open-to-all", "any")), SHOWN_TABLES_ACCESS_DENIED_MESSAGE); + assertAccessDenied(() -> accessControl.checkCanShowTables(ALICE, new CatalogSchemaName("blocked-catalog", "any")), SHOWN_TABLES_ACCESS_DENIED_MESSAGE); + assertAccessDenied(() -> accessControl.checkCanShowTables(ALICE, new CatalogSchemaName("unknown", "any")), SHOWN_TABLES_ACCESS_DENIED_MESSAGE); + + accessControl.checkCanShowTables(BOB, new CatalogSchemaName("specific-catalog", "specific-schema")); + accessControl.checkCanShowTables(BOB, new CatalogSchemaName("bob-catalog", "bob-schema")); + accessControl.checkCanShowTables(BOB, new CatalogSchemaName("alice-catalog", "bob-schema")); + assertAccessDenied(() -> accessControl.checkCanShowTables(BOB, new CatalogSchemaName("bob-catalog", "any")), SHOWN_TABLES_ACCESS_DENIED_MESSAGE); + assertAccessDenied(() -> accessControl.checkCanShowTables(BOB, new CatalogSchemaName("alice-catalog", "alice-schema")), SHOWN_TABLES_ACCESS_DENIED_MESSAGE); + assertAccessDenied(() -> accessControl.checkCanShowTables(BOB, new CatalogSchemaName("alice-catalog", "any")), SHOWN_TABLES_ACCESS_DENIED_MESSAGE); + assertAccessDenied(() -> accessControl.checkCanShowTables(BOB, new CatalogSchemaName("secret", "secret")), SHOWN_TABLES_ACCESS_DENIED_MESSAGE); + assertAccessDenied(() -> accessControl.checkCanShowTables(BOB, new CatalogSchemaName("hidden", "any")), SHOWN_TABLES_ACCESS_DENIED_MESSAGE); + assertAccessDenied(() -> accessControl.checkCanShowTables(BOB, new CatalogSchemaName("open-to-all", "any")), SHOWN_TABLES_ACCESS_DENIED_MESSAGE); + assertAccessDenied(() -> accessControl.checkCanShowTables(BOB, new CatalogSchemaName("blocked-catalog", "any")), SHOWN_TABLES_ACCESS_DENIED_MESSAGE); + assertAccessDenied(() -> accessControl.checkCanShowTables(BOB, new CatalogSchemaName("unknown", "any")), SHOWN_TABLES_ACCESS_DENIED_MESSAGE); + + accessControl.checkCanShowTables(CHARLIE, new CatalogSchemaName("specific-catalog", "specific-schema")); + assertAccessDenied(() -> accessControl.checkCanShowTables(CHARLIE, new CatalogSchemaName("bob-catalog", "bob-schema")), SHOWN_TABLES_ACCESS_DENIED_MESSAGE); + assertAccessDenied(() -> accessControl.checkCanShowTables(CHARLIE, new CatalogSchemaName("bob-catalog", "any")), SHOWN_TABLES_ACCESS_DENIED_MESSAGE); + assertAccessDenied(() -> accessControl.checkCanShowTables(CHARLIE, new CatalogSchemaName("alice-catalog", "alice-schema")), SHOWN_TABLES_ACCESS_DENIED_MESSAGE); + assertAccessDenied(() -> accessControl.checkCanShowTables(CHARLIE, new CatalogSchemaName("alice-catalog", "any")), SHOWN_TABLES_ACCESS_DENIED_MESSAGE); + assertAccessDenied(() -> accessControl.checkCanShowTables(CHARLIE, new CatalogSchemaName("secret", "secret")), SHOWN_TABLES_ACCESS_DENIED_MESSAGE); + assertAccessDenied(() -> accessControl.checkCanShowTables(CHARLIE, new CatalogSchemaName("hidden", "any")), SHOWN_TABLES_ACCESS_DENIED_MESSAGE); + assertAccessDenied(() -> accessControl.checkCanShowTables(CHARLIE, new CatalogSchemaName("open-to-all", "any")), SHOWN_TABLES_ACCESS_DENIED_MESSAGE); + assertAccessDenied(() -> accessControl.checkCanShowTables(CHARLIE, new CatalogSchemaName("blocked-catalog", "any")), SHOWN_TABLES_ACCESS_DENIED_MESSAGE); + assertAccessDenied(() -> accessControl.checkCanShowTables(CHARLIE, new CatalogSchemaName("unknown", "any")), SHOWN_TABLES_ACCESS_DENIED_MESSAGE); } @Test diff --git a/presto-plugin-toolkit/src/test/resources/catalog_principal.json b/presto-plugin-toolkit/src/test/resources/catalog_principal.json index 6bf739521323..34b46fede63b 100644 --- a/presto-plugin-toolkit/src/test/resources/catalog_principal.json +++ b/presto-plugin-toolkit/src/test/resources/catalog_principal.json @@ -1,9 +1,4 @@ { - "catalogs": [ - { - "allow": true - } - ], "principals": [ { "principal": "(.*)", diff --git a/presto-plugin-toolkit/src/test/resources/empty.json b/presto-plugin-toolkit/src/test/resources/empty.json new file mode 100644 index 000000000000..0967ef424bce --- /dev/null +++ b/presto-plugin-toolkit/src/test/resources/empty.json @@ -0,0 +1 @@ +{} diff --git a/presto-plugin-toolkit/src/test/resources/file-based-system-access-schema.json b/presto-plugin-toolkit/src/test/resources/file-based-system-access-schema.json index c9f3318cc00e..263bd0fe6782 100644 --- a/presto-plugin-toolkit/src/test/resources/file-based-system-access-schema.json +++ b/presto-plugin-toolkit/src/test/resources/file-based-system-access-schema.json @@ -1,31 +1,33 @@ { - "catalogs": [ - { - "allow": true - } - ], "schemas": [ { + "catalog": "unknown", + "owner": false + }, + { + "catalog": "secret", "schema": "secret", "owner": false }, { "user": "admin", - "schema": ".*", "owner": true }, { "group": "staff", + "catalog": "some-catalog", "schema": "staff", "owner": true }, { "group": "staff|guests", + "catalog": "some-catalog", "schema": "authenticated", "owner": true }, { "user": "bob", + "catalog": "some-catalog", "schema": "bob", "owner": true } diff --git a/presto-plugin-toolkit/src/test/resources/file-based-system-access-session-property.json b/presto-plugin-toolkit/src/test/resources/file-based-system-access-session-property.json new file mode 100644 index 000000000000..435eed8aa7b0 --- /dev/null +++ b/presto-plugin-toolkit/src/test/resources/file-based-system-access-session-property.json @@ -0,0 +1,56 @@ +{ + "system_session_properties": [ + { + "user": "admin", + "allow": true + }, + { + "user": "alice", + "property": "dangerous", + "allow": false + }, + { + "user": "alice", + "allow": true + }, + { + "user": "bob", + "property": "safe", + "allow": true + }, + { + "group": "staff", + "property": "staff", + "allow": true + } + ], + "catalog_session_properties": [ + { + "user": "admin", + "allow": true + }, + { + "user": "alice", + "catalog": "alice-catalog", + "property": "dangerous", + "allow": false + }, + { + "user": "alice", + "catalog": "alice-catalog", + "allow": true + }, + { + "user": "bob", + "catalog": "bob-catalog", + "property": "safe", + "allow": true + }, + { + "group": "staff", + "catalog": "staff-catalog", + "property": "staff", + "allow": true + } + ] +} diff --git a/presto-plugin-toolkit/src/test/resources/file-based-system-access-table-filter.json b/presto-plugin-toolkit/src/test/resources/file-based-system-access-table-filter.json new file mode 100644 index 000000000000..c256b71f0e3e --- /dev/null +++ b/presto-plugin-toolkit/src/test/resources/file-based-system-access-table-filter.json @@ -0,0 +1,50 @@ +{ + "catalogs": [ + { + "allow": true + } + ], + "schemas": [ + { + "schema": "restricted", + "owner": false + }, + { + "user": "admin", + "owner": true + }, + { + "user": "alice", + "schema": "aliceschema", + "owner": true + } + ], + "tables": [ + { + "schema": "(restricted|secret)", + "privileges": [] + }, + { + "user": "admin", + "schema": ".*", + "privileges": ["SELECT"] + }, + { + "user": "alice", + "schema": "aliceschema", + "privileges": ["SELECT"] + }, + { + "user": "bob", + "schema": "bobschema", + "table": "bob.*", + "privileges": ["INSERT"] + }, + { + "user": "bob", + "schema": "aliceschema", + "table": "bobtable", + "privileges": ["DELETE"] + } + ] +} diff --git a/presto-plugin-toolkit/src/test/resources/file-based-system-access-table.json b/presto-plugin-toolkit/src/test/resources/file-based-system-access-table.json index 9cf7d7c4c23b..72e4f2ef1ce0 100644 --- a/presto-plugin-toolkit/src/test/resources/file-based-system-access-table.json +++ b/presto-plugin-toolkit/src/test/resources/file-based-system-access-table.json @@ -1,32 +1,34 @@ { - "catalogs": [ - { - "allow": true - } - ], "tables": [ { + "catalog": "unknown", + "privileges": [] + }, + { + "catalog": "secret", "schema": "secret", "privileges": [] }, { "user": "admin", - "schema": ".*", "privileges": ["SELECT", "INSERT", "DELETE", "OWNERSHIP"] }, { "user": "alice", + "catalog": "some-catalog", "schema": "aliceschema", "privileges": ["SELECT", "INSERT", "DELETE", "OWNERSHIP"] }, { "user": "bob", + "catalog": "some-catalog", "schema": "bobschema", "table": "bob.*", "privileges": ["SELECT", "INSERT", "DELETE", "GRANT_SELECT"] }, { "group": "guests", + "catalog": "some-catalog", "schema": "bobschema", "table": "bobtable", "privileges": ["SELECT", "INSERT"] diff --git a/presto-plugin-toolkit/src/test/resources/file-based-system-access-visibility.json b/presto-plugin-toolkit/src/test/resources/file-based-system-access-visibility.json new file mode 100644 index 000000000000..1cacf0350d83 --- /dev/null +++ b/presto-plugin-toolkit/src/test/resources/file-based-system-access-visibility.json @@ -0,0 +1,123 @@ +{ + "catalogs": [ + { + "catalog": "blocked-catalog", + "allow": "none" + }, + { + "user": "admin", + "allow": "all" + }, + { + "catalog": "open-to-all", + "allow": "all" + }, + { + "catalog": "specific-catalog", + "allow": "read-only" + }, + { + "catalog": "hidden", + "allow": "all" + }, + { + "catalog": "secret", + "allow": "all" + }, + { + "user": "alice", + "catalog": "alice-catalog", + "allow": "all" + }, + { + "user": "bob", + "catalog": "(bob-catalog|alice-catalog)", + "allow": "all" + }, + { + "catalog": ".*session.*", + "allow": "read-only" + } + ], + "schemas": [ + { + "catalog": "hidden", + "owner": false + }, + { + "catalog": "secret", + "schema": "secret", + "owner": false + }, + { + "user": "admin", + "owner": true + }, + { + "user": "bob", + "catalog": "alice-catalog", + "schema": "bob-schema", + "owner": true + } + ], + "tables": [ + { + "catalog": "secret", + "schema": "secret", + "privileges": [] + }, + { + "user": "alice", + "catalog": "alice-catalog", + "schema": "alice-schema", + "privileges": [ + "SELECT" + ] + }, + { + "user": "bob", + "catalog": "bob-catalog", + "schema": "bob-schema", + "table": "bob.*", + "privileges": [ + "SELECT" + ] + }, + { + "catalog": "specific-catalog", + "schema": "specific-schema", + "table": "specific-table", + "privileges": [ + "SELECT" + ] + } + ], + "catalog_session_properties": [ + { + "catalog": "secret", + "allow": false + }, + { + "catalog": "session-catalog", + "property": "something", + "allow": true + }, + { + "user": "alice", + "catalog": "alice-catalog-session", + "property": "dangerous", + "allow": false + }, + { + "user": "alice", + "catalog": "alice-catalog-session", + "allow": true + }, + { + "user": "bob", + "catalog": "bob-catalog-session", + "property": "safe", + "allow": true + } + ] +} diff --git a/presto-plugin-toolkit/src/test/resources/file-based-system-no-access.json b/presto-plugin-toolkit/src/test/resources/file-based-system-no-access.json index b063d0613d50..93609b1c0043 100644 --- a/presto-plugin-toolkit/src/test/resources/file-based-system-no-access.json +++ b/presto-plugin-toolkit/src/test/resources/file-based-system-no-access.json @@ -1,4 +1,6 @@ { + "schemas": [ + ], "tables": [ ] } diff --git a/presto-plugin-toolkit/src/test/resources/no-access.json b/presto-plugin-toolkit/src/test/resources/no-access.json index b063d0613d50..93609b1c0043 100644 --- a/presto-plugin-toolkit/src/test/resources/no-access.json +++ b/presto-plugin-toolkit/src/test/resources/no-access.json @@ -1,4 +1,6 @@ { + "schemas": [ + ], "tables": [ ] } diff --git a/presto-plugin-toolkit/src/test/resources/query.json b/presto-plugin-toolkit/src/test/resources/query.json index 97b9a9ccf062..86c22d538428 100644 --- a/presto-plugin-toolkit/src/test/resources/query.json +++ b/presto-plugin-toolkit/src/test/resources/query.json @@ -1,9 +1,4 @@ { - "catalogs": [ - { - "allow": true - } - ], "queries": [ { "user": "admin", diff --git a/presto-plugin-toolkit/src/test/resources/security-config-file-with-unknown-rules.json b/presto-plugin-toolkit/src/test/resources/security-config-file-with-unknown-rules.json index e0c9043a9e6d..2c67aba467b7 100644 --- a/presto-plugin-toolkit/src/test/resources/security-config-file-with-unknown-rules.json +++ b/presto-plugin-toolkit/src/test/resources/security-config-file-with-unknown-rules.json @@ -1,5 +1,5 @@ { - "sessionProperties": [ + "session_properties": [ { "property": "force_local_scheduling", "allow": true diff --git a/presto-plugin-toolkit/src/test/resources/session_property.json b/presto-plugin-toolkit/src/test/resources/session_property.json index 6ac52cbf6d2c..361bd34089a1 100644 --- a/presto-plugin-toolkit/src/test/resources/session_property.json +++ b/presto-plugin-toolkit/src/test/resources/session_property.json @@ -1,5 +1,5 @@ { - "sessionProperties": [ + "session_properties": [ { "user": "admin", "property": ".*", diff --git a/presto-plugin-toolkit/src/test/resources/system-information.json b/presto-plugin-toolkit/src/test/resources/system-information.json index f1b0f7f4c10f..3efb6cc8b232 100644 --- a/presto-plugin-toolkit/src/test/resources/system-information.json +++ b/presto-plugin-toolkit/src/test/resources/system-information.json @@ -1,9 +1,4 @@ { - "catalogs": [ - { - "allow": true - } - ], "system_information": [ { "user": "admin", diff --git a/presto-plugin-toolkit/src/test/resources/table-filter.json b/presto-plugin-toolkit/src/test/resources/table-filter.json new file mode 100644 index 000000000000..c195bdd88ee4 --- /dev/null +++ b/presto-plugin-toolkit/src/test/resources/table-filter.json @@ -0,0 +1,45 @@ +{ + "schemas": [ + { + "schema": "restricted", + "owner": false + }, + { + "user": "admin", + "owner": true + }, + { + "user": "alice", + "schema": "aliceschema", + "owner": true + } + ], + "tables": [ + { + "schema": "(restricted|secret)", + "privileges": [] + }, + { + "user": "admin", + "schema": ".*", + "privileges": ["SELECT", "INSERT", "DELETE", "OWNERSHIP"] + }, + { + "user": "alice", + "schema": "aliceschema", + "privileges": ["SELECT", "INSERT", "DELETE", "OWNERSHIP"] + }, + { + "user": "bob", + "schema": "bobschema", + "table": "bob.*", + "privileges": ["SELECT", "INSERT", "DELETE", "GRANT_SELECT"] + }, + { + "user": "bob", + "schema": "aliceschema", + "table": "bobtable", + "privileges": ["SELECT", "INSERT"] + } + ] +} diff --git a/presto-plugin-toolkit/src/test/resources/table.json b/presto-plugin-toolkit/src/test/resources/table.json index 6ddc08cb4495..d5720a829855 100644 --- a/presto-plugin-toolkit/src/test/resources/table.json +++ b/presto-plugin-toolkit/src/test/resources/table.json @@ -1,4 +1,15 @@ { + "schemas": [ + { + "user": "admin", + "owner": true + }, + { + "user": "alice", + "schema": "aliceschema", + "owner": true + } + ], "tables": [ { "schema": "secret", diff --git a/presto-plugin-toolkit/src/test/resources/visibility.json b/presto-plugin-toolkit/src/test/resources/visibility.json new file mode 100644 index 000000000000..66f428ee219c --- /dev/null +++ b/presto-plugin-toolkit/src/test/resources/visibility.json @@ -0,0 +1,49 @@ +{ + "schemas": [ + { + "schema": "hidden", + "owner": false + }, + { + "schema": "secret", + "owner": false + }, + { + "user": "admin", + "owner": true + }, + { + "user": "bob", + "schema": "bob-schema", + "owner": true + } + ], + "tables": [ + { + "schema": "secret", + "privileges": [] + }, + { + "user": "alice", + "schema": "alice-schema", + "privileges": [ + "SELECT" + ] + }, + { + "user": "bob", + "schema": "bob-schema", + "table": "bob.*", + "privileges": [ + "SELECT" + ] + }, + { + "schema": "specific-schema", + "table": "specific-table", + "privileges": [ + "SELECT" + ] + } + ] +} diff --git a/presto-raptor-legacy/src/test/resources/io/prestosql/plugin/raptor/legacy/security/security.json b/presto-raptor-legacy/src/test/resources/io/prestosql/plugin/raptor/legacy/security/security.json index 6de9c5308be6..dbea06e30017 100644 --- a/presto-raptor-legacy/src/test/resources/io/prestosql/plugin/raptor/legacy/security/security.json +++ b/presto-raptor-legacy/src/test/resources/io/prestosql/plugin/raptor/legacy/security/security.json @@ -3,14 +3,14 @@ { "user": "user", "privileges": [ - "SELECT" + "SELECT", + "OWNERSHIP" ] } ], "schemas": [ { - "user": "user", - "owner": true + "owner": false } ] }