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 96e9c1aa3d7c..1dd8c42fa110 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 @@ -17,6 +17,7 @@ import io.trino.Session; import io.trino.spi.TrinoException; import io.trino.spi.connector.CatalogSchemaName; +import io.trino.spi.connector.CatalogSchemaTableName; import io.trino.spi.security.GrantInfo; import io.trino.spi.security.Identity; import io.trino.spi.security.Privilege; @@ -28,7 +29,6 @@ import java.util.Set; import static io.trino.spi.StandardErrorCode.NOT_SUPPORTED; -import static java.lang.String.format; public class DisabledSystemSecurityMetadata implements SystemSecurityMetadata @@ -100,7 +100,7 @@ public void grantSchemaPrivileges( Set privileges, TrinoPrincipal grantee, boolean grantOption) { - throw new TrinoException(NOT_SUPPORTED, format("Catalog '%s' does not support permission management", schemaName.getCatalogName())); + throw notSupportedException(schemaName.getCatalogName()); } @Override @@ -110,19 +110,19 @@ public void revokeSchemaPrivileges( Set privileges, TrinoPrincipal grantee, boolean grantOption) { - throw new TrinoException(NOT_SUPPORTED, format("Catalog '%s' does not support permission management", schemaName.getCatalogName())); + throw notSupportedException(schemaName.getCatalogName()); } @Override public void grantTablePrivileges(Session session, QualifiedObjectName tableName, Set privileges, TrinoPrincipal grantee, boolean grantOption) { - throw new TrinoException(NOT_SUPPORTED, format("Catalog '%s' does not support permission management", tableName.getCatalogName())); + throw notSupportedException(tableName.getCatalogName()); } @Override public void revokeTablePrivileges(Session session, QualifiedObjectName tableName, Set privileges, TrinoPrincipal grantee, boolean grantOption) { - throw new TrinoException(NOT_SUPPORTED, format("Catalog '%s' does not support permission management", tableName.getCatalogName())); + throw notSupportedException(tableName.getCatalogName()); } @Override @@ -130,4 +130,39 @@ public Set listTablePrivileges(Session session, QualifiedTablePrefix { return ImmutableSet.of(); } + + @Override + public Optional getSchemaOwner(Session session, CatalogSchemaName schema) + { + return Optional.empty(); + } + + @Override + public void setSchemaOwner(Session session, CatalogSchemaName schema, TrinoPrincipal principal) + { + throw notSupportedException(schema.getCatalogName()); + } + + @Override + public void setTableOwner(Session session, CatalogSchemaTableName table, TrinoPrincipal principal) + { + throw notSupportedException(table.getCatalogName()); + } + + @Override + public Optional getViewRunAsIdentity(Session session, CatalogSchemaTableName view) + { + return Optional.empty(); + } + + @Override + public void setViewOwner(Session session, CatalogSchemaTableName view, TrinoPrincipal principal) + { + throw notSupportedException(view.getCatalogName()); + } + + private static TrinoException notSupportedException(String catalogName) + { + return new TrinoException(NOT_SUPPORTED, "Catalog does not support permission management: " + catalogName); + } } diff --git a/core/trino-main/src/main/java/io/trino/metadata/MaterializedViewDefinition.java b/core/trino-main/src/main/java/io/trino/metadata/MaterializedViewDefinition.java index 5a76bb6105d1..17e22773a91c 100644 --- a/core/trino-main/src/main/java/io/trino/metadata/MaterializedViewDefinition.java +++ b/core/trino-main/src/main/java/io/trino/metadata/MaterializedViewDefinition.java @@ -101,4 +101,18 @@ public String toString() .add("properties", properties) .toString(); } + + @Override + public MaterializedViewDefinition withOwner(Identity owner) + { + return new MaterializedViewDefinition( + getOriginalSql(), + getCatalog(), + getSchema(), + getColumns(), + getComment(), + owner, + storageTable, + properties); + } } 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 6777bf8106ef..196d46ce43df 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 @@ -805,7 +805,12 @@ public void setSchemaAuthorization(Session session, CatalogSchemaName source, Tr CatalogMetadata catalogMetadata = getCatalogMetadataForWrite(session, source.getCatalogName()); CatalogName catalogName = catalogMetadata.getCatalogName(); ConnectorMetadata metadata = catalogMetadata.getMetadata(); - metadata.setSchemaAuthorization(session.toConnectorSession(catalogName), source.getSchemaName(), principal); + if (catalogMetadata.getSecurityManagement() == SecurityManagement.SYSTEM) { + systemSecurityMetadata.setSchemaOwner(session, source, principal); + } + else { + metadata.setSchemaAuthorization(session.toConnectorSession(catalogName), source.getSchemaName(), principal); + } } @Override @@ -883,8 +888,14 @@ public void dropColumn(Session session, TableHandle tableHandle, ColumnHandle co public void setTableAuthorization(Session session, CatalogSchemaTableName table, TrinoPrincipal principal) { CatalogName catalogName = new CatalogName(table.getCatalogName()); - ConnectorMetadata metadata = getMetadataForWrite(session, catalogName); - metadata.setTableAuthorization(session.toConnectorSession(catalogName), table.getSchemaTableName(), principal); + CatalogMetadata catalogMetadata = getCatalogMetadataForWrite(session, catalogName); + ConnectorMetadata metadata = catalogMetadata.getMetadata(); + if (catalogMetadata.getSecurityManagement() == SecurityManagement.SYSTEM) { + systemSecurityMetadata.setTableOwner(session, table, principal); + } + else { + metadata.setTableAuthorization(session.toConnectorSession(catalogName), table.getSchemaTableName(), principal); + } } @Override @@ -1285,15 +1296,36 @@ public Optional getSchemaOwner(Session session, CatalogSchemaNam throw new TrinoException(SCHEMA_NOT_FOUND, format("Schema '%s' does not exist", schemaName)); } CatalogMetadata catalogMetadata = getCatalogMetadata(session, new CatalogName(schemaName.getCatalogName())); + if (catalogMetadata.getSecurityManagement() == SecurityManagement.SYSTEM) { + return systemSecurityMetadata.getSchemaOwner(session, schemaName); + } CatalogName catalogName = catalogMetadata.getConnectorIdForSchema(schemaName); ConnectorMetadata metadata = catalogMetadata.getMetadataFor(catalogName); - ConnectorSession connectorSession = session.toConnectorSession(catalogName); return metadata.getSchemaOwner(connectorSession, schemaName); } + @Override + public boolean isView(Session session, QualifiedObjectName viewName) + { + return getViewInternal(session, viewName).isPresent(); + } + @Override public Optional getView(Session session, QualifiedObjectName viewName) + { + Optional view = getViewInternal(session, viewName); + if (view.isEmpty() || view.get().isRunAsInvoker() || isCatalogManagedSecurity(session, viewName.getCatalogName())) { + return view; + } + Optional runAsIdentity = systemSecurityMetadata.getViewRunAsIdentity(session, viewName.asCatalogSchemaTableName()); + if (runAsIdentity.isEmpty()) { + return view; + } + return view.map(v -> v.withOwner(runAsIdentity.get())); + } + + private Optional getViewInternal(Session session, QualifiedObjectName viewName) { if (viewName.getCatalogName().isEmpty() || viewName.getSchemaName().isEmpty() || viewName.getObjectName().isEmpty()) { // View cannot exist @@ -1343,7 +1375,12 @@ public void setViewAuthorization(Session session, CatalogSchemaTableName view, T CatalogName catalogName = catalogMetadata.getCatalogName(); ConnectorMetadata metadata = catalogMetadata.getMetadata(); - metadata.setViewAuthorization(session.toConnectorSession(catalogName), view.getSchemaTableName(), principal); + if (catalogMetadata.getSecurityManagement() == SecurityManagement.SYSTEM) { + systemSecurityMetadata.setViewOwner(session, view, principal); + } + else { + metadata.setViewAuthorization(session.toConnectorSession(catalogName), view.getSchemaTableName(), principal); + } } @Override @@ -1447,8 +1484,27 @@ public Map getMaterializedViews(Session session, return ImmutableMap.copyOf(views); } + @Override + public boolean isMaterializedView(Session session, QualifiedObjectName viewName) + { + return getMaterializedViewInternal(session, viewName).isPresent(); + } + @Override public Optional getMaterializedView(Session session, QualifiedObjectName viewName) + { + Optional view = getMaterializedViewInternal(session, viewName); + if (view.isEmpty() || isCatalogManagedSecurity(session, viewName.getCatalogName())) { + return view; + } + Optional runAsIdentity = systemSecurityMetadata.getViewRunAsIdentity(session, viewName.asCatalogSchemaTableName()); + if (runAsIdentity.isEmpty()) { + return view; + } + return view.map(v -> v.withOwner(runAsIdentity.get())); + } + + private Optional getMaterializedViewInternal(Session session, QualifiedObjectName viewName) { if (viewName.getCatalogName().isEmpty() || viewName.getSchemaName().isEmpty() || viewName.getObjectName().isEmpty()) { // View cannot exist 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 0c230d0fd69c..d4e4c58df9b0 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 @@ -15,6 +15,7 @@ import io.trino.Session; import io.trino.spi.connector.CatalogSchemaName; +import io.trino.spi.connector.CatalogSchemaTableName; import io.trino.spi.security.GrantInfo; import io.trino.spi.security.Identity; import io.trino.spi.security.Privilege; @@ -108,4 +109,30 @@ public interface SystemSecurityMetadata * Gets the privileges for the specified table available to the given grantee considering the selected session role */ Set listTablePrivileges(Session session, QualifiedTablePrefix prefix); + + /** + * Set the owner of the specified schema + */ + Optional getSchemaOwner(Session session, CatalogSchemaName schema); + + /** + * Set the owner of the specified schema + */ + void setSchemaOwner(Session session, CatalogSchemaName schema, TrinoPrincipal principal); + + /** + * Set the owner of the specified table + */ + void setTableOwner(Session session, CatalogSchemaTableName table, TrinoPrincipal principal); + + /** + * Get the identity to run the view as + * @return + */ + Optional getViewRunAsIdentity(Session session, CatalogSchemaTableName viewName); + + /** + * Set the owner of the specified view + */ + void setViewOwner(Session session, CatalogSchemaTableName view, TrinoPrincipal principal); } diff --git a/core/trino-main/src/main/java/io/trino/metadata/ViewDefinition.java b/core/trino-main/src/main/java/io/trino/metadata/ViewDefinition.java index 4f47d73bc7a7..3c8fc05d887a 100644 --- a/core/trino-main/src/main/java/io/trino/metadata/ViewDefinition.java +++ b/core/trino-main/src/main/java/io/trino/metadata/ViewDefinition.java @@ -95,6 +95,11 @@ public Optional getComment() return comment; } + public boolean isRunAsInvoker() + { + return runAsIdentity.isEmpty(); + } + public Optional getRunAsIdentity() { return runAsIdentity; @@ -126,4 +131,15 @@ public String toString() .add("runAsIdentity", runAsIdentity.orElse(null)) .toString(); } + + public ViewDefinition withOwner(Identity owner) + { + return new ViewDefinition( + originalSql, + catalog, + schema, + columns, + comment, + Optional.of(owner)); + } } 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 91142bd64551..db6a07fbea5f 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 @@ -566,7 +566,7 @@ protected Node visitShowCreate(ShowCreate node, Void context) accessControl.checkCanShowCreateTable(session.toSecurityContext(), new QualifiedObjectName(catalogName.getValue(), schemaName.getValue(), tableName.getValue())); - CreateView.Security security = viewDefinition.get().getRunAsIdentity().isEmpty() ? INVOKER : DEFINER; + CreateView.Security security = viewDefinition.get().isRunAsInvoker() ? INVOKER : DEFINER; String sql = formatSql(new CreateView( QualifiedName.of(ImmutableList.of(catalogName, schemaName, tableName)), query, diff --git a/core/trino-main/src/test/java/io/trino/connector/MockConnector.java b/core/trino-main/src/test/java/io/trino/connector/MockConnector.java index 9ad27675f1f0..b92eb22e033e 100644 --- a/core/trino-main/src/test/java/io/trino/connector/MockConnector.java +++ b/core/trino-main/src/test/java/io/trino/connector/MockConnector.java @@ -24,6 +24,7 @@ import io.trino.spi.connector.ColumnHandle; import io.trino.spi.connector.ColumnMetadata; import io.trino.spi.connector.Connector; +import io.trino.spi.connector.ConnectorAccessControl; import io.trino.spi.connector.ConnectorInsertTableHandle; import io.trino.spi.connector.ConnectorMaterializedViewDefinition; import io.trino.spi.connector.ConnectorMetadata; @@ -119,7 +120,7 @@ public class MockConnector private final BiFunction getTableProperties; private final Supplier> eventListeners; private final MockConnectorFactory.ListRoleGrants roleGrants; - private final Optional accessControl; + private final ConnectorAccessControl accessControl; private final Function>> data; private final Set procedures; @@ -145,7 +146,7 @@ public class MockConnector BiFunction getTableProperties, Supplier> eventListeners, MockConnectorFactory.ListRoleGrants roleGrants, - Optional accessControl, + ConnectorAccessControl accessControl, Function>> data, Set procedures) { @@ -219,9 +220,9 @@ public Iterable getEventListeners() } @Override - public MockConnectorAccessControl getAccessControl() + public ConnectorAccessControl getAccessControl() { - return accessControl.orElseThrow(() -> new UnsupportedOperationException("Access control for mock connector is not set")); + return accessControl; } @Override @@ -596,25 +597,30 @@ public Set listEnabledRoles(ConnectorSession session) @Override public void grantSchemaPrivileges(ConnectorSession session, String schemaName, Set privileges, TrinoPrincipal grantee, boolean grantOption) { - getAccessControl().grantSchemaPrivileges(schemaName, privileges, grantee, grantOption); + getMockAccessControl().grantSchemaPrivileges(schemaName, privileges, grantee, grantOption); } @Override public void revokeSchemaPrivileges(ConnectorSession session, String schemaName, Set privileges, TrinoPrincipal revokee, boolean grantOption) { - getAccessControl().revokeSchemaPrivileges(schemaName, privileges, revokee, grantOption); + getMockAccessControl().revokeSchemaPrivileges(schemaName, privileges, revokee, grantOption); } @Override public void grantTablePrivileges(ConnectorSession session, SchemaTableName tableName, Set privileges, TrinoPrincipal grantee, boolean grantOption) { - getAccessControl().grantTablePrivileges(tableName, privileges, grantee, grantOption); + getMockAccessControl().grantTablePrivileges(tableName, privileges, grantee, grantOption); } @Override public void revokeTablePrivileges(ConnectorSession session, SchemaTableName tableName, Set privileges, TrinoPrincipal revokee, boolean grantOption) { - getAccessControl().revokeTablePrivileges(tableName, privileges, revokee, grantOption); + getMockAccessControl().revokeTablePrivileges(tableName, privileges, revokee, grantOption); + } + + private MockConnectorAccessControl getMockAccessControl() + { + return (MockConnectorAccessControl) accessControl; } } diff --git a/core/trino-main/src/test/java/io/trino/connector/MockConnectorFactory.java b/core/trino-main/src/test/java/io/trino/connector/MockConnectorFactory.java index ea32e666be58..4ae329a3e151 100644 --- a/core/trino-main/src/test/java/io/trino/connector/MockConnectorFactory.java +++ b/core/trino-main/src/test/java/io/trino/connector/MockConnectorFactory.java @@ -16,12 +16,14 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; +import io.trino.plugin.base.security.AllowAllAccessControl; import io.trino.spi.connector.AggregateFunction; import io.trino.spi.connector.AggregationApplicationResult; import io.trino.spi.connector.CatalogSchemaTableName; import io.trino.spi.connector.ColumnHandle; import io.trino.spi.connector.ColumnMetadata; import io.trino.spi.connector.Connector; +import io.trino.spi.connector.ConnectorAccessControl; import io.trino.spi.connector.ConnectorContext; import io.trino.spi.connector.ConnectorFactory; import io.trino.spi.connector.ConnectorHandleResolver; @@ -94,7 +96,7 @@ public class MockConnectorFactory // access control private final ListRoleGrants roleGrants; - private final Optional accessControl; + private final ConnectorAccessControl accessControl; private MockConnectorFactory( Function> listSchemaNames, @@ -120,7 +122,7 @@ private MockConnectorFactory( Function>> data, Set procedures, ListRoleGrants roleGrants, - Optional accessControl) + ConnectorAccessControl accessControl) { this.listSchemaNames = requireNonNull(listSchemaNames, "listSchemaNames is null"); this.listTables = requireNonNull(listTables, "listTables is null"); @@ -471,9 +473,9 @@ public Builder withColumnMask(BiFunction accessControl = Optional.empty(); + ConnectorAccessControl accessControl = new AllowAllAccessControl(); if (provideAccessControl) { - accessControl = Optional.of(new MockConnectorAccessControl(schemaGrants, tableGrants, rowFilter, columnMask)); + accessControl = new MockConnectorAccessControl(schemaGrants, tableGrants, rowFilter, columnMask); } return new MockConnectorFactory( listSchemaNames, diff --git a/testing/trino-tests/src/test/java/io/trino/security/TestingSystemSecurityMetadata.java b/testing/trino-tests/src/test/java/io/trino/security/TestingSystemSecurityMetadata.java index f4cf0e99285a..8e17fe2c42c1 100644 --- a/testing/trino-tests/src/test/java/io/trino/security/TestingSystemSecurityMetadata.java +++ b/testing/trino-tests/src/test/java/io/trino/security/TestingSystemSecurityMetadata.java @@ -19,6 +19,7 @@ import io.trino.metadata.QualifiedTablePrefix; import io.trino.metadata.SystemSecurityMetadata; import io.trino.spi.connector.CatalogSchemaName; +import io.trino.spi.connector.CatalogSchemaTableName; import io.trino.spi.security.GrantInfo; import io.trino.spi.security.Identity; import io.trino.spi.security.Privilege; @@ -193,4 +194,34 @@ public Set listTablePrivileges(Session session, QualifiedTablePrefix { throw new UnsupportedOperationException(); } + + @Override + public Optional getSchemaOwner(Session session, CatalogSchemaName schema) + { + return Optional.empty(); + } + + @Override + public void setSchemaOwner(Session session, CatalogSchemaName schema, TrinoPrincipal principal) + { + throw new UnsupportedOperationException(); + } + + @Override + public void setTableOwner(Session session, CatalogSchemaTableName table, TrinoPrincipal principal) + { + throw new UnsupportedOperationException(); + } + + @Override + public Optional getViewRunAsIdentity(Session session, CatalogSchemaTableName viewName) + { + return Optional.empty(); + } + + @Override + public void setViewOwner(Session session, CatalogSchemaTableName view, TrinoPrincipal principal) + { + throw new UnsupportedOperationException(); + } }