Skip to content
Merged
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 @@ -856,6 +856,16 @@ public void checkCanGrantExecuteFunctionPrivilege(SecurityContext securityContex
functionName.asCatalogSchemaRoutineName(),
new TrinoPrincipal(PrincipalType.USER, grantee.getUser()),
grantOption));

catalogAuthorizationCheck(
functionName.getCatalogName(),
securityContext,
(control, context) -> control.checkCanGrantExecuteFunctionPrivilege(
context,
functionKind,
functionName.asSchemaRoutineName(),
new TrinoPrincipal(PrincipalType.USER, grantee.getUser()),
grantOption));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import io.trino.spi.connector.SchemaRoutineName;
import io.trino.spi.connector.SchemaTableName;
import io.trino.spi.function.FunctionKind;
import io.trino.spi.security.Identity;
import io.trino.spi.security.Privilege;
import io.trino.spi.security.TrinoPrincipal;
import io.trino.spi.security.ViewExpression;
Expand Down Expand Up @@ -310,6 +311,18 @@ public void checkCanRenameMaterializedView(ConnectorSecurityContext context, Sch
accessControl.checkCanRenameMaterializedView(securityContext, getQualifiedObjectName(viewName), getQualifiedObjectName(newViewName));
}

@Override
public void checkCanGrantExecuteFunctionPrivilege(ConnectorSecurityContext context, FunctionKind functionKind, SchemaRoutineName functionName, TrinoPrincipal grantee, boolean grantOption)
{
checkArgument(context == null, "context must be null");
accessControl.checkCanGrantExecuteFunctionPrivilege(
securityContext,
functionKind,
getQualifiedObjectName(functionName),
Identity.ofUser(grantee.getName()),
grantOption);
}

