Skip to content

Commit

Permalink
Only show schema if user is schema owner or has table permissions
Browse files Browse the repository at this point in the history
  • Loading branch information
dain committed Oct 7, 2020
1 parent 1f30202 commit d501016
Show file tree
Hide file tree
Showing 11 changed files with 436 additions and 8 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* 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.prestosql.plugin.base.security;

import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.regex.Pattern;

public class AnyCatalogSchemaPermissionsRule
{
private final Optional<Pattern> userRegex;
private final Optional<Pattern> groupRegex;
private final Optional<Pattern> catalogRegex;
private final Optional<Pattern> schemaRegex;

public AnyCatalogSchemaPermissionsRule(Optional<Pattern> userRegex, Optional<Pattern> groupRegex, Optional<Pattern> catalogRegex, Optional<Pattern> schemaRegex)
{
this.userRegex = userRegex;
this.groupRegex = groupRegex;
this.catalogRegex = catalogRegex;
this.schemaRegex = schemaRegex;
}

public boolean match(String user, Set<String> groups, String catalogName, String schemaName)
{
return userRegex.map(regex -> regex.matcher(user).matches()).orElse(true) &&
groupRegex.map(regex -> groups.stream().anyMatch(group -> regex.matcher(group).matches())).orElse(true) &&
catalogRegex.map(regex -> regex.matcher(catalogName).matches()).orElse(true) &&
schemaRegex.map(regex -> regex.matcher(schemaName).matches()).orElse(true);
}

@Override
public boolean equals(Object o)
{
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
AnyCatalogSchemaPermissionsRule that = (AnyCatalogSchemaPermissionsRule) o;
return patternEquals(userRegex, that.userRegex) &&
patternEquals(groupRegex, that.groupRegex) &&
patternEquals(catalogRegex, that.catalogRegex) &&
patternEquals(schemaRegex, that.schemaRegex);
}

private static boolean patternEquals(Optional<Pattern> left, Optional<Pattern> right)
{
if (left.isEmpty() || right.isEmpty()) {
return left.isEmpty() == right.isEmpty();
}
Pattern leftPattern = left.get();
Pattern rightPattern = right.get();
return leftPattern.pattern().equals(rightPattern.pattern()) && leftPattern.flags() == rightPattern.flags();
}

@Override
public int hashCode()
{
return Objects.hash(userRegex, groupRegex, catalogRegex, schemaRegex);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* 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.prestosql.plugin.base.security;

import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.regex.Pattern;

public class AnySchemaPermissionsRule
{
private final Optional<Pattern> userRegex;
private final Optional<Pattern> groupRegex;
private final Optional<Pattern> schemaRegex;

public AnySchemaPermissionsRule(Optional<Pattern> userRegex, Optional<Pattern> groupRegex, Optional<Pattern> schemaRegex)
{
this.userRegex = userRegex;
this.groupRegex = groupRegex;
this.schemaRegex = schemaRegex;
}

public boolean match(String user, Set<String> groups, String schemaName)
{
return userRegex.map(regex -> regex.matcher(user).matches()).orElse(true) &&
groupRegex.map(regex -> groups.stream().anyMatch(group -> regex.matcher(group).matches())).orElse(true) &&
schemaRegex.map(regex -> regex.matcher(schemaName).matches()).orElse(true);
}

@Override
public boolean equals(Object o)
{
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
AnySchemaPermissionsRule that = (AnySchemaPermissionsRule) o;
return patternEquals(userRegex, that.userRegex) &&
patternEquals(groupRegex, that.groupRegex) &&
patternEquals(schemaRegex, that.schemaRegex);
}

private static boolean patternEquals(Optional<Pattern> left, Optional<Pattern> right)
{
if (left.isEmpty() || right.isEmpty()) {
return left.isEmpty() == right.isEmpty();
}
Pattern leftPattern = left.get();
Pattern rightPattern = right.get();
return leftPattern.pattern().equals(rightPattern.pattern()) && leftPattern.flags() == rightPattern.flags();
}

@Override
public int hashCode()
{
return Objects.hash(userRegex, groupRegex, schemaRegex);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,16 @@ Optional<AnyCatalogPermissionsRule> toAnyCatalogPermissionsRule()
schemaAccessControlRule.getGroupRegex(),
catalogRegex));
}

Optional<AnyCatalogSchemaPermissionsRule> toAnyCatalogSchemaPermissionsRule()
{
if (!schemaAccessControlRule.isOwner()) {
return Optional.empty();
}
return Optional.of(new AnyCatalogSchemaPermissionsRule(
schemaAccessControlRule.getUserRegex(),
schemaAccessControlRule.getGroupRegex(),
catalogRegex,
schemaAccessControlRule.getSchemaRegex()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,16 @@ Optional<AnyCatalogPermissionsRule> toAnyCatalogPermissionsRule()
tableAccessControlRule.getGroupRegex(),
catalogRegex));
}

Optional<AnyCatalogSchemaPermissionsRule> toAnyCatalogSchemaPermissionsRule()
{
if (tableAccessControlRule.getPrivileges().isEmpty()) {
return Optional.empty();
}
return Optional.of(new AnyCatalogSchemaPermissionsRule(
tableAccessControlRule.getUserRegex(),
tableAccessControlRule.getGroupRegex(),
catalogRegex,
tableAccessControlRule.getSchemaRegex()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
package io.prestosql.plugin.base.security;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import io.prestosql.plugin.base.security.TableAccessControlRule.TablePrivilege;
import io.prestosql.spi.connector.ColumnMetadata;
import io.prestosql.spi.connector.ConnectorAccessControl;
Expand Down Expand Up @@ -66,6 +67,7 @@
import static io.prestosql.spi.security.AccessDeniedException.denyShowColumns;
import static io.prestosql.spi.security.AccessDeniedException.denyShowCreateSchema;
import static io.prestosql.spi.security.AccessDeniedException.denyShowCreateTable;
import static io.prestosql.spi.security.AccessDeniedException.denyShowTables;

public class FileBasedAccessControl
implements ConnectorAccessControl
Expand All @@ -75,6 +77,7 @@ public class FileBasedAccessControl
private final List<SchemaAccessControlRule> schemaRules;
private final List<TableAccessControlRule> tableRules;
private final List<SessionPropertyAccessControlRule> sessionPropertyRules;
private final Set<AnySchemaPermissionsRule> anySchemaPermissionsRules;

@Inject
public FileBasedAccessControl(FileBasedAccessControlConfig config)
Expand All @@ -84,6 +87,18 @@ public FileBasedAccessControl(FileBasedAccessControlConfig config)
this.schemaRules = rules.getSchemaRules();
this.tableRules = rules.getTableRules();
this.sessionPropertyRules = rules.getSessionPropertyRules();
ImmutableSet.Builder<AnySchemaPermissionsRule> anySchemaPermissionsRules = ImmutableSet.builder();
schemaRules.stream()
.map(SchemaAccessControlRule::toAnySchemaPermissionsRule)
.filter(Optional::isPresent)
.map(Optional::get)
.forEach(anySchemaPermissionsRules::add);
tableRules.stream()
.map(TableAccessControlRule::toAnySchemaPermissionsRule)
.filter(Optional::isPresent)
.map(Optional::get)
.forEach(anySchemaPermissionsRules::add);
this.anySchemaPermissionsRules = anySchemaPermissionsRules.build();
}

@Override
Expand Down Expand Up @@ -126,7 +141,9 @@ public void checkCanShowSchemas(ConnectorSecurityContext context)
@Override
public Set<String> filterSchemas(ConnectorSecurityContext context, Set<String> schemaNames)
{
return schemaNames;
return schemaNames.stream()
.filter(schemaName -> checkAnySchemaAccess(context, schemaName))
.collect(toImmutableSet());
}

@Override
Expand Down Expand Up @@ -165,6 +182,9 @@ public void checkCanDropTable(ConnectorSecurityContext context, SchemaTableName
@Override
public void checkCanShowTables(ConnectorSecurityContext context, String schemaName)
{
if (!checkAnySchemaAccess(context, schemaName)) {
denyShowTables(schemaName);
}
}

@Override
Expand Down Expand Up @@ -428,6 +448,12 @@ private boolean checkTablePermission(ConnectorSecurityContext context, SchemaTab
return false;
}

private boolean checkAnySchemaAccess(ConnectorSecurityContext context, String schemaName)
{
ConnectorIdentity identity = context.getIdentity();
return anySchemaPermissionsRules.stream().anyMatch(rule -> rule.match(identity.getUser(), identity.getGroups(), schemaName));
}

private boolean isSchemaOwner(ConnectorSecurityContext context, String schemaName)
{
ConnectorIdentity identity = context.getIdentity();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@
import static io.prestosql.spi.security.AccessDeniedException.denyShowCreateSchema;
import static io.prestosql.spi.security.AccessDeniedException.denyShowCreateTable;
import static io.prestosql.spi.security.AccessDeniedException.denyShowSchemas;
import static io.prestosql.spi.security.AccessDeniedException.denyShowTables;
import static io.prestosql.spi.security.AccessDeniedException.denyViewQuery;
import static io.prestosql.spi.security.AccessDeniedException.denyWriteSystemInformationAccess;
import static java.lang.String.format;
Expand All @@ -110,6 +111,7 @@ public class FileBasedSystemAccessControl
private final List<CatalogSchemaAccessControlRule> schemaRules;
private final List<CatalogTableAccessControlRule> tableRules;
private final Set<AnyCatalogPermissionsRule> anyCatalogPermissionsRules;
private final Set<AnyCatalogSchemaPermissionsRule> anyCatalogSchemaPermissionsRules;

private FileBasedSystemAccessControl(
List<CatalogAccessControlRule> catalogRules,
Expand Down Expand Up @@ -140,6 +142,19 @@ private FileBasedSystemAccessControl(
.map(Optional::get)
.forEach(anyCatalogPermissionsRules::add);
this.anyCatalogPermissionsRules = anyCatalogPermissionsRules.build();

ImmutableSet.Builder<AnyCatalogSchemaPermissionsRule> anyCatalogSchemaPermissionsRules = ImmutableSet.builder();
schemaRules.stream()
.map(CatalogSchemaAccessControlRule::toAnyCatalogSchemaPermissionsRule)
.filter(Optional::isPresent)
.map(Optional::get)
.forEach(anyCatalogSchemaPermissionsRules::add);
tableRules.stream()
.map(CatalogTableAccessControlRule::toAnyCatalogSchemaPermissionsRule)
.filter(Optional::isPresent)
.map(Optional::get)
.forEach(anyCatalogSchemaPermissionsRules::add);
this.anyCatalogSchemaPermissionsRules = anyCatalogSchemaPermissionsRules.build();
}

public static class Factory
Expand Down Expand Up @@ -425,11 +440,9 @@ public void checkCanShowSchemas(SystemSecurityContext context, String catalogNam
@Override
public Set<String> filterSchemas(SystemSecurityContext context, String catalogName, Set<String> schemaNames)
{
if (!canAccessCatalog(context, catalogName, READ_ONLY)) {
return ImmutableSet.of();
}

return schemaNames;
return schemaNames.stream()
.filter(schemaName -> checkAnySchemaAccess(context, catalogName, schemaName))
.collect(toImmutableSet());
}

@Override
Expand Down Expand Up @@ -493,6 +506,9 @@ public void checkCanSetColumnComment(SystemSecurityContext context, CatalogSchem
@Override
public void checkCanShowTables(SystemSecurityContext context, CatalogSchemaName schema)
{
if (!checkAnySchemaAccess(context, schema.getCatalogName(), schema.getSchemaName())) {
denyShowTables(schema.toString());
}
}

@Override
Expand Down Expand Up @@ -686,6 +702,13 @@ private boolean canAccessCatalog(SystemSecurityContext context, String catalogNa
return false;
}

private boolean checkAnySchemaAccess(SystemSecurityContext context, String catalogName, String schemaName)
{
Identity identity = context.getIdentity();
return canAccessCatalog(context, catalogName, READ_ONLY) &&
anyCatalogSchemaPermissionsRules.stream().anyMatch(rule -> rule.match(identity.getUser(), identity.getGroups(), catalogName, schemaName));
}

private boolean isSchemaOwner(SystemSecurityContext context, CatalogSchemaName schema)
{
if (!canAccessCatalog(context, schema.getCatalogName(), ALL)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,14 @@ public Optional<Boolean> match(String user, Set<String> groups, String schema)
return Optional.empty();
}

Optional<AnySchemaPermissionsRule> toAnySchemaPermissionsRule()
{
if (!owner) {
return Optional.empty();
}
return Optional.of(new AnySchemaPermissionsRule(userRegex, groupRegex, schemaRegex));
}

boolean isOwner()
{
return owner;
Expand All @@ -72,4 +80,9 @@ Optional<Pattern> getGroupRegex()
{
return groupRegex;
}

Optional<Pattern> getSchemaRegex()
{
return schemaRegex;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,14 @@ public Optional<Set<TablePrivilege>> match(String user, Set<String> groups, Sche
return Optional.empty();
}

Optional<AnySchemaPermissionsRule> toAnySchemaPermissionsRule()
{
if (privileges.isEmpty()) {
return Optional.empty();
}
return Optional.of(new AnySchemaPermissionsRule(userRegex, groupRegex, schemaRegex));
}

Set<TablePrivilege> getPrivileges()
{
return privileges;
Expand All @@ -80,6 +88,11 @@ Optional<Pattern> getGroupRegex()
return groupRegex;
}

Optional<Pattern> getSchemaRegex()
{
return schemaRegex;
}

public enum TablePrivilege
{
SELECT, INSERT, DELETE, OWNERSHIP, GRANT_SELECT
Expand Down
Loading

0 comments on commit d501016

Please sign in to comment.