diff --git a/core/trino-grammar/src/main/antlr4/io/trino/grammar/sql/SqlBase.g4 b/core/trino-grammar/src/main/antlr4/io/trino/grammar/sql/SqlBase.g4 index 4f5a474291b84..459abcb211b32 100644 --- a/core/trino-grammar/src/main/antlr4/io/trino/grammar/sql/SqlBase.g4 +++ b/core/trino-grammar/src/main/antlr4/io/trino/grammar/sql/SqlBase.g4 @@ -122,34 +122,34 @@ statement (IN catalog=identifier)? #createRole | DROP ROLE name=identifier (IN catalog=identifier)? #dropRole | GRANT - roles + privilegeOrRole (',' privilegeOrRole)* TO principal (',' principal)* (WITH ADMIN OPTION)? (GRANTED BY grantor)? (IN catalog=identifier)? #grantRoles + | GRANT + ((privilegeOrRole (',' privilegeOrRole)*) | ALL PRIVILEGES) + ON grantObject + TO principal + (WITH GRANT OPTION)? #grantPrivileges | REVOKE (ADMIN OPTION FOR)? - roles + privilegeOrRole (',' privilegeOrRole)* FROM principal (',' principal)* (GRANTED BY grantor)? (IN catalog=identifier)? #revokeRoles - | SET ROLE (ALL | NONE | role=identifier) - (IN catalog=identifier)? #setRole - | GRANT - (privilege (',' privilege)* | ALL PRIVILEGES) - ON (SCHEMA | TABLE)? qualifiedName - TO grantee=principal - (WITH GRANT OPTION)? #grant - | DENY - (privilege (',' privilege)* | ALL PRIVILEGES) - ON (SCHEMA | TABLE)? qualifiedName - TO grantee=principal #deny | REVOKE (GRANT OPTION FOR)? + ((privilegeOrRole (',' privilegeOrRole)*) | ALL PRIVILEGES) + ON grantObject + FROM grantee=principal #revokePrivileges + | DENY (privilege (',' privilege)* | ALL PRIVILEGES) - ON (SCHEMA | TABLE)? qualifiedName - FROM grantee=principal #revoke - | SHOW GRANTS (ON TABLE? qualifiedName)? #showGrants + ON grantObject + TO grantee=principal #deny + | SET ROLE (ALL | NONE | role=identifier) + (IN catalog=identifier)? #setRole + | SHOW GRANTS (ON grantObject)? #showGrants | EXPLAIN ('(' explainOption (',' explainOption)* ')')? statement #explain | EXPLAIN ANALYZE VERBOSE? statement #explainAnalyze | SHOW CREATE TABLE qualifiedName #showCreateTable @@ -918,7 +918,15 @@ sqlStatementList ; privilege - : CREATE | SELECT | DELETE | INSERT | UPDATE + : CREATE | SELECT | DELETE | INSERT | UPDATE | identifier + ; + +entityKind + : TABLE | SCHEMA | identifier + ; + +grantObject + : entityKind? qualifiedName ; qualifiedName @@ -950,6 +958,10 @@ roles : identifier (',' identifier)* ; +privilegeOrRole + : CREATE | SELECT | DELETE | INSERT | UPDATE | identifier + ; + identifier : IDENTIFIER #unquotedIdentifier | QUOTED_IDENTIFIER #quotedIdentifier diff --git a/core/trino-main/src/main/java/io/trino/execution/DenyTask.java b/core/trino-main/src/main/java/io/trino/execution/DenyTask.java index 4a89f0bd80693..30360269feac3 100644 --- a/core/trino-main/src/main/java/io/trino/execution/DenyTask.java +++ b/core/trino-main/src/main/java/io/trino/execution/DenyTask.java @@ -22,18 +22,21 @@ import io.trino.metadata.RedirectionAwareTableHandle; import io.trino.security.AccessControl; import io.trino.spi.connector.CatalogSchemaName; +import io.trino.spi.connector.EntityKindAndName; +import io.trino.spi.connector.EntityPrivilege; import io.trino.spi.security.Privilege; import io.trino.sql.tree.Deny; import io.trino.sql.tree.Expression; -import io.trino.sql.tree.GrantOnType; import java.util.List; import java.util.Optional; import java.util.Set; import static com.google.common.util.concurrent.Futures.immediateVoidFuture; +import static io.trino.execution.PrivilegeUtilities.fetchEntityKindPrivileges; import static io.trino.execution.PrivilegeUtilities.parseStatementPrivileges; import static io.trino.metadata.MetadataUtil.createCatalogSchemaName; +import static io.trino.metadata.MetadataUtil.createEntityKindAndName; import static io.trino.metadata.MetadataUtil.createPrincipal; import static io.trino.metadata.MetadataUtil.createQualifiedObjectName; import static io.trino.spi.StandardErrorCode.NOT_SUPPORTED; @@ -64,18 +67,22 @@ public String getName() @Override public ListenableFuture execute(Deny statement, QueryStateMachine stateMachine, List parameters, WarningCollector warningCollector) { - if (statement.getType().filter(GrantOnType.SCHEMA::equals).isPresent()) { + String entityKind = statement.getGrantObject().getEntityKind().orElse("TABLE"); + if (entityKind.equalsIgnoreCase("TABLE")) { + executeDenyOnTable(stateMachine.getSession(), statement, metadata, accessControl); + } + else if (entityKind.equalsIgnoreCase("SCHEMA")) { executeDenyOnSchema(stateMachine.getSession(), statement, metadata, accessControl); } else { - executeDenyOnTable(stateMachine.getSession(), statement, metadata, accessControl); + executeDenyOnEntity(stateMachine.getSession(), statement, metadata, entityKind, accessControl); } return immediateVoidFuture(); } private static void executeDenyOnSchema(Session session, Deny statement, Metadata metadata, AccessControl accessControl) { - CatalogSchemaName schemaName = createCatalogSchemaName(session, statement, Optional.of(statement.getName())); + CatalogSchemaName schemaName = createCatalogSchemaName(session, statement, Optional.of(statement.getGrantObject().getName())); if (!metadata.schemaExists(session, schemaName)) { throw semanticException(SCHEMA_NOT_FOUND, statement, "Schema '%s' does not exist", schemaName); @@ -91,7 +98,7 @@ private static void executeDenyOnSchema(Session session, Deny statement, Metadat private static void executeDenyOnTable(Session session, Deny statement, Metadata metadata, AccessControl accessControl) { - QualifiedObjectName tableName = createQualifiedObjectName(session, statement, statement.getName()); + QualifiedObjectName tableName = createQualifiedObjectName(session, statement, statement.getGrantObject().getName()); RedirectionAwareTableHandle redirection = metadata.getRedirectionAwareTableHandle(session, tableName); if (redirection.tableHandle().isEmpty()) { throw semanticException(TABLE_NOT_FOUND, statement, "Table '%s' does not exist", tableName); @@ -108,4 +115,16 @@ private static void executeDenyOnTable(Session session, Deny statement, Metadata metadata.denyTablePrivileges(session, tableName, privileges, createPrincipal(statement.getGrantee())); } + + private static void executeDenyOnEntity(Session session, Deny statement, Metadata metadata, String entityKind, AccessControl accessControl) + { + EntityKindAndName entity = createEntityKindAndName(entityKind, statement.getGrantObject().getName()); + Set privileges = fetchEntityKindPrivileges(entityKind, metadata, statement.getPrivileges()); + + for (EntityPrivilege privilege : privileges) { + accessControl.checkCanDenyEntityPrivilege(session.toSecurityContext(), privilege, entity, createPrincipal(statement.getGrantee())); + } + + metadata.denyEntityPrivileges(session, entity, privileges, createPrincipal(statement.getGrantee())); + } } diff --git a/core/trino-main/src/main/java/io/trino/execution/GrantTask.java b/core/trino-main/src/main/java/io/trino/execution/GrantTask.java index 62f522b1ffc38..7d46882de6db5 100644 --- a/core/trino-main/src/main/java/io/trino/execution/GrantTask.java +++ b/core/trino-main/src/main/java/io/trino/execution/GrantTask.java @@ -22,18 +22,21 @@ import io.trino.metadata.RedirectionAwareTableHandle; import io.trino.security.AccessControl; import io.trino.spi.connector.CatalogSchemaName; +import io.trino.spi.connector.EntityKindAndName; +import io.trino.spi.connector.EntityPrivilege; import io.trino.spi.security.Privilege; import io.trino.sql.tree.Expression; import io.trino.sql.tree.Grant; -import io.trino.sql.tree.GrantOnType; import java.util.List; import java.util.Optional; import java.util.Set; import static com.google.common.util.concurrent.Futures.immediateVoidFuture; +import static io.trino.execution.PrivilegeUtilities.fetchEntityKindPrivileges; import static io.trino.execution.PrivilegeUtilities.parseStatementPrivileges; import static io.trino.metadata.MetadataUtil.createCatalogSchemaName; +import static io.trino.metadata.MetadataUtil.createEntityKindAndName; import static io.trino.metadata.MetadataUtil.createPrincipal; import static io.trino.metadata.MetadataUtil.createQualifiedObjectName; import static io.trino.spi.StandardErrorCode.NOT_SUPPORTED; @@ -68,18 +71,22 @@ public ListenableFuture execute( List parameters, WarningCollector warningCollector) { - if (statement.getType().filter(GrantOnType.SCHEMA::equals).isPresent()) { + String entityKind = statement.getGrantObject().getEntityKind().orElse("TABLE"); + if (entityKind.equalsIgnoreCase("TABLE")) { + executeGrantOnTable(stateMachine.getSession(), statement); + } + else if (entityKind.equalsIgnoreCase("SCHEMA")) { executeGrantOnSchema(stateMachine.getSession(), statement); } else { - executeGrantOnTable(stateMachine.getSession(), statement); + executeGrantOnEntity(stateMachine.getSession(), entityKind, metadata, statement); } return immediateVoidFuture(); } private void executeGrantOnSchema(Session session, Grant statement) { - CatalogSchemaName schemaName = createCatalogSchemaName(session, statement, Optional.of(statement.getName())); + CatalogSchemaName schemaName = createCatalogSchemaName(session, statement, Optional.of(statement.getGrantObject().getName())); if (!metadata.schemaExists(session, schemaName)) { throw semanticException(SCHEMA_NOT_FOUND, statement, "Schema '%s' does not exist", schemaName); @@ -95,7 +102,7 @@ private void executeGrantOnSchema(Session session, Grant statement) private void executeGrantOnTable(Session session, Grant statement) { - QualifiedObjectName tableName = createQualifiedObjectName(session, statement, statement.getName()); + QualifiedObjectName tableName = createQualifiedObjectName(session, statement, statement.getGrantObject().getName()); RedirectionAwareTableHandle redirection = metadata.getRedirectionAwareTableHandle(session, tableName); if (redirection.tableHandle().isEmpty()) { throw semanticException(TABLE_NOT_FOUND, statement, "Table '%s' does not exist", tableName); @@ -112,4 +119,16 @@ private void executeGrantOnTable(Session session, Grant statement) metadata.grantTablePrivileges(session, tableName, privileges, createPrincipal(statement.getGrantee()), statement.isWithGrantOption()); } + + private void executeGrantOnEntity(Session session, String entityKind, Metadata metadata, Grant statement) + { + EntityKindAndName entity = createEntityKindAndName(entityKind, statement.getGrantObject().getName()); + Set privileges = fetchEntityKindPrivileges(entityKind, metadata, statement.getPrivileges()); + + for (EntityPrivilege privilege : privileges) { + accessControl.checkCanGrantEntityPrivilege(session.toSecurityContext(), privilege, entity, createPrincipal(statement.getGrantee()), statement.isWithGrantOption()); + } + + metadata.grantEntityPrivileges(session, entity, privileges, createPrincipal(statement.getGrantee()), statement.isWithGrantOption()); + } } diff --git a/core/trino-main/src/main/java/io/trino/execution/PrivilegeUtilities.java b/core/trino-main/src/main/java/io/trino/execution/PrivilegeUtilities.java index c172e2095b07f..fc15fe0fa8d5e 100644 --- a/core/trino-main/src/main/java/io/trino/execution/PrivilegeUtilities.java +++ b/core/trino-main/src/main/java/io/trino/execution/PrivilegeUtilities.java @@ -13,11 +13,15 @@ */ package io.trino.execution; +import io.trino.metadata.Metadata; +import io.trino.spi.TrinoException; +import io.trino.spi.connector.EntityPrivilege; import io.trino.spi.security.Privilege; import io.trino.sql.tree.Node; import java.util.EnumSet; import java.util.List; +import java.util.Locale; import java.util.Optional; import java.util.Set; @@ -44,6 +48,24 @@ public static Set parseStatementPrivileges(Node statement, Optional fetchEntityKindPrivileges(String entityKind, Metadata metadata, Optional> privileges) + { + Set allPrivileges = metadata.getAllEntityKindPrivileges(entityKind); + if (privileges.isPresent()) { + return privileges.get().stream() + .map(privilege -> { + EntityPrivilege entityPrivilege = new EntityPrivilege(privilege.toUpperCase(Locale.ENGLISH)); + if (!allPrivileges.contains(entityPrivilege)) { + throw new TrinoException(INVALID_PRIVILEGE, "Privilege %s is not supported for entity kind %s".formatted(privilege, entityKind)); + } + return entityPrivilege; + }).collect(toImmutableSet()); + } + else { + return allPrivileges; + } + } + private static Privilege parsePrivilege(Node statement, String privilegeString) { for (Privilege privilege : Privilege.values()) { diff --git a/core/trino-main/src/main/java/io/trino/execution/RevokeTask.java b/core/trino-main/src/main/java/io/trino/execution/RevokeTask.java index 47bda73522dda..ceb3495add74a 100644 --- a/core/trino-main/src/main/java/io/trino/execution/RevokeTask.java +++ b/core/trino-main/src/main/java/io/trino/execution/RevokeTask.java @@ -22,9 +22,10 @@ import io.trino.metadata.RedirectionAwareTableHandle; import io.trino.security.AccessControl; import io.trino.spi.connector.CatalogSchemaName; +import io.trino.spi.connector.EntityKindAndName; +import io.trino.spi.connector.EntityPrivilege; import io.trino.spi.security.Privilege; import io.trino.sql.tree.Expression; -import io.trino.sql.tree.GrantOnType; import io.trino.sql.tree.Revoke; import java.util.List; @@ -32,8 +33,10 @@ import java.util.Set; import static com.google.common.util.concurrent.Futures.immediateVoidFuture; +import static io.trino.execution.PrivilegeUtilities.fetchEntityKindPrivileges; import static io.trino.execution.PrivilegeUtilities.parseStatementPrivileges; import static io.trino.metadata.MetadataUtil.createCatalogSchemaName; +import static io.trino.metadata.MetadataUtil.createEntityKindAndName; import static io.trino.metadata.MetadataUtil.createPrincipal; import static io.trino.metadata.MetadataUtil.createQualifiedObjectName; import static io.trino.spi.StandardErrorCode.NOT_SUPPORTED; @@ -68,18 +71,23 @@ public ListenableFuture execute( List parameters, WarningCollector warningCollector) { - if (statement.getType().filter(GrantOnType.SCHEMA::equals).isPresent()) { + String entityKind = statement.getGrantObject().getEntityKind().orElse("TABLE"); + if (entityKind.equalsIgnoreCase("TABLE")) { + executeRevokeOnTable(stateMachine.getSession(), statement); + } + else if (entityKind.equalsIgnoreCase("SCHEMA")) { executeRevokeOnSchema(stateMachine.getSession(), statement); } else { - executeRevokeOnTable(stateMachine.getSession(), statement); + executeRevokeOnEntity(stateMachine.getSession(), entityKind, metadata, statement); } + return immediateVoidFuture(); } private void executeRevokeOnSchema(Session session, Revoke statement) { - CatalogSchemaName schemaName = createCatalogSchemaName(session, statement, Optional.of(statement.getName())); + CatalogSchemaName schemaName = createCatalogSchemaName(session, statement, Optional.of(statement.getGrantObject().getName())); if (!metadata.schemaExists(session, schemaName)) { throw semanticException(SCHEMA_NOT_FOUND, statement, "Schema '%s' does not exist", schemaName); @@ -95,7 +103,7 @@ private void executeRevokeOnSchema(Session session, Revoke statement) private void executeRevokeOnTable(Session session, Revoke statement) { - QualifiedObjectName tableName = createQualifiedObjectName(session, statement, statement.getName()); + QualifiedObjectName tableName = createQualifiedObjectName(session, statement, statement.getGrantObject().getName()); RedirectionAwareTableHandle redirection = metadata.getRedirectionAwareTableHandle(session, tableName); if (redirection.tableHandle().isEmpty()) { throw semanticException(TABLE_NOT_FOUND, statement, "Table '%s' does not exist", tableName); @@ -111,4 +119,16 @@ private void executeRevokeOnTable(Session session, Revoke statement) metadata.revokeTablePrivileges(session, tableName, privileges, createPrincipal(statement.getGrantee()), statement.isGrantOptionFor()); } + + private void executeRevokeOnEntity(Session session, String entityKind, Metadata metadata, Revoke statement) + { + EntityKindAndName entity = createEntityKindAndName(entityKind, statement.getGrantObject().getName()); + Set privileges = fetchEntityKindPrivileges(entityKind, metadata, statement.getPrivileges()); + + for (EntityPrivilege privilege : privileges) { + accessControl.checkCanRevokeEntityPrivilege(session.toSecurityContext(), privilege, entity, createPrincipal(statement.getGrantee()), statement.isGrantOptionFor()); + } + + metadata.revokeEntityPrivileges(session, entity, privileges, createPrincipal(statement.getGrantee()), statement.isGrantOptionFor()); + } } diff --git a/core/trino-main/src/main/java/io/trino/metadata/DisabledSystemSecurityMetadata.java b/core/trino-main/src/main/java/io/trino/metadata/DisabledSystemSecurityMetadata.java index e0920b94e3ac5..74790970e6a82 100644 --- a/core/trino-main/src/main/java/io/trino/metadata/DisabledSystemSecurityMetadata.java +++ b/core/trino-main/src/main/java/io/trino/metadata/DisabledSystemSecurityMetadata.java @@ -18,6 +18,8 @@ import io.trino.spi.TrinoException; import io.trino.spi.connector.CatalogSchemaName; import io.trino.spi.connector.CatalogSchemaTableName; +import io.trino.spi.connector.EntityKindAndName; +import io.trino.spi.connector.EntityPrivilege; import io.trino.spi.function.CatalogSchemaFunctionName; import io.trino.spi.security.GrantInfo; import io.trino.spi.security.Identity; @@ -25,8 +27,10 @@ import io.trino.spi.security.RoleGrant; import io.trino.spi.security.TrinoPrincipal; +import java.util.List; import java.util.Optional; import java.util.Set; +import java.util.stream.Collectors; import static io.trino.spi.StandardErrorCode.NOT_SUPPORTED; @@ -137,6 +141,24 @@ public Set listTablePrivileges(Session session, QualifiedTablePrefix return ImmutableSet.of(); } + @Override + public void grantEntityPrivileges(Session session, EntityKindAndName entity, Set privileges, TrinoPrincipal grantee, boolean grantOption) + { + throw notSupportedException(composeNameString(entity.name())); + } + + @Override + public void denyEntityPrivileges(Session session, EntityKindAndName entity, Set privileges, TrinoPrincipal grantee) + { + throw notSupportedException(composeNameString(entity.name())); + } + + @Override + public void revokeEntityPrivileges(Session session, EntityKindAndName entity, Set privileges, TrinoPrincipal grantee, boolean grantOption) + { + throw notSupportedException(composeNameString(entity.name())); + } + @Override public Optional getSchemaOwner(Session session, CatalogSchemaName schema) { @@ -207,4 +229,9 @@ private static TrinoException notSupportedException(String catalogName) { return new TrinoException(NOT_SUPPORTED, "Catalog does not support permission management: " + catalogName); } + + private String composeNameString(List parts) + { + return parts.stream().collect(Collectors.joining(".")); + } } diff --git a/core/trino-main/src/main/java/io/trino/metadata/Metadata.java b/core/trino-main/src/main/java/io/trino/metadata/Metadata.java index 00ac637ceeb45..1a90b94f71a44 100644 --- a/core/trino-main/src/main/java/io/trino/metadata/Metadata.java +++ b/core/trino-main/src/main/java/io/trino/metadata/Metadata.java @@ -30,6 +30,8 @@ import io.trino.spi.connector.ConnectorTableMetadata; import io.trino.spi.connector.Constraint; import io.trino.spi.connector.ConstraintApplicationResult; +import io.trino.spi.connector.EntityKindAndName; +import io.trino.spi.connector.EntityPrivilege; import io.trino.spi.connector.JoinApplicationResult; import io.trino.spi.connector.JoinStatistics; import io.trino.spi.connector.JoinType; @@ -668,7 +670,7 @@ default void validateScan(Session session, TableHandle table) {} void denyTablePrivileges(Session session, QualifiedObjectName tableName, Set privileges, TrinoPrincipal grantee); /** - * Revokes the specified privilege on the specified table from the specified user + * Revokes the specified privilege on the specified table from the specified user. */ void revokeTablePrivileges(Session session, QualifiedObjectName tableName, Set privileges, TrinoPrincipal grantee, boolean grantOption); @@ -677,6 +679,42 @@ default void validateScan(Session session, TableHandle table) {} */ List listTablePrivileges(Session session, QualifiedTablePrefix prefix); + /** + * Gets all the EntityPrivileges associated with an entityKind. Defines ALL PRIVILEGES + * for the entityKind + */ + default Set getAllEntityKindPrivileges(String entityKind) + { + throw new UnsupportedOperationException(); + } + + /** + * Grants the specified privileges to the specified user on the specified grantee. + * If the set of privileges is empty, it is interpreted as all privileges for the entityKind. + */ + default void grantEntityPrivileges(Session session, EntityKindAndName entity, Set privileges, TrinoPrincipal grantee, boolean grantOption) + { + throw new UnsupportedOperationException(); + } + + /** + * Deny the specified privileges to the specified principal on the specified entity. + * If the set of privileges is empty, it is interpreted as all privileges for the entityKind. + */ + default void denyEntityPrivileges(Session session, EntityKindAndName entity, Set privileges, TrinoPrincipal grantee) + { + throw new UnsupportedOperationException(); + } + + /** + * Revokes the specified privilege on the specified entity from the specified grantee. + * If the set of privileges is empty, it is interpreted as all privileges for the entityKind. + */ + default void revokeEntityPrivileges(Session session, EntityKindAndName entity, Set privileges, TrinoPrincipal grantee, boolean grantOption) + { + throw new UnsupportedOperationException(); + } + // // Functions // diff --git a/core/trino-main/src/main/java/io/trino/metadata/MetadataManager.java b/core/trino-main/src/main/java/io/trino/metadata/MetadataManager.java index a5431b17d8e0f..6e7f2ed8ac1e9 100644 --- a/core/trino-main/src/main/java/io/trino/metadata/MetadataManager.java +++ b/core/trino-main/src/main/java/io/trino/metadata/MetadataManager.java @@ -61,6 +61,8 @@ import io.trino.spi.connector.ConnectorViewDefinition; import io.trino.spi.connector.Constraint; import io.trino.spi.connector.ConstraintApplicationResult; +import io.trino.spi.connector.EntityKindAndName; +import io.trino.spi.connector.EntityPrivilege; import io.trino.spi.connector.JoinApplicationResult; import io.trino.spi.connector.JoinStatistics; import io.trino.spi.connector.JoinType; @@ -2392,6 +2394,31 @@ public List listTablePrivileges(Session session, QualifiedTablePrefix return ImmutableList.copyOf(grantInfos.build()); } + @Override + public Set getAllEntityKindPrivileges(String entityKind) + { + requireNonNull(entityKind, "entityKind is null"); + return systemSecurityMetadata.getAllEntityKindPrivileges(entityKind); + } + + @Override + public void grantEntityPrivileges(Session session, EntityKindAndName entity, Set privileges, TrinoPrincipal grantee, boolean grantOption) + { + systemSecurityMetadata.grantEntityPrivileges(session, entity, privileges, grantee, grantOption); + } + + @Override + public void denyEntityPrivileges(Session session, EntityKindAndName entity, Set privileges, TrinoPrincipal grantee) + { + systemSecurityMetadata.denyEntityPrivileges(session, entity, privileges, grantee); + } + + @Override + public void revokeEntityPrivileges(Session session, EntityKindAndName entity, Set privileges, TrinoPrincipal grantee, boolean grantOption) + { + systemSecurityMetadata.revokeEntityPrivileges(session, entity, privileges, grantee, grantOption); + } + // // Functions // diff --git a/core/trino-main/src/main/java/io/trino/metadata/MetadataUtil.java b/core/trino-main/src/main/java/io/trino/metadata/MetadataUtil.java index dc090f2648097..fcdf9c0c8b50f 100644 --- a/core/trino-main/src/main/java/io/trino/metadata/MetadataUtil.java +++ b/core/trino-main/src/main/java/io/trino/metadata/MetadataUtil.java @@ -21,6 +21,7 @@ import io.trino.spi.connector.CatalogSchemaName; import io.trino.spi.connector.ColumnMetadata; import io.trino.spi.connector.ConnectorTableMetadata; +import io.trino.spi.connector.EntityKindAndName; import io.trino.spi.connector.SchemaTableName; import io.trino.spi.security.PrincipalType; import io.trino.spi.security.TrinoPrincipal; @@ -153,6 +154,11 @@ public static QualifiedObjectName createQualifiedObjectName(Session session, Nod return new QualifiedObjectName(catalogName, schemaName, objectName); } + public static EntityKindAndName createEntityKindAndName(String entityKind, QualifiedName name) + { + return new EntityKindAndName(entityKind, name.getParts()); + } + public static TrinoPrincipal createPrincipal(Session session, GrantorSpecification specification) { GrantorSpecification.Type type = specification.getType(); diff --git a/core/trino-main/src/main/java/io/trino/metadata/SystemSecurityMetadata.java b/core/trino-main/src/main/java/io/trino/metadata/SystemSecurityMetadata.java index 8f528faa86202..1aae90b066c2b 100644 --- a/core/trino-main/src/main/java/io/trino/metadata/SystemSecurityMetadata.java +++ b/core/trino-main/src/main/java/io/trino/metadata/SystemSecurityMetadata.java @@ -16,6 +16,8 @@ import io.trino.Session; import io.trino.spi.connector.CatalogSchemaName; import io.trino.spi.connector.CatalogSchemaTableName; +import io.trino.spi.connector.EntityKindAndName; +import io.trino.spi.connector.EntityPrivilege; import io.trino.spi.function.CatalogSchemaFunctionName; import io.trino.spi.security.GrantInfo; import io.trino.spi.security.Identity; @@ -114,6 +116,44 @@ public interface SystemSecurityMetadata */ Set listTablePrivileges(Session session, QualifiedTablePrefix prefix); + default Set getAllEntityKindPrivileges(String entityKind) + { + throw new UnsupportedOperationException(); + } + + /** + * Grants the specified privilege to the specified user on the specified entity + */ + default void grantEntityPrivileges(Session session, EntityKindAndName entity, Set privileges, TrinoPrincipal grantee, boolean grantOption) + { + throw new UnsupportedOperationException(); + } + + /** + * Deny the specified privilege to the specified principal on the specified entity + */ + default void denyEntityPrivileges(Session session, EntityKindAndName entity, Set privileges, TrinoPrincipal grantee) + { + throw new UnsupportedOperationException(); + } + + /** + * Revokes the specified privilege on the specified entity from the specified user + */ + default void revokeEntityPrivileges(Session session, EntityKindAndName entity, Set privileges, TrinoPrincipal grantee, boolean grantOption) + { + throw new UnsupportedOperationException(); + } + + /** + * Throws an exception if the entityKind is not supported, or if the privileges + * are not supported for the entityKind + */ + default void validateEntityKindAndPrivileges(Session session, String entityKind, Set privileges) + { + throw new UnsupportedOperationException(); + } + /** * Set the owner of the specified schema */ diff --git a/core/trino-main/src/main/java/io/trino/security/AccessControl.java b/core/trino-main/src/main/java/io/trino/security/AccessControl.java index b3e6c24f394e3..dff63b7c8c4d9 100644 --- a/core/trino-main/src/main/java/io/trino/security/AccessControl.java +++ b/core/trino-main/src/main/java/io/trino/security/AccessControl.java @@ -17,6 +17,8 @@ import io.trino.metadata.QualifiedObjectName; import io.trino.spi.connector.CatalogSchemaName; import io.trino.spi.connector.CatalogSchemaTableName; +import io.trino.spi.connector.EntityKindAndName; +import io.trino.spi.connector.EntityPrivilege; import io.trino.spi.connector.SchemaTableName; import io.trino.spi.function.SchemaFunctionName; import io.trino.spi.security.AccessDeniedException; @@ -435,6 +437,27 @@ default void checkCanSetViewAuthorization(SecurityContext context, QualifiedObje */ void checkCanRevokeTablePrivilege(SecurityContext context, Privilege privilege, QualifiedObjectName tableName, TrinoPrincipal revokee, boolean grantOption); + /** + * Check if identity is allowed to grant the specified privilege to the grantee on the specified entity. + * + * @throws AccessDeniedException if not allowed + */ + void checkCanGrantEntityPrivilege(SecurityContext context, EntityPrivilege privilege, EntityKindAndName entity, TrinoPrincipal grantee, boolean grantOption); + + /** + * Check if identity is allowed to deny the specified privilege to the grantee on the specified entity. + * + * @throws AccessDeniedException if not allowed + */ + void checkCanDenyEntityPrivilege(SecurityContext context, EntityPrivilege privilege, EntityKindAndName entity, TrinoPrincipal grantee); + + /** + * Check if identity is allowed to revoke the specified privilege on the specified entity from the revokee. + * + * @throws AccessDeniedException if not allowed + */ + void checkCanRevokeEntityPrivilege(SecurityContext context, EntityPrivilege privilege, EntityKindAndName entity, TrinoPrincipal revokee, boolean grantOption); + /** * Check if identity is allowed to set the specified system property. * diff --git a/core/trino-main/src/main/java/io/trino/security/AccessControlManager.java b/core/trino-main/src/main/java/io/trino/security/AccessControlManager.java index 90ff4afab672b..10b2478c32fac 100644 --- a/core/trino-main/src/main/java/io/trino/security/AccessControlManager.java +++ b/core/trino-main/src/main/java/io/trino/security/AccessControlManager.java @@ -41,6 +41,8 @@ import io.trino.spi.connector.CatalogSchemaTableName; import io.trino.spi.connector.ConnectorAccessControl; import io.trino.spi.connector.ConnectorSecurityContext; +import io.trino.spi.connector.EntityKindAndName; +import io.trino.spi.connector.EntityPrivilege; import io.trino.spi.connector.SchemaTableName; import io.trino.spi.function.FunctionKind; import io.trino.spi.function.SchemaFunctionName; @@ -1014,6 +1016,36 @@ public void checkCanRevokeTablePrivilege(SecurityContext securityContext, Privil catalogAuthorizationCheck(tableName.getCatalogName(), securityContext, (control, context) -> control.checkCanRevokeTablePrivilege(context, privilege, tableName.asSchemaTableName(), revokee, grantOption)); } + @Override + public void checkCanGrantEntityPrivilege(SecurityContext securityContext, EntityPrivilege privilege, EntityKindAndName entity, TrinoPrincipal grantee, boolean grantOption) + { + requireNonNull(securityContext, "securityContext is null"); + requireNonNull(entity, "entity is null"); + requireNonNull(privilege, "privilege is null"); + + systemAuthorizationCheck(control -> control.checkCanGrantEntityPrivilege(securityContext.toSystemSecurityContext(), privilege, entity, grantee, grantOption)); + } + + @Override + public void checkCanDenyEntityPrivilege(SecurityContext securityContext, EntityPrivilege privilege, EntityKindAndName entity, TrinoPrincipal grantee) + { + requireNonNull(securityContext, "securityContext is null"); + requireNonNull(entity, "entity is null"); + requireNonNull(privilege, "privilege is null"); + + systemAuthorizationCheck(control -> control.checkCanDenyEntityPrivilege(securityContext.toSystemSecurityContext(), privilege, entity, grantee)); + } + + @Override + public void checkCanRevokeEntityPrivilege(SecurityContext securityContext, EntityPrivilege privilege, EntityKindAndName entity, TrinoPrincipal revokee, boolean grantOption) + { + requireNonNull(securityContext, "securityContext is null"); + requireNonNull(entity, "entity is null"); + requireNonNull(privilege, "privilege is null"); + + systemAuthorizationCheck(control -> control.checkCanRevokeEntityPrivilege(securityContext.toSystemSecurityContext(), privilege, entity, revokee, grantOption)); + } + @Override public void checkCanSetSystemSessionProperty(Identity identity, String propertyName) { diff --git a/core/trino-main/src/main/java/io/trino/security/AllowAllAccessControl.java b/core/trino-main/src/main/java/io/trino/security/AllowAllAccessControl.java index 64f23efeefbfd..a9f01b26c4ece 100644 --- a/core/trino-main/src/main/java/io/trino/security/AllowAllAccessControl.java +++ b/core/trino-main/src/main/java/io/trino/security/AllowAllAccessControl.java @@ -16,6 +16,8 @@ import io.trino.metadata.QualifiedObjectName; import io.trino.spi.connector.CatalogSchemaName; import io.trino.spi.connector.CatalogSchemaTableName; +import io.trino.spi.connector.EntityKindAndName; +import io.trino.spi.connector.EntityPrivilege; import io.trino.spi.connector.SchemaTableName; import io.trino.spi.function.SchemaFunctionName; import io.trino.spi.security.Identity; @@ -323,6 +325,21 @@ public void checkCanRevokeTablePrivilege(SecurityContext context, Privilege priv { } + @Override + public void checkCanGrantEntityPrivilege(SecurityContext context, EntityPrivilege privilege, EntityKindAndName entity, TrinoPrincipal grantee, boolean grantOption) + { + } + + @Override + public void checkCanDenyEntityPrivilege(SecurityContext context, EntityPrivilege privilege, EntityKindAndName entity, TrinoPrincipal grantee) + { + } + + @Override + public void checkCanRevokeEntityPrivilege(SecurityContext context, EntityPrivilege privilege, EntityKindAndName entity, TrinoPrincipal revokee, boolean grantOption) + { + } + @Override public void checkCanSetSystemSessionProperty(Identity identity, String propertyName) { diff --git a/core/trino-main/src/main/java/io/trino/security/DenyAllAccessControl.java b/core/trino-main/src/main/java/io/trino/security/DenyAllAccessControl.java index 7d3013b1c5352..7787465576e2b 100644 --- a/core/trino-main/src/main/java/io/trino/security/DenyAllAccessControl.java +++ b/core/trino-main/src/main/java/io/trino/security/DenyAllAccessControl.java @@ -19,6 +19,8 @@ import io.trino.metadata.QualifiedObjectName; import io.trino.spi.connector.CatalogSchemaName; import io.trino.spi.connector.CatalogSchemaTableName; +import io.trino.spi.connector.EntityKindAndName; +import io.trino.spi.connector.EntityPrivilege; import io.trino.spi.connector.SchemaTableName; import io.trino.spi.function.SchemaFunctionName; import io.trino.spi.security.Identity; @@ -45,6 +47,7 @@ import static io.trino.spi.security.AccessDeniedException.denyCreateView; import static io.trino.spi.security.AccessDeniedException.denyCreateViewWithSelect; import static io.trino.spi.security.AccessDeniedException.denyDeleteTable; +import static io.trino.spi.security.AccessDeniedException.denyDenyEntityPrivilege; import static io.trino.spi.security.AccessDeniedException.denyDenySchemaPrivilege; import static io.trino.spi.security.AccessDeniedException.denyDenyTablePrivilege; import static io.trino.spi.security.AccessDeniedException.denyDropCatalog; @@ -58,6 +61,7 @@ import static io.trino.spi.security.AccessDeniedException.denyExecuteProcedure; import static io.trino.spi.security.AccessDeniedException.denyExecuteQuery; import static io.trino.spi.security.AccessDeniedException.denyExecuteTableProcedure; +import static io.trino.spi.security.AccessDeniedException.denyGrantEntityPrivilege; import static io.trino.spi.security.AccessDeniedException.denyGrantRoles; import static io.trino.spi.security.AccessDeniedException.denyGrantSchemaPrivilege; import static io.trino.spi.security.AccessDeniedException.denyGrantTablePrivilege; @@ -71,6 +75,7 @@ import static io.trino.spi.security.AccessDeniedException.denyRenameSchema; import static io.trino.spi.security.AccessDeniedException.denyRenameTable; import static io.trino.spi.security.AccessDeniedException.denyRenameView; +import static io.trino.spi.security.AccessDeniedException.denyRevokeEntityPrivilege; import static io.trino.spi.security.AccessDeniedException.denyRevokeRoles; import static io.trino.spi.security.AccessDeniedException.denyRevokeSchemaPrivilege; import static io.trino.spi.security.AccessDeniedException.denyRevokeTablePrivilege; @@ -431,6 +436,24 @@ public void checkCanRevokeTablePrivilege(SecurityContext context, Privilege priv denyRevokeTablePrivilege(privilege.name(), tableName.toString()); } + @Override + public void checkCanGrantEntityPrivilege(SecurityContext context, EntityPrivilege privilege, EntityKindAndName entity, TrinoPrincipal grantee, boolean grantOption) + { + denyGrantEntityPrivilege(privilege.name(), entity); + } + + @Override + public void checkCanDenyEntityPrivilege(SecurityContext context, EntityPrivilege privilege, EntityKindAndName entity, TrinoPrincipal grantee) + { + denyDenyEntityPrivilege(privilege.name(), entity); + } + + @Override + public void checkCanRevokeEntityPrivilege(SecurityContext context, EntityPrivilege privilege, EntityKindAndName entity, TrinoPrincipal revokee, boolean grantOption) + { + denyRevokeEntityPrivilege(privilege.name(), entity); + } + @Override public void checkCanSetSystemSessionProperty(Identity identity, String propertyName) { diff --git a/core/trino-main/src/main/java/io/trino/security/ForwardingAccessControl.java b/core/trino-main/src/main/java/io/trino/security/ForwardingAccessControl.java index a9522729a2ecf..de75fc97c312a 100644 --- a/core/trino-main/src/main/java/io/trino/security/ForwardingAccessControl.java +++ b/core/trino-main/src/main/java/io/trino/security/ForwardingAccessControl.java @@ -16,6 +16,8 @@ import io.trino.metadata.QualifiedObjectName; import io.trino.spi.connector.CatalogSchemaName; import io.trino.spi.connector.CatalogSchemaTableName; +import io.trino.spi.connector.EntityKindAndName; +import io.trino.spi.connector.EntityPrivilege; import io.trino.spi.connector.SchemaTableName; import io.trino.spi.function.SchemaFunctionName; import io.trino.spi.security.Identity; @@ -383,6 +385,24 @@ public void checkCanRevokeTablePrivilege(SecurityContext context, Privilege priv delegate().checkCanRevokeTablePrivilege(context, privilege, tableName, revokee, grantOption); } + @Override + public void checkCanGrantEntityPrivilege(SecurityContext context, EntityPrivilege privilege, EntityKindAndName entity, TrinoPrincipal grantee, boolean grantOption) + { + delegate().checkCanGrantEntityPrivilege(context, privilege, entity, grantee, grantOption); + } + + @Override + public void checkCanDenyEntityPrivilege(SecurityContext context, EntityPrivilege privilege, EntityKindAndName entity, TrinoPrincipal grantee) + { + delegate().checkCanDenyEntityPrivilege(context, privilege, entity, grantee); + } + + @Override + public void checkCanRevokeEntityPrivilege(SecurityContext context, EntityPrivilege privilege, EntityKindAndName entity, TrinoPrincipal revokee, boolean grantOption) + { + delegate().checkCanRevokeEntityPrivilege(context, privilege, entity, revokee, grantOption); + } + @Override public void checkCanSetSystemSessionProperty(Identity identity, String propertyName) { diff --git a/core/trino-main/src/main/java/io/trino/sql/rewrite/ShowQueriesRewrite.java b/core/trino-main/src/main/java/io/trino/sql/rewrite/ShowQueriesRewrite.java index 1f8b6c5e927de..441c9bd08e2a7 100644 --- a/core/trino-main/src/main/java/io/trino/sql/rewrite/ShowQueriesRewrite.java +++ b/core/trino-main/src/main/java/io/trino/sql/rewrite/ShowQueriesRewrite.java @@ -71,6 +71,7 @@ import io.trino.sql.tree.Explain; import io.trino.sql.tree.ExplainAnalyze; import io.trino.sql.tree.Expression; +import io.trino.sql.tree.GrantObject; import io.trino.sql.tree.Identifier; import io.trino.sql.tree.LikePredicate; import io.trino.sql.tree.LongLiteral; @@ -313,7 +314,8 @@ protected Node visitShowGrants(ShowGrants showGrants, Void context) String catalogName = session.getCatalog().orElse(null); Optional predicate = Optional.empty(); - Optional tableName = showGrants.getTableName(); + // TODO: Should this handle any entityKind? + Optional tableName = showGrants.getGrantObject().map(GrantObject::getName); if (tableName.isPresent()) { QualifiedObjectName qualifiedTableName = createQualifiedObjectName(session, showGrants, tableName.get()); if (!metadata.isView(session, qualifiedTableName)) { diff --git a/core/trino-main/src/main/java/io/trino/tracing/TracingAccessControl.java b/core/trino-main/src/main/java/io/trino/tracing/TracingAccessControl.java index 672a9db118247..1344b836fc0ac 100644 --- a/core/trino-main/src/main/java/io/trino/tracing/TracingAccessControl.java +++ b/core/trino-main/src/main/java/io/trino/tracing/TracingAccessControl.java @@ -22,6 +22,8 @@ import io.trino.security.SecurityContext; import io.trino.spi.connector.CatalogSchemaName; import io.trino.spi.connector.CatalogSchemaTableName; +import io.trino.spi.connector.EntityKindAndName; +import io.trino.spi.connector.EntityPrivilege; import io.trino.spi.connector.SchemaTableName; import io.trino.spi.function.SchemaFunctionName; import io.trino.spi.security.Identity; @@ -554,6 +556,33 @@ public void checkCanRevokeTablePrivilege(SecurityContext context, Privilege priv } } + @Override + public void checkCanGrantEntityPrivilege(SecurityContext context, EntityPrivilege privilege, EntityKindAndName entity, TrinoPrincipal grantee, boolean grantOption) + { + Span span = startSpan("checkCanGrantEntityPrivilege"); + try (var ignored = scopedSpan(span)) { + delegate.checkCanGrantEntityPrivilege(context, privilege, entity, grantee, grantOption); + } + } + + @Override + public void checkCanDenyEntityPrivilege(SecurityContext context, EntityPrivilege privilege, EntityKindAndName entity, TrinoPrincipal grantee) + { + Span span = startSpan("checkCanDenyEntityPrivilege"); + try (var ignored = scopedSpan(span)) { + delegate.checkCanDenyEntityPrivilege(context, privilege, entity, grantee); + } + } + + @Override + public void checkCanRevokeEntityPrivilege(SecurityContext context, EntityPrivilege privilege, EntityKindAndName entity, TrinoPrincipal revokee, boolean grantOption) + { + Span span = startSpan("checkCanRevokeEntityPrivilege"); + try (var ignored = scopedSpan(span)) { + delegate.checkCanRevokeEntityPrivilege(context, privilege, entity, revokee, grantOption); + } + } + @Override public void checkCanSetSystemSessionProperty(Identity identity, String propertyName) { diff --git a/core/trino-main/src/main/java/io/trino/tracing/TracingMetadata.java b/core/trino-main/src/main/java/io/trino/tracing/TracingMetadata.java index a1ac8733a4dfd..844ed48ff30cb 100644 --- a/core/trino-main/src/main/java/io/trino/tracing/TracingMetadata.java +++ b/core/trino-main/src/main/java/io/trino/tracing/TracingMetadata.java @@ -58,6 +58,8 @@ import io.trino.spi.connector.ConnectorTableMetadata; import io.trino.spi.connector.Constraint; import io.trino.spi.connector.ConstraintApplicationResult; +import io.trino.spi.connector.EntityKindAndName; +import io.trino.spi.connector.EntityPrivilege; import io.trino.spi.connector.JoinApplicationResult; import io.trino.spi.connector.JoinStatistics; import io.trino.spi.connector.JoinType; @@ -110,6 +112,7 @@ import java.util.OptionalLong; import java.util.Set; import java.util.function.UnaryOperator; +import java.util.stream.Collectors; import static io.trino.tracing.ScopedSpan.scopedSpan; import static java.util.Objects.requireNonNull; @@ -1203,6 +1206,39 @@ public List listTablePrivileges(Session session, QualifiedTablePrefix } } + @Override + public Set getAllEntityKindPrivileges(String entityKind) + { + return delegate.getAllEntityKindPrivileges(entityKind); + } + + @Override + public void grantEntityPrivileges(Session session, EntityKindAndName entity, Set privileges, TrinoPrincipal grantee, boolean grantOption) + { + Span span = startSpan("grantEntityPrivileges", entity, privileges, grantee, grantOption); + try (var ignored = scopedSpan(span)) { + delegate.grantEntityPrivileges(session, entity, privileges, grantee, grantOption); + } + } + + @Override + public void denyEntityPrivileges(Session session, EntityKindAndName entity, Set privileges, TrinoPrincipal grantee) + { + Span span = startSpan("denyEntityPrivileges", entity, privileges, grantee, false); + try (var ignored = scopedSpan(span)) { + delegate.denyEntityPrivileges(session, entity, privileges, grantee); + } + } + + @Override + public void revokeEntityPrivileges(Session session, EntityKindAndName entity, Set privileges, TrinoPrincipal grantee, boolean grantOption) + { + Span span = startSpan("revokeEntityPrivileges", entity, privileges, grantee, grantOption); + try (var ignored = scopedSpan(span)) { + delegate.revokeEntityPrivileges(session, entity, privileges, grantee, grantOption); + } + } + @Override public Collection listGlobalFunctions(Session session) { @@ -1575,4 +1611,29 @@ private Span startSpan(String methodName, CatalogSchemaFunctionName table) .setAttribute(TrinoAttributes.SCHEMA, table.getSchemaName()) .setAttribute(TrinoAttributes.FUNCTION, table.getFunctionName()); } + + private Span startSpan(String methodName, EntityKindAndName entity, Set privileges, TrinoPrincipal grantee, boolean grantOption) + { + Span span = startSpan(methodName); + if (span.isRecording()) { + String grant = String.format("%s-%s-%s-%s-%s%s", + entity.entityKind(), + entity.name(), + grantee.getType(), + grantee.getName(), + privileges.stream().map(EntityPrivilege::name).collect(Collectors.joining("-")), + grantOption ? "-grantOption" : ""); + span.setAttribute(TrinoAttributes.PRIVILEGE_GRANT, grant); + } + return span; + } + + private Span startSpan(String methodName, EntityKindAndName entity) + { + Span span = startSpan(methodName); + if (span.isRecording()) { + span.setAttribute(TrinoAttributes.ENTITY, String.format("%s-%s", entity.entityKind(), entity.name())); + } + return span; + } } diff --git a/core/trino-main/src/main/java/io/trino/tracing/TrinoAttributes.java b/core/trino-main/src/main/java/io/trino/tracing/TrinoAttributes.java index 785d197b96de1..487002f5b44db 100644 --- a/core/trino-main/src/main/java/io/trino/tracing/TrinoAttributes.java +++ b/core/trino-main/src/main/java/io/trino/tracing/TrinoAttributes.java @@ -62,4 +62,6 @@ private TrinoAttributes() {} public static final AttributeKey EVENT_STATE = stringKey("state"); public static final AttributeKey FAILURE_MESSAGE = stringKey("failure"); + public static final AttributeKey PRIVILEGE_GRANT = stringKey("grant"); + public static final AttributeKey ENTITY = stringKey("entity"); } diff --git a/core/trino-parser/src/main/java/io/trino/sql/SqlFormatter.java b/core/trino-parser/src/main/java/io/trino/sql/SqlFormatter.java index 6cb4166e0724b..d07a296eed581 100644 --- a/core/trino-parser/src/main/java/io/trino/sql/SqlFormatter.java +++ b/core/trino-parser/src/main/java/io/trino/sql/SqlFormatter.java @@ -69,6 +69,7 @@ import io.trino.sql.tree.FetchFirst; import io.trino.sql.tree.FunctionSpecification; import io.trino.sql.tree.Grant; +import io.trino.sql.tree.GrantObject; import io.trino.sql.tree.GrantRoles; import io.trino.sql.tree.GrantorSpecification; import io.trino.sql.tree.Identifier; @@ -2148,15 +2149,16 @@ public Void visitGrant(Grant node, Integer indent) { builder.append("GRANT "); - builder.append(node.getPrivileges() - .map(privileges -> join(", ", privileges)) - .orElse("ALL PRIVILEGES")); - - builder.append(" ON "); - node.getType().ifPresent(type -> builder - .append(type.name()) - .append(' ')); - builder.append(formatName(node.getName())) + if (node.getPrivileges().isEmpty()) { + builder.append("ALL PRIVILEGES"); + } + else { + builder.append(node.getPrivileges() + .map(privileges -> join(", ", privileges)) + .orElseThrow()); + } + builder.append(" ON ") + .append(formatGrantScope(node.getGrantObject())) .append(" TO ") .append(formatPrincipal(node.getGrantee())); if (node.isWithGrantOption()) { @@ -2178,12 +2180,8 @@ public Void visitDeny(Deny node, Integer indent) builder.append("ALL PRIVILEGES"); } - builder.append(" ON "); - if (node.getType().isPresent()) { - builder.append(node.getType().get().name()); - builder.append(" "); - } - builder.append(formatName(node.getName())) + builder.append(" ON ") + .append(formatGrantScope(node.getGrantObject())) .append(" TO ") .append(formatPrincipal(node.getGrantee())); @@ -2199,15 +2197,17 @@ public Void visitRevoke(Revoke node, Integer indent) builder.append("GRANT OPTION FOR "); } - builder.append(node.getPrivileges() - .map(privileges -> join(", ", privileges)) - .orElse("ALL PRIVILEGES")); + if (node.getPrivileges().isEmpty()) { + builder.append("ALL PRIVILEGES"); + } + else { + builder.append(node.getPrivileges() + .map(privileges -> join(", ", privileges)) + .orElseThrow()); + } - builder.append(" ON "); - node.getType().ifPresent(type -> builder - .append(type.name()) - .append(' ')); - builder.append(formatName(node.getName())) + builder.append(" ON ") + .append(formatGrantScope(node.getGrantObject())) .append(" FROM ") .append(formatPrincipal(node.getGrantee())); @@ -2219,13 +2219,8 @@ public Void visitShowGrants(ShowGrants node, Integer indent) { builder.append("SHOW GRANTS "); - node.getTableName().ifPresent(tableName -> { - builder.append("ON "); - if (node.getTable()) { - builder.append("TABLE "); - } - builder.append(formatName(tableName)); - }); + node.getGrantObject().ifPresent(scope -> builder.append("ON ") + .append(formatGrantScope(scope))); return null; } @@ -2634,4 +2629,11 @@ private static void appendAliasColumns(Formatter.SqlBuilder builder, List privileges; + if (context.ALL() != null) { + privileges = null; + } + else { + privileges = extractPrivileges(context.privilegeOrRole()); + } + return new Grant( + getLocation(context), + privileges == null ? Optional.empty() : Optional.of(privileges), + createGrantObject(getLocation(context), context.grantObject()), + getPrincipalSpecification(context.principal()), + context.OPTION() != null); + } + @Override public Node visitGrantRoles(SqlBaseParser.GrantRolesContext context) { return new GrantRoles( getLocation(context), - ImmutableSet.copyOf(getIdentifiers(context.roles().identifier())), + extractRoles(context.privilegeOrRole()), ImmutableSet.copyOf(getPrincipalSpecifications(context.principal())), context.OPTION() != null, getGrantorSpecificationIfPresent(context.grantor()), visitIfPresent(context.catalog, Identifier.class)); } + @Override + public Node visitRevokePrivileges(SqlBaseParser.RevokePrivilegesContext context) + { + List privileges; + if (context.ALL() != null) { + privileges = null; + } + else { + privileges = extractPrivileges(context.privilegeOrRole()); + } + return new Revoke( + getLocation(context), + context.OPTION() != null, + privileges == null ? Optional.empty() : Optional.of(privileges), + createGrantObject(getLocation(context), context.grantObject()), + getPrincipalSpecification(context.principal())); + } + @Override public Node visitRevokeRoles(SqlBaseParser.RevokeRolesContext context) { return new RevokeRoles( getLocation(context), - ImmutableSet.copyOf(getIdentifiers(context.roles().identifier())), + ImmutableSet.copyOf(extractRoles(context.privilegeOrRole())), ImmutableSet.copyOf(getPrincipalSpecifications(context.principal())), context.OPTION() != null, getGrantorSpecificationIfPresent(context.grantor()), visitIfPresent(context.catalog, Identifier.class)); } + private List extractPrivileges(List privilegesOrRoles) + { + return privilegesOrRoles.stream().map(SqlBaseParser.PrivilegeOrRoleContext::getText).collect(toImmutableList()); + } + + private Set extractRoles(List privilegesOrRoles) + { + privilegesOrRoles.forEach(context -> { + if (isPrivilegeKeywordContext(context)) { + throw parseError("May not use a privilege name as a role name", context); + } + }); + return privilegesOrRoles.stream().map(context -> (Identifier) visit(context.identifier())).collect(toImmutableSet()); + } + + private boolean isPrivilegeKeywordContext(SqlBaseParser.PrivilegeOrRoleContext context) + { + return context.CREATE() != null || context.SELECT() != null || context.INSERT() != null || context.UPDATE() != null || context.DELETE() != null; + } + + private GrantObject createGrantObject(NodeLocation location, SqlBaseParser.GrantObjectContext context) + { + return new GrantObject( + location, + context.entityKind() == null ? Optional.empty() : Optional.of(context.entityKind().getText()), + getQualifiedName(context.qualifiedName())); + } + @Override public Node visitSetRole(SqlBaseParser.SetRoleContext context) { @@ -1602,39 +1668,6 @@ else if (context.NONE() != null) { visitIfPresent(context.catalog, Identifier.class)); } - @Override - public Node visitGrant(SqlBaseParser.GrantContext context) - { - Optional> privileges; - if (context.ALL() != null) { - privileges = Optional.empty(); - } - else { - privileges = Optional.of(context.privilege().stream() - .map(SqlBaseParser.PrivilegeContext::getText) - .collect(toList())); - } - - Optional type; - if (context.SCHEMA() != null) { - type = Optional.of(GrantOnType.SCHEMA); - } - else if (context.TABLE() != null) { - type = Optional.of(GrantOnType.TABLE); - } - else { - type = Optional.empty(); - } - - return new Grant( - getLocation(context), - privileges, - type, - getQualifiedName(context.qualifiedName()), - getPrincipalSpecification(context.grantee), - context.OPTION() != null); - } - @Override public Node visitDeny(SqlBaseParser.DenyContext context) { @@ -1647,72 +1680,20 @@ public Node visitDeny(SqlBaseParser.DenyContext context) .map(SqlBaseParser.PrivilegeContext::getText) .collect(toList())); } - - Optional type; - if (context.SCHEMA() != null) { - type = Optional.of(GrantOnType.SCHEMA); - } - else if (context.TABLE() != null) { - type = Optional.of(GrantOnType.TABLE); - } - else { - type = Optional.empty(); - } - return new Deny( getLocation(context), privileges, - type, - getQualifiedName(context.qualifiedName()), - getPrincipalSpecification(context.grantee)); - } - - @Override - public Node visitRevoke(SqlBaseParser.RevokeContext context) - { - Optional> privileges; - if (context.ALL() != null) { - privileges = Optional.empty(); - } - else { - privileges = Optional.of(context.privilege().stream() - .map(SqlBaseParser.PrivilegeContext::getText) - .collect(toList())); - } - - Optional type; - if (context.SCHEMA() != null) { - type = Optional.of(GrantOnType.SCHEMA); - } - else if (context.TABLE() != null) { - type = Optional.of(GrantOnType.TABLE); - } - else { - type = Optional.empty(); - } - - return new Revoke( - getLocation(context), - context.OPTION() != null, - privileges, - type, - getQualifiedName(context.qualifiedName()), + createGrantObject(getLocation(context), context.grantObject()), getPrincipalSpecification(context.grantee)); } @Override public Node visitShowGrants(SqlBaseParser.ShowGrantsContext context) { - Optional tableName = Optional.empty(); - - if (context.qualifiedName() != null) { - tableName = Optional.of(getQualifiedName(context.qualifiedName())); - } - return new ShowGrants( getLocation(context), - context.TABLE() != null, - tableName); + context.grantObject() == null || + context.grantObject().isEmpty() ? Optional.empty() : Optional.of(createGrantObject(getLocation(context), context.grantObject()))); } @Override @@ -4222,9 +4203,9 @@ private static QuantifiedComparisonExpression.Quantifier getComparisonQuantifier }; } - private List getIdentifiers(List identifiers) + private List getIdentifiers(SqlBaseParser.PrivilegeOrRoleContext identifiers) { - return identifiers.stream().map(context -> (Identifier) visit(context)).collect(toList()); + return identifiers.children.stream().map(context -> (Identifier) visit(context)).collect(toList()); } private List getPrincipalSpecifications(List principals) diff --git a/core/trino-parser/src/main/java/io/trino/sql/tree/Deny.java b/core/trino-parser/src/main/java/io/trino/sql/tree/Deny.java index d20f035bdee1d..91d395e069dfd 100644 --- a/core/trino-parser/src/main/java/io/trino/sql/tree/Deny.java +++ b/core/trino-parser/src/main/java/io/trino/sql/tree/Deny.java @@ -26,27 +26,25 @@ public class Deny extends Statement { private final Optional> privileges; // missing means ALL PRIVILEGES - private final Optional type; - private final QualifiedName name; + private final GrantObject grantObject; private final PrincipalSpecification grantee; - public Deny(Optional> privileges, Optional type, QualifiedName name, PrincipalSpecification grantee) + public Deny(Optional> privileges, GrantObject grantObject, PrincipalSpecification grantee) { - this(Optional.empty(), privileges, type, name, grantee); + this(Optional.empty(), privileges, grantObject, grantee); } - public Deny(NodeLocation location, Optional> privileges, Optional type, QualifiedName name, PrincipalSpecification grantee) + public Deny(NodeLocation location, Optional> privileges, GrantObject grantObject, PrincipalSpecification grantee) { - this(Optional.of(location), privileges, type, name, grantee); + this(Optional.of(location), privileges, grantObject, grantee); } - private Deny(Optional location, Optional> privileges, Optional type, QualifiedName name, PrincipalSpecification grantee) + private Deny(Optional location, Optional> privileges, GrantObject grantObject, PrincipalSpecification grantee) { super(location); requireNonNull(privileges, "privileges is null"); this.privileges = privileges.map(ImmutableList::copyOf); - this.type = requireNonNull(type, "type is null"); - this.name = requireNonNull(name, "name is null"); + this.grantObject = requireNonNull(grantObject, "grantScope is null"); this.grantee = requireNonNull(grantee, "grantee is null"); } @@ -55,14 +53,9 @@ public Optional> getPrivileges() return privileges; } - public Optional getType() + public GrantObject getGrantObject() { - return type; - } - - public QualifiedName getName() - { - return name; + return grantObject; } public PrincipalSpecification getGrantee() @@ -85,7 +78,7 @@ public List getChildren() @Override public int hashCode() { - return Objects.hash(privileges, type, name, grantee); + return Objects.hash(privileges, grantObject, grantee); } @Override @@ -99,8 +92,7 @@ public boolean equals(Object obj) } Deny o = (Deny) obj; return Objects.equals(privileges, o.privileges) && - Objects.equals(type, o.type) && - Objects.equals(name, o.name) && + Objects.equals(grantObject, o.grantObject) && Objects.equals(grantee, o.grantee); } @@ -109,8 +101,7 @@ public String toString() { return toStringHelper(this) .add("privileges", privileges) - .add("type", type) - .add("name", name) + .add("grantScope", grantObject) .add("grantee", grantee) .toString(); } diff --git a/core/trino-parser/src/main/java/io/trino/sql/tree/Grant.java b/core/trino-parser/src/main/java/io/trino/sql/tree/Grant.java index f61b29518f260..25bf2d8e88f56 100644 --- a/core/trino-parser/src/main/java/io/trino/sql/tree/Grant.java +++ b/core/trino-parser/src/main/java/io/trino/sql/tree/Grant.java @@ -26,28 +26,26 @@ public class Grant extends Statement { private final Optional> privileges; // missing means ALL PRIVILEGES - private final Optional type; - private final QualifiedName name; + private final GrantObject grantObject; private final PrincipalSpecification grantee; private final boolean grantOption; - public Grant(Optional> privileges, Optional type, QualifiedName name, PrincipalSpecification grantee, boolean grantOption) + public Grant(Optional> privileges, GrantObject grantObject, PrincipalSpecification grantee, boolean grantOption) { - this(Optional.empty(), privileges, type, name, grantee, grantOption); + this(Optional.empty(), privileges, grantObject, grantee, grantOption); } - public Grant(NodeLocation location, Optional> privileges, Optional type, QualifiedName name, PrincipalSpecification grantee, boolean grantOption) + public Grant(NodeLocation location, Optional> privileges, GrantObject grantObject, PrincipalSpecification grantee, boolean grantOption) { - this(Optional.of(location), privileges, type, name, grantee, grantOption); + this(Optional.of(location), privileges, grantObject, grantee, grantOption); } - private Grant(Optional location, Optional> privileges, Optional type, QualifiedName name, PrincipalSpecification grantee, boolean grantOption) + private Grant(Optional location, Optional> privileges, GrantObject grantObject, PrincipalSpecification grantee, boolean grantOption) { super(location); requireNonNull(privileges, "privileges is null"); this.privileges = privileges.map(ImmutableList::copyOf); - this.type = requireNonNull(type, "type is null"); - this.name = requireNonNull(name, "name is null"); + this.grantObject = requireNonNull(grantObject, "grantScope is null"); this.grantee = requireNonNull(grantee, "grantee is null"); this.grantOption = grantOption; } @@ -57,14 +55,9 @@ public Optional> getPrivileges() return privileges; } - public Optional getType() + public GrantObject getGrantObject() { - return type; - } - - public QualifiedName getName() - { - return name; + return grantObject; } public PrincipalSpecification getGrantee() @@ -92,7 +85,7 @@ public List getChildren() @Override public int hashCode() { - return Objects.hash(privileges, type, name, grantee, grantOption); + return Objects.hash(privileges, grantObject, grantee, grantOption); } @Override @@ -106,8 +99,7 @@ public boolean equals(Object obj) } Grant o = (Grant) obj; return Objects.equals(privileges, o.privileges) && - Objects.equals(type, o.type) && - Objects.equals(name, o.name) && + Objects.equals(grantObject, o.grantObject) && Objects.equals(grantee, o.grantee) && Objects.equals(grantOption, o.grantOption); } @@ -117,8 +109,7 @@ public String toString() { return toStringHelper(this) .add("privileges", privileges) - .add("type", type) - .add("name", name) + .add("grantScope", grantObject) .add("grantee", grantee) .add("grantOption", grantOption) .toString(); diff --git a/core/trino-parser/src/main/java/io/trino/sql/tree/GrantObject.java b/core/trino-parser/src/main/java/io/trino/sql/tree/GrantObject.java new file mode 100644 index 0000000000000..1b22d12c8f8ac --- /dev/null +++ b/core/trino-parser/src/main/java/io/trino/sql/tree/GrantObject.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.trino.sql.tree; + +import com.google.common.collect.ImmutableList; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +import static com.google.common.base.MoreObjects.toStringHelper; +import static java.util.Objects.requireNonNull; + +public class GrantObject + extends Node +{ + private final Optional entityKind; + private final QualifiedName name; + + public GrantObject(NodeLocation location, Optional entityKind, QualifiedName name) + { + super(Optional.of(location)); + this.entityKind = requireNonNull(entityKind, "entityKind is null"); + this.name = requireNonNull(name, "name is null"); + } + + public Optional getEntityKind() + { + return entityKind; + } + + public QualifiedName getName() + { + return name; + } + + @Override + public List getChildren() + { + return ImmutableList.of(); + } + + @Override + public boolean equals(Object o) + { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + GrantObject that = (GrantObject) o; + return Objects.equals(entityKind, that.entityKind) && Objects.equals(name, that.name); + } + + @Override + public String toString() + { + return toStringHelper(this) + .add("entityKind", entityKind) + .add("qualifiedName", name) + .toString(); + } + + @Override + public int hashCode() + { + return Objects.hash(entityKind, name); + } +} diff --git a/core/trino-parser/src/main/java/io/trino/sql/tree/Revoke.java b/core/trino-parser/src/main/java/io/trino/sql/tree/Revoke.java index f66652424a9fa..d78d1a3cca5bd 100644 --- a/core/trino-parser/src/main/java/io/trino/sql/tree/Revoke.java +++ b/core/trino-parser/src/main/java/io/trino/sql/tree/Revoke.java @@ -27,28 +27,26 @@ public class Revoke { private final boolean grantOptionFor; private final Optional> privileges; // missing means ALL PRIVILEGES - private final Optional type; - private final QualifiedName name; + private final GrantObject grantObject; private final PrincipalSpecification grantee; - public Revoke(boolean grantOptionFor, Optional> privileges, Optional type, QualifiedName name, PrincipalSpecification grantee) + public Revoke(boolean grantOptionFor, Optional> privileges, GrantObject grantObject, PrincipalSpecification grantee) { - this(Optional.empty(), grantOptionFor, privileges, type, name, grantee); + this(Optional.empty(), grantOptionFor, privileges, grantObject, grantee); } - public Revoke(NodeLocation location, boolean grantOptionFor, Optional> privileges, Optional type, QualifiedName name, PrincipalSpecification grantee) + public Revoke(NodeLocation location, boolean grantOptionFor, Optional> privileges, GrantObject grantObject, PrincipalSpecification grantee) { - this(Optional.of(location), grantOptionFor, privileges, type, name, grantee); + this(Optional.of(location), grantOptionFor, privileges, grantObject, grantee); } - private Revoke(Optional location, boolean grantOptionFor, Optional> privileges, Optional type, QualifiedName name, PrincipalSpecification grantee) + private Revoke(Optional location, boolean grantOptionFor, Optional> privileges, GrantObject grantObject, PrincipalSpecification grantee) { super(location); this.grantOptionFor = grantOptionFor; requireNonNull(privileges, "privileges is null"); this.privileges = privileges.map(ImmutableList::copyOf); - this.type = requireNonNull(type, "type is null"); - this.name = requireNonNull(name, "name is null"); + this.grantObject = requireNonNull(grantObject, "grantScope is null"); this.grantee = requireNonNull(grantee, "grantee is null"); } @@ -62,14 +60,9 @@ public Optional> getPrivileges() return privileges; } - public Optional getType() + public GrantObject getGrantObject() { - return type; - } - - public QualifiedName getName() - { - return name; + return grantObject; } public PrincipalSpecification getGrantee() @@ -92,7 +85,7 @@ public List getChildren() @Override public int hashCode() { - return Objects.hash(grantOptionFor, privileges, type, name, grantee); + return Objects.hash(grantOptionFor, privileges, grantObject, grantee); } @Override @@ -107,8 +100,7 @@ public boolean equals(Object obj) Revoke o = (Revoke) obj; return Objects.equals(grantOptionFor, o.grantOptionFor) && Objects.equals(privileges, o.privileges) && - Objects.equals(type, o.type) && - Objects.equals(name, o.name) && + Objects.equals(grantObject, o.grantObject) && Objects.equals(grantee, o.grantee); } @@ -118,8 +110,7 @@ public String toString() return toStringHelper(this) .add("grantOptionFor", grantOptionFor) .add("privileges", privileges) - .add("type", type) - .add("name", name) + .add("grantScope", grantObject) .add("grantee", grantee) .toString(); } diff --git a/core/trino-parser/src/main/java/io/trino/sql/tree/ShowGrants.java b/core/trino-parser/src/main/java/io/trino/sql/tree/ShowGrants.java index 8b32b78249edc..9f6ab63c94ee4 100644 --- a/core/trino-parser/src/main/java/io/trino/sql/tree/ShowGrants.java +++ b/core/trino-parser/src/main/java/io/trino/sql/tree/ShowGrants.java @@ -20,41 +20,36 @@ import java.util.Optional; import static com.google.common.base.MoreObjects.toStringHelper; -import static java.util.Objects.requireNonNull; public class ShowGrants extends Statement { - private final boolean table; - private final Optional tableName; + private final Optional grantObject; - public ShowGrants(boolean table, Optional tableName) + public ShowGrants(Optional grantObject) { - this(Optional.empty(), table, tableName); + this(Optional.empty(), grantObject); } - public ShowGrants(NodeLocation location, boolean table, Optional tableName) + public ShowGrants(NodeLocation location, Optional grantObject) { - this(Optional.of(location), table, tableName); + this(Optional.of(location), grantObject); } - public ShowGrants(Optional location, boolean table, Optional tableName) + public ShowGrants(Optional location, Optional grantObject) { super(location); - requireNonNull(tableName, "tableName is null"); - - this.table = table; - this.tableName = tableName; + this.grantObject = grantObject == null ? Optional.empty() : grantObject; } - public boolean getTable() + public Optional getEntityKind() { - return table; + return grantObject.flatMap(scope -> scope.getEntityKind()); } - public Optional getTableName() + public Optional getGrantObject() { - return tableName; + return grantObject; } @Override @@ -72,7 +67,7 @@ public List getChildren() @Override public int hashCode() { - return Objects.hash(table, tableName); + return grantObject.hashCode(); } @Override @@ -85,16 +80,14 @@ public boolean equals(Object obj) return false; } ShowGrants o = (ShowGrants) obj; - return Objects.equals(table, o.table) && - Objects.equals(tableName, o.tableName); + return Objects.equals(grantObject, o.grantObject); } @Override public String toString() { return toStringHelper(this) - .add("table", table) - .add("tableName", tableName) + .add("grantScope", grantObject) .toString(); } } diff --git a/core/trino-parser/src/test/java/io/trino/sql/parser/TestSqlParser.java b/core/trino-parser/src/test/java/io/trino/sql/parser/TestSqlParser.java index a1397bddb7e27..81c5e0615714f 100644 --- a/core/trino-parser/src/test/java/io/trino/sql/parser/TestSqlParser.java +++ b/core/trino-parser/src/test/java/io/trino/sql/parser/TestSqlParser.java @@ -81,7 +81,7 @@ import io.trino.sql.tree.GenericDataType; import io.trino.sql.tree.GenericLiteral; import io.trino.sql.tree.Grant; -import io.trino.sql.tree.GrantOnType; +import io.trino.sql.tree.GrantObject; import io.trino.sql.tree.GrantRoles; import io.trino.sql.tree.GrantorSpecification; import io.trino.sql.tree.GroupBy; @@ -3354,43 +3354,37 @@ public void testGrant() assertStatement("GRANT INSERT, DELETE ON t TO u", new Grant( Optional.of(ImmutableList.of("INSERT", "DELETE")), - Optional.empty(), - QualifiedName.of("t"), + new GrantObject(location(1, 1), Optional.empty(), QualifiedName.of("t")), new PrincipalSpecification(PrincipalSpecification.Type.UNSPECIFIED, new Identifier("u")), false)); assertStatement("GRANT UPDATE ON t TO u", new Grant( Optional.of(ImmutableList.of("UPDATE")), - Optional.empty(), - QualifiedName.of("t"), + new GrantObject(location(1, 1), Optional.empty(), QualifiedName.of("t")), new PrincipalSpecification(PrincipalSpecification.Type.UNSPECIFIED, new Identifier("u")), false)); assertStatement("GRANT SELECT ON t TO ROLE PUBLIC WITH GRANT OPTION", new Grant( Optional.of(ImmutableList.of("SELECT")), - Optional.empty(), - QualifiedName.of("t"), + new GrantObject(location(1, 1), Optional.empty(), QualifiedName.of("t")), new PrincipalSpecification(PrincipalSpecification.Type.ROLE, new Identifier("PUBLIC")), true)); assertStatement("GRANT ALL PRIVILEGES ON TABLE t TO USER u", new Grant( Optional.empty(), - Optional.of(GrantOnType.TABLE), - QualifiedName.of("t"), + new GrantObject(location(1, 1), Optional.of("TABLE"), QualifiedName.of("t")), new PrincipalSpecification(PrincipalSpecification.Type.USER, new Identifier("u")), false)); assertStatement("GRANT DELETE ON \"t\" TO ROLE \"public\" WITH GRANT OPTION", new Grant( Optional.of(ImmutableList.of("DELETE")), - Optional.empty(), - QualifiedName.of("t"), + new GrantObject(location(1, 1), Optional.empty(), QualifiedName.of("t")), new PrincipalSpecification(PrincipalSpecification.Type.ROLE, new Identifier("public")), true)); assertStatement("GRANT SELECT ON SCHEMA s TO USER u", new Grant( Optional.of(ImmutableList.of("SELECT")), - Optional.of(GrantOnType.SCHEMA), - QualifiedName.of("s"), + new GrantObject(location(1, 1), Optional.of("SCHEMA"), QualifiedName.of("s")), new PrincipalSpecification(PrincipalSpecification.Type.USER, new Identifier("u")), false)); } @@ -3401,26 +3395,22 @@ public void testDeny() assertStatement("DENY INSERT, DELETE ON t TO u", new Deny( Optional.of(ImmutableList.of("INSERT", "DELETE")), - Optional.empty(), - QualifiedName.of("t"), + new GrantObject(location(1, 1), Optional.empty(), QualifiedName.of("t")), new PrincipalSpecification(PrincipalSpecification.Type.UNSPECIFIED, new Identifier("u")))); assertStatement("DENY UPDATE ON t TO u", new Deny( Optional.of(ImmutableList.of("UPDATE")), - Optional.empty(), - QualifiedName.of("t"), + new GrantObject(location(1, 1), Optional.empty(), QualifiedName.of("t")), new PrincipalSpecification(PrincipalSpecification.Type.UNSPECIFIED, new Identifier("u")))); assertStatement("DENY ALL PRIVILEGES ON TABLE t TO USER u", new Deny( Optional.empty(), - Optional.of(GrantOnType.TABLE), - QualifiedName.of("t"), + new GrantObject(location(1, 1), Optional.of("TABLE"), QualifiedName.of("t")), new PrincipalSpecification(PrincipalSpecification.Type.USER, new Identifier("u")))); assertStatement("DENY SELECT ON SCHEMA s TO USER u", new Deny( Optional.of(ImmutableList.of("SELECT")), - Optional.of(GrantOnType.SCHEMA), - QualifiedName.of("s"), + new GrantObject(location(1, 1), Optional.of("SCHEMA"), QualifiedName.of("s")), new PrincipalSpecification(PrincipalSpecification.Type.USER, new Identifier("u")))); } @@ -3431,55 +3421,137 @@ public void testRevoke() new Revoke( false, Optional.of(ImmutableList.of("INSERT", "DELETE")), - Optional.empty(), - QualifiedName.of("t"), + new GrantObject(location(1, 1), Optional.empty(), QualifiedName.of("t")), new PrincipalSpecification(PrincipalSpecification.Type.UNSPECIFIED, new Identifier("u")))); assertStatement("REVOKE UPDATE ON t FROM u", new Revoke( false, Optional.of(ImmutableList.of("UPDATE")), - Optional.empty(), - QualifiedName.of("t"), + new GrantObject(location(1, 1), Optional.empty(), QualifiedName.of("t")), new PrincipalSpecification(PrincipalSpecification.Type.UNSPECIFIED, new Identifier("u")))); assertStatement("REVOKE GRANT OPTION FOR SELECT ON t FROM ROLE PUBLIC", new Revoke( true, Optional.of(ImmutableList.of("SELECT")), - Optional.empty(), - QualifiedName.of("t"), + new GrantObject(location(1, 1), Optional.empty(), QualifiedName.of("t")), new PrincipalSpecification(PrincipalSpecification.Type.ROLE, new Identifier("PUBLIC")))); assertStatement("REVOKE ALL PRIVILEGES ON TABLE t FROM USER u", new Revoke( false, Optional.empty(), - Optional.of(GrantOnType.TABLE), - QualifiedName.of("t"), + new GrantObject(location(1, 1), Optional.of("TABLE"), QualifiedName.of("t")), new PrincipalSpecification(PrincipalSpecification.Type.USER, new Identifier("u")))); assertStatement("REVOKE DELETE ON TABLE \"t\" FROM \"u\"", new Revoke( false, Optional.of(ImmutableList.of("DELETE")), - Optional.of(GrantOnType.TABLE), - QualifiedName.of("t"), + new GrantObject(location(1, 1), Optional.of("TABLE"), QualifiedName.of("t")), new PrincipalSpecification(PrincipalSpecification.Type.UNSPECIFIED, new Identifier("u")))); assertStatement("REVOKE SELECT ON SCHEMA s FROM USER u", new Revoke( false, Optional.of(ImmutableList.of("SELECT")), - Optional.of(GrantOnType.SCHEMA), - QualifiedName.of("s"), + new GrantObject(location(1, 1), Optional.of("SCHEMA"), QualifiedName.of("s")), new PrincipalSpecification(PrincipalSpecification.Type.USER, new Identifier("u")))); } + @Test + public void testExoticPrivilegesAndEntityKinds() + { + assertThat(statement("GRANT ALL PRIVILEGES ON FUNKY_ENTITY t TO u")) + .isEqualTo(new Grant( + location(1, 1), + Optional.empty(), + new GrantObject(location(1, 1), Optional.of("FUNKY_ENTITY"), QualifiedName.of(ImmutableList.of(new Identifier(location(1, 38), "t", false)))), + new PrincipalSpecification(Type.UNSPECIFIED, new Identifier(location(1, 43), "u", false)), + false)); + assertThat(statement("GRANT ALL PRIVILEGES ON FUNKY_ENTITY t TO u WITH GRANT OPTION")) + .isEqualTo(new Grant( + location(1, 1), + Optional.empty(), + new GrantObject(location(1, 1), Optional.of("FUNKY_ENTITY"), QualifiedName.of(ImmutableList.of(new Identifier(location(1, 38), "t", false)))), + new PrincipalSpecification(Type.UNSPECIFIED, new Identifier(location(1, 43), "u", false)), + true)); + assertThat(statement("GRANT AUTO_GYRATE ON FUNKY_ENTITY t TO u WITH GRANT OPTION")) + .isEqualTo(new Grant( + location(1, 1), + Optional.of(ImmutableList.of("AUTO_GYRATE")), + new GrantObject(location(1, 1), Optional.of("FUNKY_ENTITY"), QualifiedName.of(ImmutableList.of(new Identifier(location(1, 35), "t", false)))), + new PrincipalSpecification(Type.UNSPECIFIED, new Identifier(location(1, 40), "u", false)), + true)); + assertThat(statement("GRANT AUTO_GYRATE ON FUNKY_ENTITY t TO u")) + .isEqualTo(new Grant( + location(1, 1), + Optional.of(ImmutableList.of("AUTO_GYRATE")), + new GrantObject(location(1, 1), Optional.of("FUNKY_ENTITY"), QualifiedName.of(ImmutableList.of(new Identifier(location(1, 35), "t", false)))), + new PrincipalSpecification(Type.UNSPECIFIED, new Identifier(location(1, 40), "u", false)), + false)); + assertThat(statement("GRANT AUTO_GYRATE ON t TO u")) + .isEqualTo(new Grant( + location(1, 1), + Optional.of(ImmutableList.of("AUTO_GYRATE")), + new GrantObject(location(1, 1), Optional.empty(), QualifiedName.of(ImmutableList.of(new Identifier(location(1, 22), "t", false)))), + new PrincipalSpecification(Type.UNSPECIFIED, new Identifier(location(1, 27), "u", false)), + false)); + + assertThat(statement("REVOKE ALL PRIVILEGES ON FUNKY_ENTITY t FROM u")) + .isEqualTo(new Revoke( + location(1, 1), + false, + Optional.empty(), + new GrantObject(location(1, 1), Optional.of("FUNKY_ENTITY"), QualifiedName.of(ImmutableList.of(new Identifier(location(1, 39), "t", false)))), + new PrincipalSpecification(Type.UNSPECIFIED, new Identifier(location(1, 46), "u", false)))); + assertThat(statement("REVOKE AUTO_GYRATE ON FUNKY_ENTITY t FROM u")) + .isEqualTo(new Revoke( + location(1, 1), + false, + Optional.of(ImmutableList.of("AUTO_GYRATE")), + new GrantObject(location(1, 1), Optional.of("FUNKY_ENTITY"), QualifiedName.of(ImmutableList.of(new Identifier(location(1, 36), "t", false)))), + new PrincipalSpecification(Type.UNSPECIFIED, new Identifier(location(1, 43), "u", false)))); + assertThat(statement("REVOKE GRANT OPTION FOR AUTO_GYRATE ON FUNKY_ENTITY t FROM u")) + .isEqualTo(new Revoke( + location(1, 1), + true, + Optional.of(ImmutableList.of("AUTO_GYRATE")), + new GrantObject(location(1, 1), Optional.of("FUNKY_ENTITY"), QualifiedName.of(ImmutableList.of(new Identifier(location(1, 53), "t", false)))), + new PrincipalSpecification(Type.UNSPECIFIED, new Identifier(location(1, 60), "u", false)))); + assertThat(statement("REVOKE AUTO_GYRATE ON t FROM u")) + .isEqualTo(new Revoke( + location(1, 1), + false, + Optional.of(ImmutableList.of("AUTO_GYRATE")), + new GrantObject(location(1, 1), Optional.empty(), QualifiedName.of(ImmutableList.of(new Identifier(location(1, 23), "t", false)))), + new PrincipalSpecification(Type.UNSPECIFIED, new Identifier(location(1, 30), "u", false)))); + + assertThat(statement("DENY ALL PRIVILEGES ON FUNKY_ENTITY t TO u")) + .isEqualTo(new Deny( + location(1, 1), + Optional.empty(), + new GrantObject(location(1, 1), Optional.of("FUNKY_ENTITY"), QualifiedName.of(ImmutableList.of(new Identifier(location(1, 37), "t", false)))), + new PrincipalSpecification(Type.UNSPECIFIED, new Identifier(location(1, 42), "u", false)))); + assertThat(statement("DENY AUTO_GYRATE ON FUNKY_ENTITY t TO u")) + .isEqualTo(new Deny( + location(1, 1), + Optional.of(ImmutableList.of("AUTO_GYRATE")), + new GrantObject(location(1, 1), Optional.of("FUNKY_ENTITY"), QualifiedName.of(ImmutableList.of(new Identifier(location(1, 34), "t", false)))), + new PrincipalSpecification(Type.UNSPECIFIED, new Identifier(location(1, 39), "u", false)))); + assertThat(statement("DENY AUTO_GYRATE ON t TO u")) + .isEqualTo(new Deny( + location(1, 1), + Optional.of(ImmutableList.of("AUTO_GYRATE")), + new GrantObject(location(1, 1), Optional.empty(), QualifiedName.of(ImmutableList.of(new Identifier(location(1, 21), "t", false)))), + new PrincipalSpecification(Type.UNSPECIFIED, new Identifier(location(1, 26), "u", false)))); + } + @Test public void testShowGrants() { assertStatement("SHOW GRANTS ON TABLE t", - new ShowGrants(true, Optional.of(QualifiedName.of("t")))); + new ShowGrants(Optional.of(new GrantObject(location(1, 1), Optional.of("TABLE"), QualifiedName.of("t"))))); assertStatement("SHOW GRANTS ON t", - new ShowGrants(false, Optional.of(QualifiedName.of("t")))); + new ShowGrants(Optional.of(new GrantObject(location(1, 1), Optional.empty(), QualifiedName.of("t"))))); assertStatement("SHOW GRANTS", - new ShowGrants(false, Optional.empty())); + new ShowGrants(Optional.empty())); } @Test diff --git a/core/trino-spi/src/main/java/io/trino/spi/connector/EntityKindAndName.java b/core/trino-spi/src/main/java/io/trino/spi/connector/EntityKindAndName.java new file mode 100644 index 0000000000000..ac4e88b8c503d --- /dev/null +++ b/core/trino-spi/src/main/java/io/trino/spi/connector/EntityKindAndName.java @@ -0,0 +1,29 @@ +/* + * 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.trino.spi.connector; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.List; + +import static java.util.Objects.requireNonNull; + +public record EntityKindAndName(String entityKind, @JsonProperty List name) +{ + public EntityKindAndName + { + requireNonNull(entityKind, "entityKind is null"); + name = List.copyOf(name); + } +} diff --git a/core/trino-spi/src/main/java/io/trino/spi/connector/EntityPrivilege.java b/core/trino-spi/src/main/java/io/trino/spi/connector/EntityPrivilege.java new file mode 100644 index 0000000000000..1888b6cbc2590 --- /dev/null +++ b/core/trino-spi/src/main/java/io/trino/spi/connector/EntityPrivilege.java @@ -0,0 +1,24 @@ +/* + * 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.trino.spi.connector; + +import static java.util.Objects.requireNonNull; + +public record EntityPrivilege(String name) +{ + public EntityPrivilege + { + requireNonNull(name, "name is null"); + } +} diff --git a/core/trino-spi/src/main/java/io/trino/spi/security/AccessDeniedException.java b/core/trino-spi/src/main/java/io/trino/spi/security/AccessDeniedException.java index af010719ca4a5..d716f0909374b 100644 --- a/core/trino-spi/src/main/java/io/trino/spi/security/AccessDeniedException.java +++ b/core/trino-spi/src/main/java/io/trino/spi/security/AccessDeniedException.java @@ -14,6 +14,7 @@ package io.trino.spi.security; import io.trino.spi.TrinoException; +import io.trino.spi.connector.EntityKindAndName; import io.trino.spi.function.FunctionKind; import java.security.Principal; @@ -590,6 +591,46 @@ public static void denyRevokeTablePrivilege(String privilege, String tableName, throw new AccessDeniedException(format("Cannot revoke privilege %s on table %s%s", privilege, tableName, formatExtraInfo(extraInfo))); } + public static void denyGrantEntityPrivilege(String privilege, EntityKindAndName entity) + { + denyGrantEntityPrivilege(privilege, entity, null); + } + + public static void denyGrantEntityPrivilege(String privilege, EntityKindAndName entity, String extraInfo) + { + entityPrivilegeException("grant", privilege, entity, extraInfo); + } + + public static void denyDenyEntityPrivilege(String privilege, EntityKindAndName entity) + { + denyDenyEntityPrivilege(privilege, entity, null); + } + + public static void denyDenyEntityPrivilege(String privilege, EntityKindAndName entity, String extraInfo) + { + entityPrivilegeException("deny", privilege, entity, extraInfo); + } + + public static void denyRevokeEntityPrivilege(String privilege, EntityKindAndName entity) + { + denyRevokeEntityPrivilege(privilege, entity, null); + } + + public static void denyRevokeEntityPrivilege(String privilege, EntityKindAndName entity, String extraInfo) + { + entityPrivilegeException("revoke", privilege, entity, extraInfo); + } + + private static void entityPrivilegeException(String operation, String privilege, EntityKindAndName entity, String extraInfo) + { + throw new AccessDeniedException(format("Cannot %s privilege %s on %s %s%s", + operation, + privilege, + entity.entityKind().toLowerCase(Locale.ROOT), + entity.name(), + formatExtraInfo(extraInfo))); + } + public static void denyShowRoles() { throw new AccessDeniedException("Cannot show roles"); diff --git a/core/trino-spi/src/main/java/io/trino/spi/security/SystemAccessControl.java b/core/trino-spi/src/main/java/io/trino/spi/security/SystemAccessControl.java index d9ad77b5809a8..aea58858a2d4b 100644 --- a/core/trino-spi/src/main/java/io/trino/spi/security/SystemAccessControl.java +++ b/core/trino-spi/src/main/java/io/trino/spi/security/SystemAccessControl.java @@ -16,6 +16,8 @@ import io.trino.spi.connector.CatalogSchemaName; import io.trino.spi.connector.CatalogSchemaRoutineName; import io.trino.spi.connector.CatalogSchemaTableName; +import io.trino.spi.connector.EntityKindAndName; +import io.trino.spi.connector.EntityPrivilege; import io.trino.spi.connector.SchemaTableName; import io.trino.spi.eventlistener.EventListener; import io.trino.spi.function.SchemaFunctionName; @@ -43,6 +45,7 @@ import static io.trino.spi.security.AccessDeniedException.denyCreateView; import static io.trino.spi.security.AccessDeniedException.denyCreateViewWithSelect; import static io.trino.spi.security.AccessDeniedException.denyDeleteTable; +import static io.trino.spi.security.AccessDeniedException.denyDenyEntityPrivilege; import static io.trino.spi.security.AccessDeniedException.denyDenySchemaPrivilege; import static io.trino.spi.security.AccessDeniedException.denyDenyTablePrivilege; import static io.trino.spi.security.AccessDeniedException.denyDropCatalog; @@ -56,6 +59,7 @@ import static io.trino.spi.security.AccessDeniedException.denyExecuteProcedure; import static io.trino.spi.security.AccessDeniedException.denyExecuteQuery; import static io.trino.spi.security.AccessDeniedException.denyExecuteTableProcedure; +import static io.trino.spi.security.AccessDeniedException.denyGrantEntityPrivilege; import static io.trino.spi.security.AccessDeniedException.denyGrantRoles; import static io.trino.spi.security.AccessDeniedException.denyGrantSchemaPrivilege; import static io.trino.spi.security.AccessDeniedException.denyGrantTablePrivilege; @@ -68,6 +72,7 @@ import static io.trino.spi.security.AccessDeniedException.denyRenameMaterializedView; import static io.trino.spi.security.AccessDeniedException.denyRenameSchema; import static io.trino.spi.security.AccessDeniedException.denyRenameTable; +import static io.trino.spi.security.AccessDeniedException.denyRevokeEntityPrivilege; import static io.trino.spi.security.AccessDeniedException.denyRevokeRoles; import static io.trino.spi.security.AccessDeniedException.denyRevokeSchemaPrivilege; import static io.trino.spi.security.AccessDeniedException.denyRevokeTablePrivilege; @@ -709,6 +714,36 @@ default void checkCanRevokeTablePrivilege(SystemSecurityContext context, Privile denyRevokeTablePrivilege(privilege.toString(), table.toString()); } + /** + * Check if identity is allowed to grant the specified privilege to the grantee on the specified entity. + * + * @throws AccessDeniedException if not allowed + */ + default void checkCanGrantEntityPrivilege(SystemSecurityContext context, EntityPrivilege privilege, EntityKindAndName entity, TrinoPrincipal grantee, boolean grantOption) + { + denyGrantEntityPrivilege(privilege.toString(), entity); + } + + /** + * Check if identity is allowed to deny the specified privilege to the grantee on the specified entity. + * + * @throws AccessDeniedException if not allowed + */ + default void checkCanDenyEntityPrivilege(SystemSecurityContext context, EntityPrivilege privilege, EntityKindAndName entity, TrinoPrincipal grantee) + { + denyDenyEntityPrivilege(privilege.toString(), entity); + } + + /** + * Check if identity is allowed to revoke the specified privilege on the specified entity from the revokee. + * + * @throws AccessDeniedException if not allowed + */ + default void checkCanRevokeEntityPrivilege(SystemSecurityContext context, EntityPrivilege privilege, EntityKindAndName entity, TrinoPrincipal revokee, boolean grantOption) + { + denyRevokeEntityPrivilege(privilege.toString(), entity); + } + /** * Check if identity is allowed to show roles. * diff --git a/lib/trino-plugin-toolkit/src/main/java/io/trino/plugin/base/security/AllowAllSystemAccessControl.java b/lib/trino-plugin-toolkit/src/main/java/io/trino/plugin/base/security/AllowAllSystemAccessControl.java index 451d6b6dd1eaa..a6c685fd1db97 100644 --- a/lib/trino-plugin-toolkit/src/main/java/io/trino/plugin/base/security/AllowAllSystemAccessControl.java +++ b/lib/trino-plugin-toolkit/src/main/java/io/trino/plugin/base/security/AllowAllSystemAccessControl.java @@ -17,6 +17,8 @@ import io.trino.spi.connector.CatalogSchemaName; import io.trino.spi.connector.CatalogSchemaRoutineName; import io.trino.spi.connector.CatalogSchemaTableName; +import io.trino.spi.connector.EntityKindAndName; +import io.trino.spi.connector.EntityPrivilege; import io.trino.spi.connector.SchemaTableName; import io.trino.spi.eventlistener.EventListener; import io.trino.spi.function.SchemaFunctionName; @@ -370,6 +372,21 @@ public void checkCanRevokeTablePrivilege(SystemSecurityContext context, Privileg { } + @Override + public void checkCanGrantEntityPrivilege(SystemSecurityContext context, EntityPrivilege privilege, EntityKindAndName entity, TrinoPrincipal grantee, boolean grantOption) + { + } + + @Override + public void checkCanDenyEntityPrivilege(SystemSecurityContext context, EntityPrivilege privilege, EntityKindAndName entity, TrinoPrincipal grantee) + { + } + + @Override + public void checkCanRevokeEntityPrivilege(SystemSecurityContext context, EntityPrivilege privilege, EntityKindAndName entity, TrinoPrincipal revokee, boolean grantOption) + { + } + @Override public void checkCanShowRoles(SystemSecurityContext context) { diff --git a/lib/trino-plugin-toolkit/src/main/java/io/trino/plugin/base/security/FileBasedSystemAccessControl.java b/lib/trino-plugin-toolkit/src/main/java/io/trino/plugin/base/security/FileBasedSystemAccessControl.java index f3c18677a7cbe..b91e28d0a9349 100644 --- a/lib/trino-plugin-toolkit/src/main/java/io/trino/plugin/base/security/FileBasedSystemAccessControl.java +++ b/lib/trino-plugin-toolkit/src/main/java/io/trino/plugin/base/security/FileBasedSystemAccessControl.java @@ -23,6 +23,8 @@ import io.trino.spi.connector.CatalogSchemaName; import io.trino.spi.connector.CatalogSchemaRoutineName; import io.trino.spi.connector.CatalogSchemaTableName; +import io.trino.spi.connector.EntityKindAndName; +import io.trino.spi.connector.EntityPrivilege; import io.trino.spi.connector.SchemaTableName; import io.trino.spi.eventlistener.EventListener; import io.trino.spi.function.SchemaFunctionName; @@ -874,6 +876,24 @@ public void checkCanRevokeTablePrivilege(SystemSecurityContext context, Privileg } } + @Override + public void checkCanGrantEntityPrivilege(SystemSecurityContext context, EntityPrivilege privilege, EntityKindAndName entity, TrinoPrincipal grantee, boolean grantOption) + { + throw new UnsupportedOperationException(); + } + + @Override + public void checkCanDenyEntityPrivilege(SystemSecurityContext context, EntityPrivilege privilege, EntityKindAndName entity, TrinoPrincipal grantee) + { + throw new UnsupportedOperationException(); + } + + @Override + public void checkCanRevokeEntityPrivilege(SystemSecurityContext context, EntityPrivilege privilege, EntityKindAndName entity, TrinoPrincipal revokee, boolean grantOption) + { + throw new UnsupportedOperationException(); + } + @Override public void checkCanCreateRole(SystemSecurityContext context, String role, Optional grantor) { diff --git a/lib/trino-plugin-toolkit/src/main/java/io/trino/plugin/base/security/ForwardingSystemAccessControl.java b/lib/trino-plugin-toolkit/src/main/java/io/trino/plugin/base/security/ForwardingSystemAccessControl.java index ff281745ae233..43883643219e4 100644 --- a/lib/trino-plugin-toolkit/src/main/java/io/trino/plugin/base/security/ForwardingSystemAccessControl.java +++ b/lib/trino-plugin-toolkit/src/main/java/io/trino/plugin/base/security/ForwardingSystemAccessControl.java @@ -16,6 +16,8 @@ import io.trino.spi.connector.CatalogSchemaName; import io.trino.spi.connector.CatalogSchemaRoutineName; import io.trino.spi.connector.CatalogSchemaTableName; +import io.trino.spi.connector.EntityKindAndName; +import io.trino.spi.connector.EntityPrivilege; import io.trino.spi.connector.SchemaTableName; import io.trino.spi.eventlistener.EventListener; import io.trino.spi.function.SchemaFunctionName; @@ -427,6 +429,24 @@ public void checkCanRevokeTablePrivilege(SystemSecurityContext context, Privileg delegate().checkCanRevokeTablePrivilege(context, privilege, table, revokee, grantOption); } + @Override + public void checkCanGrantEntityPrivilege(SystemSecurityContext context, EntityPrivilege privilege, EntityKindAndName entity, TrinoPrincipal grantee, boolean grantOption) + { + delegate().checkCanGrantEntityPrivilege(context, privilege, entity, grantee, grantOption); + } + + @Override + public void checkCanDenyEntityPrivilege(SystemSecurityContext context, EntityPrivilege privilege, EntityKindAndName entity, TrinoPrincipal grantee) + { + delegate().checkCanDenyEntityPrivilege(context, privilege, entity, grantee); + } + + @Override + public void checkCanRevokeEntityPrivilege(SystemSecurityContext context, EntityPrivilege privilege, EntityKindAndName entity, TrinoPrincipal revokee, boolean grantOption) + { + delegate().checkCanRevokeEntityPrivilege(context, privilege, entity, revokee, grantOption); + } + @Override public void checkCanShowRoles(SystemSecurityContext context) {