Skip to content

Commit d6e48b8

Browse files
committed
Add tables with authorization information to system.metadata #25907
Trino supports ALTER (TABLE | FUNCTION | SCHEMA) SET AUTHORIZATION for quite some time. However there is no way to retrieve this information. This commit fixes this by introducing system.metadata.(tables | schemas | functions)_authorization tables.
1 parent 6e94019 commit d6e48b8

File tree

18 files changed

+930
-3
lines changed

18 files changed

+930
-3
lines changed
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
/*
2+
* Licensed under the Apache License, Version 2.0 (the "License");
3+
* you may not use this file except in compliance with the License.
4+
* You may obtain a copy of the License at
5+
*
6+
* http://www.apache.org/licenses/LICENSE-2.0
7+
*
8+
* Unless required by applicable law or agreed to in writing, software
9+
* distributed under the License is distributed on an "AS IS" BASIS,
10+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
* See the License for the specific language governing permissions and
12+
* limitations under the License.
13+
*/
14+
package io.trino.connector.system;
15+
16+
import com.google.common.collect.ImmutableList;
17+
import com.google.inject.Inject;
18+
import io.trino.FullConnectorSession;
19+
import io.trino.Session;
20+
import io.trino.metadata.Metadata;
21+
import io.trino.metadata.QualifiedTablePrefix;
22+
import io.trino.security.AccessControl;
23+
import io.trino.spi.connector.CatalogSchemaName;
24+
import io.trino.spi.connector.ConnectorSession;
25+
import io.trino.spi.connector.ConnectorTableMetadata;
26+
import io.trino.spi.connector.ConnectorTransactionHandle;
27+
import io.trino.spi.connector.InMemoryRecordSet;
28+
import io.trino.spi.connector.RecordCursor;
29+
import io.trino.spi.connector.SchemaTableName;
30+
import io.trino.spi.connector.SystemTable;
31+
import io.trino.spi.function.SchemaFunctionName;
32+
import io.trino.spi.predicate.TupleDomain;
33+
import io.trino.spi.security.FunctionAuthorization;
34+
import io.trino.spi.security.TrinoPrincipal;
35+
36+
import java.util.List;
37+
import java.util.Set;
38+
39+
import static com.google.common.collect.ImmutableSet.toImmutableSet;
40+
import static io.trino.metadata.MetadataListing.handleListingException;
41+
import static io.trino.metadata.MetadataListing.listAllAvailableSchemas;
42+
import static io.trino.metadata.MetadataUtil.TableMetadataBuilder.tableMetadataBuilder;
43+
import static io.trino.spi.connector.SystemTable.Distribution.SINGLE_COORDINATOR;
44+
import static io.trino.spi.type.VarcharType.VARCHAR;
45+
import static io.trino.spi.type.VarcharType.createUnboundedVarcharType;
46+
import static java.util.Objects.requireNonNull;
47+
48+
public class FunctionsAuthorization
49+
implements SystemTable
50+
{
51+
public static final SchemaTableName FUNCTIONS_AUTHORIZATION_NAME = new SchemaTableName("metadata", "functions_authorization");
52+
53+
public static final ConnectorTableMetadata FUNCTIONS_AUTHORIZATION = tableMetadataBuilder(FUNCTIONS_AUTHORIZATION_NAME)
54+
.column("catalog", createUnboundedVarcharType())
55+
.column("schema", createUnboundedVarcharType())
56+
.column("name", createUnboundedVarcharType())
57+
.column("authorization_type", createUnboundedVarcharType())
58+
.column("authorization", createUnboundedVarcharType())
59+
.build();
60+
61+
private final Metadata metadata;
62+
private final AccessControl accessControl;
63+
64+
@Inject
65+
public FunctionsAuthorization(Metadata metadata, AccessControl accessControl)
66+
{
67+
this.metadata = requireNonNull(metadata, "metadata is null");
68+
this.accessControl = requireNonNull(accessControl, "accessControl is null");
69+
}
70+
71+
@Override
72+
public Distribution getDistribution()
73+
{
74+
return SINGLE_COORDINATOR;
75+
}
76+
77+
@Override
78+
public ConnectorTableMetadata getTableMetadata()
79+
{
80+
return FUNCTIONS_AUTHORIZATION;
81+
}
82+
83+
@Override
84+
public RecordCursor cursor(ConnectorTransactionHandle transactionHandle, ConnectorSession connectorSession, TupleDomain<Integer> constraint)
85+
{
86+
Session session = ((FullConnectorSession) connectorSession).getSession();
87+
InMemoryRecordSet.Builder table = InMemoryRecordSet.builder(FUNCTIONS_AUTHORIZATION);
88+
for (CatalogFunctionAuthorization functionAuthorization : getFunctionsAuthorization(session, constraint)) {
89+
SchemaFunctionName schemaFunctionName = functionAuthorization.functionAuthorization().schemaFunctionName();
90+
TrinoPrincipal trinoPrincipal = functionAuthorization.functionAuthorization().trinoPrincipal();
91+
table.addRow(
92+
functionAuthorization.catalog(),
93+
schemaFunctionName.getSchemaName(),
94+
schemaFunctionName.getFunctionName(),
95+
trinoPrincipal.getType().toString(),
96+
trinoPrincipal.getName());
97+
}
98+
return table.build().cursor();
99+
}
100+
101+
private List<CatalogFunctionAuthorization> getFunctionsAuthorization(Session session, TupleDomain<Integer> constraint)
102+
{
103+
try {
104+
return doGetFunctionsAuthorization(session, constraint);
105+
}
106+
catch (RuntimeException exception) {
107+
throw handleListingException(exception, "functions_authorization", "system");
108+
}
109+
}
110+
111+
private List<CatalogFunctionAuthorization> doGetFunctionsAuthorization(Session session, TupleDomain<Integer> constraint)
112+
{
113+
Set<CatalogSchemaName> availableSchemas = listAllAvailableSchemas(session, metadata, accessControl, constraint.getDomain(0, VARCHAR));
114+
115+
ImmutableList.Builder<CatalogFunctionAuthorization> result = ImmutableList.builder();
116+
availableSchemas.forEach(catalogSchemaName -> {
117+
Set<FunctionAuthorization> allFunctionsAuthorization = metadata.getAllFunctionsAuthorizationInfo(session, new QualifiedTablePrefix(catalogSchemaName.getCatalogName()));
118+
Set<String> accessibleFunctions = metadata.listFunctions(session, new CatalogSchemaName(catalogSchemaName.getCatalogName(), catalogSchemaName.getSchemaName())).stream()
119+
.flatMap(functionMetadata -> functionMetadata.getNames().stream())
120+
.collect(toImmutableSet());
121+
122+
allFunctionsAuthorization.stream()
123+
.filter(functionAuthorization -> accessibleFunctions.contains(functionAuthorization.schemaFunctionName().getFunctionName()))
124+
.map(functionAuthorization -> new CatalogFunctionAuthorization(catalogSchemaName.getCatalogName(), functionAuthorization))
125+
.forEach(result::add);
126+
});
127+
return result.build();
128+
}
129+
130+
private record CatalogFunctionAuthorization(String catalog, FunctionAuthorization functionAuthorization)
131+
{
132+
public CatalogFunctionAuthorization
133+
{
134+
requireNonNull(catalog, "catalog is null");
135+
requireNonNull(functionAuthorization, "functionAuthorization is null");
136+
}
137+
}
138+
}
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
/*
2+
* Licensed under the Apache License, Version 2.0 (the "License");
3+
* you may not use this file except in compliance with the License.
4+
* You may obtain a copy of the License at
5+
*
6+
* http://www.apache.org/licenses/LICENSE-2.0
7+
*
8+
* Unless required by applicable law or agreed to in writing, software
9+
* distributed under the License is distributed on an "AS IS" BASIS,
10+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
* See the License for the specific language governing permissions and
12+
* limitations under the License.
13+
*/
14+
package io.trino.connector.system;
15+
16+
import com.google.common.collect.ImmutableList;
17+
import com.google.inject.Inject;
18+
import io.trino.FullConnectorSession;
19+
import io.trino.Session;
20+
import io.trino.metadata.Metadata;
21+
import io.trino.metadata.QualifiedTablePrefix;
22+
import io.trino.security.AccessControl;
23+
import io.trino.spi.connector.CatalogSchemaName;
24+
import io.trino.spi.connector.ConnectorSession;
25+
import io.trino.spi.connector.ConnectorTableMetadata;
26+
import io.trino.spi.connector.ConnectorTransactionHandle;
27+
import io.trino.spi.connector.InMemoryRecordSet;
28+
import io.trino.spi.connector.RecordCursor;
29+
import io.trino.spi.connector.SchemaTableName;
30+
import io.trino.spi.connector.SystemTable;
31+
import io.trino.spi.predicate.TupleDomain;
32+
import io.trino.spi.security.SchemaAuthorization;
33+
import io.trino.spi.security.TrinoPrincipal;
34+
35+
import java.util.List;
36+
import java.util.Map;
37+
import java.util.Set;
38+
import java.util.stream.Collectors;
39+
40+
import static io.trino.metadata.MetadataListing.handleListingException;
41+
import static io.trino.metadata.MetadataListing.listAllAvailableSchemas;
42+
import static io.trino.metadata.MetadataUtil.TableMetadataBuilder.tableMetadataBuilder;
43+
import static io.trino.spi.connector.SystemTable.Distribution.SINGLE_COORDINATOR;
44+
import static io.trino.spi.type.VarcharType.VARCHAR;
45+
import static io.trino.spi.type.VarcharType.createUnboundedVarcharType;
46+
import static java.util.Objects.requireNonNull;
47+
import static java.util.stream.Collectors.groupingBy;
48+
49+
public class SchemasAuthorization
50+
implements SystemTable
51+
{
52+
public static final SchemaTableName SCHEMAS_AUTHORIZATION_NAME = new SchemaTableName("metadata", "schemas_authorization");
53+
54+
public static final ConnectorTableMetadata SCHEMAS_AUTHORIZATION = tableMetadataBuilder(SCHEMAS_AUTHORIZATION_NAME)
55+
.column("catalog", createUnboundedVarcharType())
56+
.column("schema", createUnboundedVarcharType())
57+
.column("authorization_type", createUnboundedVarcharType())
58+
.column("authorization", createUnboundedVarcharType())
59+
.build();
60+
61+
private final Metadata metadata;
62+
private final AccessControl accessControl;
63+
64+
@Inject
65+
public SchemasAuthorization(Metadata metadata, AccessControl accessControl)
66+
{
67+
this.metadata = requireNonNull(metadata, "metadata is null");
68+
this.accessControl = requireNonNull(accessControl, "accessControl is null");
69+
}
70+
71+
@Override
72+
public Distribution getDistribution()
73+
{
74+
return SINGLE_COORDINATOR;
75+
}
76+
77+
@Override
78+
public ConnectorTableMetadata getTableMetadata()
79+
{
80+
return SCHEMAS_AUTHORIZATION;
81+
}
82+
83+
@Override
84+
public RecordCursor cursor(ConnectorTransactionHandle transactionHandle, ConnectorSession connectorSession, TupleDomain<Integer> constraint)
85+
{
86+
Session session = ((FullConnectorSession) connectorSession).getSession();
87+
InMemoryRecordSet.Builder table = InMemoryRecordSet.builder(SCHEMAS_AUTHORIZATION);
88+
for (CatalogSchemaAuthorization catalogSchemaAuthorization : getSchemasAuthorization(session, constraint)) {
89+
TrinoPrincipal trinoPrincipal = catalogSchemaAuthorization.schemaAuthorization().trinoPrincipal();
90+
table.addRow(
91+
catalogSchemaAuthorization.catalog(),
92+
catalogSchemaAuthorization.schemaAuthorization().schemaName(),
93+
trinoPrincipal.getType().toString(),
94+
trinoPrincipal.getName());
95+
}
96+
return table.build().cursor();
97+
}
98+
99+
private List<CatalogSchemaAuthorization> getSchemasAuthorization(Session session, TupleDomain<Integer> constraint)
100+
{
101+
try {
102+
return doGetSchemasAuthorization(session, constraint);
103+
}
104+
catch (RuntimeException exception) {
105+
throw handleListingException(exception, "schemas_authorization", "system");
106+
}
107+
}
108+
109+
private List<CatalogSchemaAuthorization> doGetSchemasAuthorization(Session session, TupleDomain<Integer> constraint)
110+
{
111+
Set<CatalogSchemaName> availableSchemas = listAllAvailableSchemas(session, metadata, accessControl, constraint.getDomain(0, VARCHAR));
112+
Map<String, Set<CatalogSchemaName>> groupedByCatalog = availableSchemas.stream()
113+
.collect(groupingBy(CatalogSchemaName::getCatalogName, Collectors.toSet()));
114+
ImmutableList.Builder<CatalogSchemaAuthorization> result = ImmutableList.builder();
115+
for (String catalog : groupedByCatalog.keySet()) {
116+
Set<SchemaAuthorization> allSchemasAuthorization = metadata.getAllSchemasAuthorizationInfo(session, new QualifiedTablePrefix(catalog));
117+
allSchemasAuthorization.stream()
118+
.filter(schemaAuthorization -> groupedByCatalog.get(catalog).contains(new CatalogSchemaName(catalog, schemaAuthorization.schemaName())))
119+
.map(schemaAuthorization -> new CatalogSchemaAuthorization(catalog, schemaAuthorization))
120+
.forEach(result::add);
121+
}
122+
return result.build();
123+
}
124+
125+
private record CatalogSchemaAuthorization(String catalog, SchemaAuthorization schemaAuthorization)
126+
{
127+
public CatalogSchemaAuthorization
128+
{
129+
requireNonNull(catalog, "catalog is null");
130+
requireNonNull(schemaAuthorization, "schemaAuthorization is null");
131+
}
132+
}
133+
}

core/trino-main/src/main/java/io/trino/connector/system/SystemConnectorModule.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ public void configure(Binder binder)
4747
globalTableBinder.addBinding().to(QuerySystemTable.class).in(Scopes.SINGLETON);
4848
globalTableBinder.addBinding().to(TaskSystemTable.class).in(Scopes.SINGLETON);
4949
globalTableBinder.addBinding().to(CatalogSystemTable.class).in(Scopes.SINGLETON);
50+
globalTableBinder.addBinding().to(TablesAuthorization.class).in(Scopes.SINGLETON);
51+
globalTableBinder.addBinding().to(FunctionsAuthorization.class).in(Scopes.SINGLETON);
52+
globalTableBinder.addBinding().to(SchemasAuthorization.class).in(Scopes.SINGLETON);
5053
globalTableBinder.addBinding().to(TableCommentSystemTable.class).in(Scopes.SINGLETON);
5154
globalTableBinder.addBinding().to(SchemaPropertiesSystemTable.class).in(Scopes.SINGLETON);
5255
globalTableBinder.addBinding().to(TablePropertiesSystemTable.class).in(Scopes.SINGLETON);

0 commit comments

Comments
 (0)