Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import io.trino.metadata.ViewPropertyManager;
import io.trino.security.AccessControl;
import io.trino.spi.security.Identity;
import io.trino.spi.security.ViewSecurity;
import io.trino.sql.PlannerContext;
import io.trino.sql.analyzer.Analysis;
import io.trino.sql.analyzer.AnalyzerFactory;
Expand Down Expand Up @@ -92,8 +93,9 @@ public ListenableFuture<Void> execute(

Session session = stateMachine.getSession();
QualifiedObjectName name = createQualifiedObjectName(session, statement, statement.getName());
Optional<ViewSecurity> security = statement.getSecurity().map(sec -> ViewSecurity.valueOf(sec.name()));

accessControl.checkCanCreateView(session.toSecurityContext(), name);
accessControl.checkCanCreateView(session.toSecurityContext(), name, security);

if (metadata.isMaterializedView(session, name)) {
throw semanticException(TABLE_ALREADY_EXISTS, statement, "Materialized view already exists: '%s'", name);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import io.trino.spi.security.Privilege;
import io.trino.spi.security.TrinoPrincipal;
import io.trino.spi.security.ViewExpression;
import io.trino.spi.security.ViewSecurity;

import java.security.Principal;
import java.util.Collection;
Expand Down Expand Up @@ -313,7 +314,7 @@ public interface AccessControl
*
* @throws AccessDeniedException if not allowed
*/
void checkCanCreateView(SecurityContext context, QualifiedObjectName viewName);
void checkCanCreateView(SecurityContext context, QualifiedObjectName viewName, Optional<ViewSecurity> security);

/**
* Check if identity is allowed to rename the specified view.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
import io.trino.spi.security.SystemSecurityContext;
import io.trino.spi.security.TrinoPrincipal;
import io.trino.spi.security.ViewExpression;
import io.trino.spi.security.ViewSecurity;
import io.trino.transaction.TransactionId;
import io.trino.transaction.TransactionManager;
import jakarta.annotation.PreDestroy;
Expand Down Expand Up @@ -771,16 +772,16 @@ public void checkCanUpdateTableColumns(SecurityContext securityContext, Qualifie
}

@Override
public void checkCanCreateView(SecurityContext securityContext, QualifiedObjectName viewName)
public void checkCanCreateView(SecurityContext securityContext, QualifiedObjectName viewName, Optional<ViewSecurity> security)
{
requireNonNull(securityContext, "securityContext is null");
requireNonNull(viewName, "viewName is null");

checkCanAccessCatalog(securityContext, viewName.catalogName());

systemAuthorizationCheck(control -> control.checkCanCreateView(securityContext.toSystemSecurityContext(), viewName.asCatalogSchemaTableName()));
systemAuthorizationCheck(control -> control.checkCanCreateView(securityContext.toSystemSecurityContext(), viewName.asCatalogSchemaTableName(), security));

catalogAuthorizationCheck(viewName.catalogName(), securityContext, (control, context) -> control.checkCanCreateView(context, viewName.asSchemaTableName()));
catalogAuthorizationCheck(viewName.catalogName(), securityContext, (control, context) -> control.checkCanCreateView(context, viewName.asSchemaTableName(), security));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import io.trino.spi.security.Identity;
import io.trino.spi.security.Privilege;
import io.trino.spi.security.TrinoPrincipal;
import io.trino.spi.security.ViewSecurity;

import java.security.Principal;
import java.util.Collection;
Expand Down Expand Up @@ -161,7 +162,7 @@ public void checkCanTruncateTable(SecurityContext context, QualifiedObjectName t
public void checkCanUpdateTableColumns(SecurityContext context, QualifiedObjectName tableName, Set<String> updatedColumnNames) {}

@Override
public void checkCanCreateView(SecurityContext context, QualifiedObjectName viewName) {}
public void checkCanCreateView(SecurityContext context, QualifiedObjectName viewName, Optional<ViewSecurity> security) {}

@Override
public void checkCanRenameView(SecurityContext context, QualifiedObjectName viewName, QualifiedObjectName newViewName) {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import io.trino.spi.security.Identity;
import io.trino.spi.security.Privilege;
import io.trino.spi.security.TrinoPrincipal;
import io.trino.spi.security.ViewSecurity;

import java.security.Principal;
import java.util.Collection;
Expand Down Expand Up @@ -337,7 +338,7 @@ public void checkCanUpdateTableColumns(SecurityContext context, QualifiedObjectN
}

@Override
public void checkCanCreateView(SecurityContext context, QualifiedObjectName viewName)
public void checkCanCreateView(SecurityContext context, QualifiedObjectName viewName, Optional<ViewSecurity> security)
{
denyCreateView(viewName.toString());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import io.trino.spi.security.Privilege;
import io.trino.spi.security.TrinoPrincipal;
import io.trino.spi.security.ViewExpression;
import io.trino.spi.security.ViewSecurity;

import java.security.Principal;
import java.util.Collection;
Expand Down Expand Up @@ -279,9 +280,9 @@ public void checkCanUpdateTableColumns(SecurityContext context, QualifiedObjectN
}

@Override
public void checkCanCreateView(SecurityContext context, QualifiedObjectName viewName)
public void checkCanCreateView(SecurityContext context, QualifiedObjectName viewName, Optional<ViewSecurity> security)
{
delegate().checkCanCreateView(context, viewName);
delegate().checkCanCreateView(context, viewName, security);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import io.trino.spi.security.Privilege;
import io.trino.spi.security.TrinoPrincipal;
import io.trino.spi.security.ViewExpression;
import io.trino.spi.security.ViewSecurity;
import io.trino.spi.type.Type;

import java.util.List;
Expand Down Expand Up @@ -259,10 +260,10 @@ public void checkCanUpdateTableColumns(ConnectorSecurityContext context, SchemaT
}

@Override
public void checkCanCreateView(ConnectorSecurityContext context, SchemaTableName viewName)
public void checkCanCreateView(ConnectorSecurityContext context, SchemaTableName viewName, Optional<ViewSecurity> security)
{
checkArgument(context == null, "context must be null");
accessControl.checkCanCreateView(securityContext, getQualifiedObjectName(viewName));
accessControl.checkCanCreateView(securityContext, getQualifiedObjectName(viewName), security);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@
import io.trino.spi.security.GroupProvider;
import io.trino.spi.security.Identity;
import io.trino.spi.security.ViewExpression;
import io.trino.spi.security.ViewSecurity;
import io.trino.spi.type.ArrayType;
import io.trino.spi.type.CharType;
import io.trino.spi.type.DateType;
Expand Down Expand Up @@ -1056,6 +1057,7 @@ protected Scope visitCreateTableAsSelect(CreateTableAsSelect node, Optional<Scop
protected Scope visitCreateView(CreateView node, Optional<Scope> scope)
{
QualifiedObjectName viewName = createQualifiedObjectName(session, node, node.getName());
Optional<ViewSecurity> viewSecurity = node.getSecurity().map(security -> ViewSecurity.valueOf(security.name()));

node.getQuery().getFunctions().stream().findFirst().ifPresent(function -> {
throw semanticException(NOT_SUPPORTED, function, "Views cannot contain inline functions");
Expand All @@ -1066,7 +1068,7 @@ protected Scope visitCreateView(CreateView node, Optional<Scope> scope)

Scope queryScope = analyzer.analyze(node.getQuery());

accessControl.checkCanCreateView(session.toSecurityContext(), viewName);
accessControl.checkCanCreateView(session.toSecurityContext(), viewName, viewSecurity);

validateColumns(node, queryScope.getRelationType());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import io.trino.spi.connector.SchemaTableName;
import io.trino.spi.security.Identity;
import io.trino.spi.security.ViewExpression;
import io.trino.spi.security.ViewSecurity;
import io.trino.transaction.TransactionManager;

import java.security.Principal;
Expand Down Expand Up @@ -538,13 +539,13 @@ public void checkCanUpdateTableColumns(SecurityContext context, QualifiedObjectN
}

@Override
public void checkCanCreateView(SecurityContext context, QualifiedObjectName viewName)
public void checkCanCreateView(SecurityContext context, QualifiedObjectName viewName, Optional<ViewSecurity> security)
{
if (shouldDenyPrivilege(context.getIdentity().getUser(), viewName.objectName(), CREATE_VIEW)) {
denyCreateView(viewName.toString());
}
if (denyPrivileges.isEmpty()) {
super.checkCanCreateView(context, viewName);
super.checkCanCreateView(context, viewName, security);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import io.trino.spi.security.Privilege;
import io.trino.spi.security.TrinoPrincipal;
import io.trino.spi.security.ViewExpression;
import io.trino.spi.security.ViewSecurity;

import java.security.Principal;
import java.util.Collection;
Expand Down Expand Up @@ -396,11 +397,11 @@ public void checkCanUpdateTableColumns(SecurityContext context, QualifiedObjectN
}

@Override
public void checkCanCreateView(SecurityContext context, QualifiedObjectName viewName)
public void checkCanCreateView(SecurityContext context, QualifiedObjectName viewName, Optional<ViewSecurity> security)
{
Span span = startSpan("checkCanCreateView");
try (var _ = scopedSpan(span)) {
delegate.checkCanCreateView(context, viewName);
delegate.checkCanCreateView(context, viewName, security);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -555,7 +555,7 @@ public void testViewOperations()
SecurityContext bobContext = new SecurityContext(transactionId, bob, queryId, queryStart);
SecurityContext nonAsciiContext = new SecurityContext(transactionId, nonAsciiUser, queryId, queryStart);

accessControlManager.checkCanCreateView(aliceContext, aliceView);
accessControlManager.checkCanCreateView(aliceContext, aliceView, Optional.empty());
accessControlManager.checkCanDropView(aliceContext, aliceView);
accessControlManager.checkCanSelectFromColumns(aliceContext, aliceView, ImmutableSet.of());
accessControlManager.checkCanCreateViewWithSelectFromColumns(aliceContext, aliceTable, ImmutableSet.of());
Expand All @@ -564,7 +564,7 @@ public void testViewOperations()
accessControlManager.checkCanGrantTablePrivilege(aliceContext, SELECT, aliceTable, new TrinoPrincipal(USER, "grantee"), true);
accessControlManager.checkCanRevokeTablePrivilege(aliceContext, SELECT, aliceTable, new TrinoPrincipal(USER, "revokee"), true);

accessControlManager.checkCanCreateView(aliceContext, staffView);
accessControlManager.checkCanCreateView(aliceContext, staffView, Optional.empty());
accessControlManager.checkCanDropView(aliceContext, staffView);
accessControlManager.checkCanSelectFromColumns(aliceContext, staffView, ImmutableSet.of());
accessControlManager.checkCanCreateViewWithSelectFromColumns(aliceContext, staffTable, ImmutableSet.of());
Expand All @@ -573,7 +573,7 @@ public void testViewOperations()
accessControlManager.checkCanGrantTablePrivilege(aliceContext, SELECT, staffTable, new TrinoPrincipal(USER, "grantee"), true);
accessControlManager.checkCanRevokeTablePrivilege(aliceContext, SELECT, staffTable, new TrinoPrincipal(USER, "revokee"), true);

assertThatThrownBy(() -> accessControlManager.checkCanCreateView(bobContext, aliceView))
assertThatThrownBy(() -> accessControlManager.checkCanCreateView(bobContext, aliceView, Optional.empty()))
.isInstanceOf(AccessDeniedException.class)
.hasMessage("Access Denied: Cannot access catalog alice-catalog");
assertThatThrownBy(() -> accessControlManager.checkCanDropView(bobContext, aliceView))
Expand All @@ -598,7 +598,7 @@ public void testViewOperations()
.isInstanceOf(AccessDeniedException.class)
.hasMessage("Access Denied: Cannot access catalog alice-catalog");

accessControlManager.checkCanCreateView(bobContext, staffView);
accessControlManager.checkCanCreateView(bobContext, staffView, Optional.empty());
accessControlManager.checkCanDropView(bobContext, staffView);
accessControlManager.checkCanSelectFromColumns(bobContext, staffView, ImmutableSet.of());
accessControlManager.checkCanCreateViewWithSelectFromColumns(bobContext, staffTable, ImmutableSet.of());
Expand All @@ -607,7 +607,7 @@ public void testViewOperations()
accessControlManager.checkCanGrantTablePrivilege(bobContext, SELECT, staffTable, new TrinoPrincipal(USER, "grantee"), true);
accessControlManager.checkCanRevokeTablePrivilege(bobContext, SELECT, staffTable, new TrinoPrincipal(USER, "revokee"), true);

assertThatThrownBy(() -> accessControlManager.checkCanCreateView(nonAsciiContext, aliceView))
assertThatThrownBy(() -> accessControlManager.checkCanCreateView(nonAsciiContext, aliceView, Optional.empty()))
.isInstanceOf(AccessDeniedException.class)
.hasMessage("Access Denied: Cannot access catalog alice-catalog");
assertThatThrownBy(() -> accessControlManager.checkCanDropView(nonAsciiContext, aliceView))
Expand All @@ -632,7 +632,7 @@ public void testViewOperations()
.isInstanceOf(AccessDeniedException.class)
.hasMessage("Access Denied: Cannot access catalog alice-catalog");

assertThatThrownBy(() -> accessControlManager.checkCanCreateView(nonAsciiContext, staffView))
assertThatThrownBy(() -> accessControlManager.checkCanCreateView(nonAsciiContext, staffView, Optional.empty()))
.isInstanceOf(AccessDeniedException.class)
.hasMessage("Access Denied: Cannot access catalog staff-catalog");
assertThatThrownBy(() -> accessControlManager.checkCanDropView(nonAsciiContext, staffView))
Expand Down Expand Up @@ -675,7 +675,7 @@ public void testViewOperationsReadOnly()
});

assertThatThrownBy(() -> transaction(transactionManager, metadata, accessControlManager).execute(transactionId -> {
accessControlManager.checkCanCreateView(new SecurityContext(transactionId, alice, queryId, queryStart), aliceView);
accessControlManager.checkCanCreateView(new SecurityContext(transactionId, alice, queryId, queryStart), aliceView, Optional.empty());
})).isInstanceOf(AccessDeniedException.class)
.hasMessage("Access Denied: Cannot create view alice-catalog.schema.view");

Expand All @@ -695,7 +695,7 @@ public void testViewOperationsReadOnly()
.hasMessage("Access Denied: Cannot revoke privilege SELECT on table alice-catalog.schema.table");

assertThatThrownBy(() -> transaction(transactionManager, metadata, accessControlManager).execute(transactionId -> {
accessControlManager.checkCanCreateView(new SecurityContext(transactionId, bob, queryId, queryStart), aliceView);
accessControlManager.checkCanCreateView(new SecurityContext(transactionId, bob, queryId, queryStart), aliceView, Optional.empty());
})).isInstanceOf(AccessDeniedException.class)
.hasMessage("Access Denied: Cannot access catalog alice-catalog");
}
Expand Down Expand Up @@ -834,24 +834,24 @@ public void testRefreshing(@TempDir Path tempDir)

transaction(transactionManager, metadata, accessControlManager)
.execute(transactionId -> {
accessControlManager.checkCanCreateView(new SecurityContext(transactionId, alice, queryId, queryStart), aliceView);
accessControlManager.checkCanCreateView(new SecurityContext(transactionId, alice, queryId, queryStart), aliceView);
accessControlManager.checkCanCreateView(new SecurityContext(transactionId, alice, queryId, queryStart), aliceView);
accessControlManager.checkCanCreateView(new SecurityContext(transactionId, alice, queryId, queryStart), aliceView, Optional.empty());
accessControlManager.checkCanCreateView(new SecurityContext(transactionId, alice, queryId, queryStart), aliceView, Optional.empty());
accessControlManager.checkCanCreateView(new SecurityContext(transactionId, alice, queryId, queryStart), aliceView, Optional.empty());
});

Files.copy(getResourcePath("security-config-file-with-unknown-rules.json"), configFile, REPLACE_EXISTING);
sleep(2);

assertThatThrownBy(() -> transaction(transactionManager, metadata, accessControlManager)
.execute(transactionId -> {
accessControlManager.checkCanCreateView(new SecurityContext(transactionId, alice, queryId, queryStart), aliceView);
accessControlManager.checkCanCreateView(new SecurityContext(transactionId, alice, queryId, queryStart), aliceView, Optional.empty());
}))
.isInstanceOf(UncheckedIOException.class)
.hasMessageStartingWith("Failed to convert JSON tree node");
// test if file based cached control was not cached somewhere
assertThatThrownBy(() -> transaction(transactionManager, metadata, accessControlManager)
.execute(transactionId -> {
accessControlManager.checkCanCreateView(new SecurityContext(transactionId, alice, queryId, queryStart), aliceView);
accessControlManager.checkCanCreateView(new SecurityContext(transactionId, alice, queryId, queryStart), aliceView, Optional.empty());
}))
.isInstanceOf(UncheckedIOException.class)
.hasMessageStartingWith("Failed to convert JSON tree node");
Expand All @@ -861,7 +861,7 @@ public void testRefreshing(@TempDir Path tempDir)

transaction(transactionManager, metadata, accessControlManager)
.execute(transactionId -> {
accessControlManager.checkCanCreateView(new SecurityContext(transactionId, alice, queryId, queryStart), aliceView);
accessControlManager.checkCanCreateView(new SecurityContext(transactionId, alice, queryId, queryStart), aliceView, Optional.empty());
});
}

Expand Down
Loading
Loading