@Override
public void checkCanSetMaterializedViewProperties(ConnectorSecurityContext context, SchemaTableName materializedViewName, Map<String, Optional<Object>> properties)
{
Expand Down Expand Up @@ -486,6 +499,11 @@ private QualifiedObjectName getQualifiedObjectName(SchemaTableName schemaTableNa
return new QualifiedObjectName(catalogName, schemaTableName.getSchemaName(), schemaTableName.getTableName());
}

private QualifiedObjectName getQualifiedObjectName(SchemaRoutineName schemaRoutineName)
{
return new QualifiedObjectName(catalogName, schemaRoutineName.getSchemaName(), schemaRoutineName.getRoutineName());
}

private CatalogSchemaName getCatalogSchemaName(String schemaName)
{
return new CatalogSchemaName(catalogName, schemaName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
import java.util.Set;

import static io.trino.SessionTestUtils.TEST_SESSION;
import static io.trino.spi.function.FunctionKind.TABLE;
import static io.trino.spi.security.AccessDeniedException.denySelectTable;
import static io.trino.spi.type.BigintType.BIGINT;
import static io.trino.testing.TestingEventListenerManager.emptyEventListenerManager;
Expand Down Expand Up @@ -186,6 +187,29 @@ public void testDenyCatalogAccessControl()
}
}

@Test
public void testDenyTableFunctionCatalogAccessControl()
{
try (LocalQueryRunner queryRunner = LocalQueryRunner.create(TEST_SESSION)) {
TransactionManager transactionManager = queryRunner.getTransactionManager();
AccessControlManager accessControlManager = createAccessControlManager(transactionManager);

TestSystemAccessControlFactory accessControlFactory = new TestSystemAccessControlFactory("test");
accessControlManager.addSystemAccessControlFactory(accessControlFactory);
accessControlManager.loadSystemAccessControl("allow-all", ImmutableMap.of());

queryRunner.createCatalog(TEST_CATALOG_NAME, MockConnectorFactory.create(), ImmutableMap.of());
accessControlManager.setConnectorAccessControlProvider(CatalogServiceProvider.singleton(TEST_CATALOG_HANDLE, Optional.of(new DenyConnectorAccessControl())));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could you please verify that you have access before setting DenyConnectorAccessControl?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is checked in testAllowExecuteTableFunction test

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I'd prefer to have these tests duplicated, than not to have it here.
It would underline that just before setting DenyConnectorAccessControl it was possible to select from that connector. Actually that's @kokosing's style.


assertThatThrownBy(() -> transaction(transactionManager, accessControlManager)
.execute(transactionId -> {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May be { not needed?

What about testing checkCanExecuteFunction?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May be { not needed?

execute is overloaded and in-lining cause compilation error

What about testing checkCanExecuteFunction?

This is already checked in other tests

accessControlManager.checkCanGrantExecuteFunctionPrivilege(context(transactionId), TABLE, new QualifiedObjectName(TEST_CATALOG_NAME, "example_schema", "executed_function"), Identity.ofUser("bob"), true);
}))
.isInstanceOf(TrinoException.class)
.hasMessageMatching("Access Denied: 'user_name' cannot grant 'example_schema\\.executed_function' execution to user 'bob'");
}
}

@Test
public void testColumnMaskOrdering()
{
Expand Down Expand Up @@ -449,6 +473,20 @@ public void testAllowExecuteFunction()
});
}

@Test
public void testAllowExecuteTableFunction()
{
TransactionManager transactionManager = createTestTransactionManager();
AccessControlManager accessControlManager = createAccessControlManager(transactionManager);
accessControlManager.loadSystemAccessControl("allow-all", ImmutableMap.of());

transaction(transactionManager, accessControlManager)
.execute(transactionId -> {
accessControlManager.checkCanExecuteFunction(context(transactionId), TABLE, new QualifiedObjectName(TEST_CATALOG_NAME, "example_schema", "executed_function"));
accessControlManager.checkCanGrantExecuteFunctionPrivilege(context(transactionId), TABLE, new QualifiedObjectName(TEST_CATALOG_NAME, "example_schema", "executed_function"), Identity.ofUser("bob"), true);
});
}

private AccessControlManager createAccessControlManager(TestingEventListenerManager eventListenerManager, List<String> systemAccessControlProperties)
throws IOException
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,15 @@
package io.trino.spi.connector;

import io.trino.spi.function.FunctionKind;
import io.trino.spi.security.AccessDeniedException;
import io.trino.spi.security.Identity;
import io.trino.spi.security.Privilege;
import io.trino.spi.security.TrinoPrincipal;
import io.trino.spi.security.ViewExpression;
import io.trino.spi.type.Type;

import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
Expand All @@ -46,6 +49,7 @@
import static io.trino.spi.security.AccessDeniedException.denyExecuteFunction;
import static io.trino.spi.security.AccessDeniedException.denyExecuteProcedure;
import static io.trino.spi.security.AccessDeniedException.denyExecuteTableProcedure;
import static io.trino.spi.security.AccessDeniedException.denyGrantExecuteFunctionPrivilege;
import static io.trino.spi.security.AccessDeniedException.denyGrantRoles;
import static io.trino.spi.security.AccessDeniedException.denyGrantSchemaPrivilege;
import static io.trino.spi.security.AccessDeniedException.denyGrantTablePrivilege;
Expand Down Expand Up @@ -78,6 +82,7 @@
import static io.trino.spi.security.AccessDeniedException.denyShowTables;
import static io.trino.spi.security.AccessDeniedException.denyTruncateTable;
import static io.trino.spi.security.AccessDeniedException.denyUpdateTableColumns;
import static java.lang.String.format;
import static java.util.Collections.emptyList;
import static java.util.Collections.emptySet;

Expand Down Expand Up @@ -469,6 +474,17 @@ default void checkCanRenameMaterializedView(ConnectorSecurityContext context, Sc
denyRenameMaterializedView(viewName.toString(), newViewName.toString());
}

/**
* Check if identity is allowed to grant an access to the function execution to grantee.
*
* @throws AccessDeniedException if not allowed
*/
default void checkCanGrantExecuteFunctionPrivilege(ConnectorSecurityContext context, FunctionKind functionKind, SchemaRoutineName functionName, TrinoPrincipal grantee, boolean grantOption)
Comment thread
huberty89 marked this conversation as resolved.
Outdated
{
String granteeAsString = format("%s '%s'", grantee.getType().name().toLowerCase(Locale.ENGLISH), grantee.getName());
denyGrantExecuteFunctionPrivilege(functionName.toString(), Identity.ofUser(context.getIdentity().getUser()), granteeAsString);
}

/**
* Check if identity is allowed to set the specified property.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,14 @@ public void checkCanRenameMaterializedView(ConnectorSecurityContext context, Sch
}
}

@Override
public void checkCanGrantExecuteFunctionPrivilege(ConnectorSecurityContext context, FunctionKind functionKind, SchemaRoutineName functionName, TrinoPrincipal grantee, boolean grantOption)
{
try (ThreadContextClassLoader ignored = new ThreadContextClassLoader(classLoader)) {
delegate.checkCanGrantExecuteFunctionPrivilege(context, functionKind, functionName, grantee, grantOption);
}
}

@Override
public void checkCanSetMaterializedViewProperties(ConnectorSecurityContext context, SchemaTableName materializedViewName, Map<String, Optional<Object>> properties)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,11 @@ public void checkCanRenameMaterializedView(ConnectorSecurityContext context, Sch
{
}

@Override
public void checkCanGrantExecuteFunctionPrivilege(ConnectorSecurityContext context, FunctionKind functionKind, SchemaRoutineName functionName, TrinoPrincipal grantee, boolean grantOption)
{
}

@Override
public void checkCanSetMaterializedViewProperties(ConnectorSecurityContext context, SchemaTableName materializedViewName, Map<String, Optional<Object>> properties)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -468,6 +468,18 @@ public void checkCanRenameMaterializedView(ConnectorSecurityContext context, Sch
}
}

@Override
public void checkCanGrantExecuteFunctionPrivilege(ConnectorSecurityContext context, FunctionKind functionKind, SchemaRoutineName functionName, TrinoPrincipal grantee, boolean grantOption)
{
switch (functionKind) {
case SCALAR, AGGREGATE, WINDOW:
return;
case TABLE:
denyExecuteFunction(functionName.toString());
}
throw new UnsupportedOperationException("Unsupported function kind: " + functionKind);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am under impression that this throw is not needed. However, it leaves no question that is a dead code.

Or maybe you can use return switch here?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wanted to say the same, but the return value is void so it won't work.

}

@Override
public void checkCanSetMaterializedViewProperties(ConnectorSecurityContext context, SchemaTableName materializedViewName, Map<String, Optional<Object>> properties)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,12 @@ public void checkCanRenameMaterializedView(ConnectorSecurityContext context, Sch
delegate().checkCanRenameMaterializedView(context, viewName, newViewName);
}

@Override
public void checkCanGrantExecuteFunctionPrivilege(ConnectorSecurityContext context, FunctionKind functionKind, SchemaRoutineName functionName, TrinoPrincipal grantee, boolean grantOption)
{
delegate().checkCanGrantExecuteFunctionPrivilege(context, functionKind, functionName, grantee, grantOption);
}

@Override
public void checkCanSetMaterializedViewProperties(ConnectorSecurityContext context, SchemaTableName materializedViewName, Map<String, Optional<Object>> properties)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,11 @@ public void checkCanRenameMaterializedView(ConnectorSecurityContext context, Sch
{
}

@Override
public void checkCanGrantExecuteFunctionPrivilege(ConnectorSecurityContext context, FunctionKind functionKind, SchemaRoutineName functionName, TrinoPrincipal grantee, boolean grantOption)
{
}

@Override
public void checkCanSetMaterializedViewProperties(ConnectorSecurityContext context, SchemaTableName materializedViewName, Map<String, Optional<Object>> properties)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import io.trino.spi.function.FunctionKind;
import io.trino.spi.security.AccessDeniedException;
import io.trino.spi.security.ConnectorIdentity;
import io.trino.spi.security.Identity;
import io.trino.spi.security.Privilege;
import io.trino.spi.security.RoleGrant;
import io.trino.spi.security.TrinoPrincipal;
Expand Down Expand Up @@ -74,6 +75,7 @@
import static io.trino.spi.security.AccessDeniedException.denyDropView;
import static io.trino.spi.security.AccessDeniedException.denyExecuteFunction;
import static io.trino.spi.security.AccessDeniedException.denyExecuteTableProcedure;
import static io.trino.spi.security.AccessDeniedException.denyGrantExecuteFunctionPrivilege;
import static io.trino.spi.security.AccessDeniedException.denyGrantRoles;
import static io.trino.spi.security.AccessDeniedException.denyGrantTablePrivilege;
import static io.trino.spi.security.AccessDeniedException.denyInsertTable;
Expand Down Expand Up @@ -102,6 +104,8 @@
import static io.trino.spi.security.AccessDeniedException.denyUpdateTableColumns;
import static io.trino.spi.security.PrincipalType.ROLE;
import static io.trino.spi.security.PrincipalType.USER;
import static java.lang.String.format;
import static java.util.Locale.ENGLISH;
import static java.util.Objects.requireNonNull;
import static java.util.stream.Collectors.toSet;

Expand Down Expand Up @@ -416,6 +420,24 @@ public void checkCanRenameMaterializedView(ConnectorSecurityContext context, Sch
}
}

@Override
public void checkCanGrantExecuteFunctionPrivilege(ConnectorSecurityContext context, FunctionKind functionKind, SchemaRoutineName functionName, TrinoPrincipal grantee, boolean grantOption)
{
switch (functionKind) {
case SCALAR, AGGREGATE, WINDOW -> {
return;
}
case TABLE -> {
if (isAdmin(context)) {
return;
}
String granteeAsString = format("%s '%s'", grantee.getType().name().toLowerCase(ENGLISH), grantee.getName());
denyGrantExecuteFunctionPrivilege(functionName.toString(), Identity.ofUser(context.getIdentity().getUser()), granteeAsString);
}
}
throw new UnsupportedOperationException("Unsupported function kind: " + functionKind);
}

@Override
public void checkCanSetMaterializedViewProperties(ConnectorSecurityContext context, SchemaTableName materializedViewName, Map<String, Optional<Object>> properties)
{
Expand Down