Skip to content

Commit

Permalink
Add system information checks to FileBasedSystemAccessControl
Browse files Browse the repository at this point in the history
  • Loading branch information
dain committed Jun 25, 2020
1 parent 7f27cd3 commit 9638664
Show file tree
Hide file tree
Showing 9 changed files with 281 additions and 2 deletions.
Expand Up @@ -81,8 +81,9 @@ The config file is specified in JSON format.
* The query rules specifying which queries can be managed by which user (see Query Rules below).
* The impersonation rules specify which user impersonations are allowed (see Impersonation Rules below).
* The principal rules specifying what principals can identify as what users (see Principal Rules below).
* The system information rules specifying what users can access system management information (see System Information Rules below).

This plugin currently supports catalog access, query, impersonation. and principal
This plugin currently supports catalog access, query, impersonation, principal, and system information
rules. If you want to limit access on a system level in any other way, you
must implement a custom SystemAccessControl plugin
(see :doc:`/develop/system-access-control`).
Expand Down Expand Up @@ -309,3 +310,24 @@ name, and allow ``alice`` and ``bob`` to use a group principal named as
}
]
}
.. _system_information_rules:

System Information Rules
------------------------

These rules specify which users can access the system information management interface.
The user is granted or denied access, based on the first matching rule read from top to
bottom. If no rules are specified, all access to system information is denied. If
no rule matches, system access is denied. Each rule is composed of the following fields:

* ``user`` (optional): regex to match against user name. If matched, it
will grant or deny the authorization based on the value of ``allow``.
* ``allow`` (required): set of access permissions granted to user. Values: ``read``, ``write``

For example, if you want to allow only the user ``admin`` to read and write
system information, allow ``alice`` to read system information, and deny all other access, you
can use the following rules:

.. literalinclude:: system-information-access.json
:language: json
@@ -0,0 +1,17 @@
{
"catalogs": [
{
"allow": true
}
],
"system_information": [
{
"user": "admin",
"allow": ["read", "write"]
},
{
"user": "alice",
"allow": ["read"]
}
]
}
Expand Up @@ -174,6 +174,31 @@ public void testCanSetUserOperations()
accessControlManagerNoPatterns.checkCanSetUser(kerberosValidAlice.getPrincipal(), kerberosValidAlice.getUser());
}

@Test
public void testSystemInformation()
{
TransactionManager transactionManager = createTestTransactionManager();
AccessControlManager accessControlManager = newAccessControlManager(transactionManager, "system_information.json");

accessControlManager.checkCanReadSystemInformation(admin);
accessControlManager.checkCanWriteSystemInformation(admin);

accessControlManager.checkCanReadSystemInformation(nonAsciiUser);
accessControlManager.checkCanWriteSystemInformation(nonAsciiUser);

accessControlManager.checkCanReadSystemInformation(admin);
assertThrows(AccessDeniedException.class, () -> transaction(transactionManager, accessControlManager).execute(transactionId -> {
accessControlManager.checkCanWriteSystemInformation(alice);
}));

assertThrows(AccessDeniedException.class, () -> transaction(transactionManager, accessControlManager).execute(transactionId -> {
accessControlManager.checkCanReadSystemInformation(bob);
}));
assertThrows(AccessDeniedException.class, () -> transaction(transactionManager, accessControlManager).execute(transactionId -> {
accessControlManager.checkCanWriteSystemInformation(bob);
}));
}

@Test
public void testCatalogOperations()
{
Expand Down
21 changes: 21 additions & 0 deletions presto-main/src/test/resources/system_information.json
@@ -0,0 +1,21 @@
{
"catalogs": [
{
"allow": true
}
],
"system_information": [
{
"user": "admin",
"allow": ["read", "write"]
},
{
"user": "alice",
"allow": ["read"]
},
{
"user": "\u0194\u0194\u0194",
"allow": ["read", "write"]
}
]
}
Expand Up @@ -71,6 +71,7 @@
import static io.prestosql.spi.security.AccessDeniedException.denyGrantTablePrivilege;
import static io.prestosql.spi.security.AccessDeniedException.denyImpersonateUser;
import static io.prestosql.spi.security.AccessDeniedException.denyInsertTable;
import static io.prestosql.spi.security.AccessDeniedException.denyReadSystemInformationAccess;
import static io.prestosql.spi.security.AccessDeniedException.denyRenameColumn;
import static io.prestosql.spi.security.AccessDeniedException.denyRenameSchema;
import static io.prestosql.spi.security.AccessDeniedException.denyRenameTable;
Expand All @@ -83,6 +84,7 @@
import static io.prestosql.spi.security.AccessDeniedException.denyShowCreateSchema;
import static io.prestosql.spi.security.AccessDeniedException.denyShowCreateTable;
import static io.prestosql.spi.security.AccessDeniedException.denyViewQuery;
import static io.prestosql.spi.security.AccessDeniedException.denyWriteSystemInformationAccess;
import static java.lang.String.format;
import static java.util.Objects.requireNonNull;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
Expand All @@ -99,6 +101,7 @@ public class FileBasedSystemAccessControl
private final Optional<List<QueryAccessRule>> queryAccessRules;
private final Optional<List<ImpersonationRule>> impersonationRules;
private final Optional<List<PrincipalUserMatchRule>> principalUserMatchRules;
private final Optional<List<SystemInformationRule>> systemInformationRules;
private final Optional<List<SchemaAccessControlRule>> schemaRules;
private final Optional<List<TableAccessControlRule>> tableRules;

Expand All @@ -107,13 +110,15 @@ private FileBasedSystemAccessControl(
Optional<List<QueryAccessRule>> queryAccessRules,
Optional<List<ImpersonationRule>> impersonationRules,
Optional<List<PrincipalUserMatchRule>> principalUserMatchRules,
Optional<List<SystemInformationRule>> systemInformationRules,
Optional<List<SchemaAccessControlRule>> schemaRules,
Optional<List<TableAccessControlRule>> tableRules)
{
this.catalogRules = catalogRules;
this.queryAccessRules = queryAccessRules;
this.impersonationRules = impersonationRules;
this.principalUserMatchRules = principalUserMatchRules;
this.systemInformationRules = systemInformationRules;
this.schemaRules = schemaRules;
this.tableRules = tableRules;
}
Expand Down Expand Up @@ -184,6 +189,7 @@ private SystemAccessControl create(String configFileName)
rules.getQueryAccessRules(),
rules.getImpersonationRules(),
rules.getPrincipalUserMatchRules(),
rules.getSystemInformationRules(),
rules.getSchemaRules(),
rules.getTableRules());
}
Expand Down Expand Up @@ -304,11 +310,28 @@ private boolean canAccessQuery(Identity identity, QueryAccessRule.AccessMode req
@Override
public void checkCanReadSystemInformation(SystemSecurityContext context)
{
if (!checkCanSystemInformation(context.getIdentity(), SystemInformationRule.AccessMode.READ)) {
denyReadSystemInformationAccess();
}
}

@Override
public void checkCanWriteSystemInformation(SystemSecurityContext context)
{
if (!checkCanSystemInformation(context.getIdentity(), SystemInformationRule.AccessMode.WRITE)) {
denyWriteSystemInformationAccess();
}
}

private boolean checkCanSystemInformation(Identity identity, SystemInformationRule.AccessMode requiredAccess)
{
for (SystemInformationRule rule : systemInformationRules.orElseGet(ImmutableList::of)) {
Optional<Set<SystemInformationRule.AccessMode>> accessMode = rule.match(identity.getUser());
if (accessMode.isPresent()) {
return accessMode.get().contains(requiredAccess);
}
}
return false;
}

@Override
Expand Down
Expand Up @@ -26,6 +26,7 @@ public class FileBasedSystemAccessControlRules
private final Optional<List<QueryAccessRule>> queryAccessRules;
private final Optional<List<ImpersonationRule>> impersonationRules;
private final Optional<List<PrincipalUserMatchRule>> principalUserMatchRules;
private final Optional<List<SystemInformationRule>> systemInformationRules;
private final Optional<List<SchemaAccessControlRule>> schemaRules;
private final Optional<List<TableAccessControlRule>> tableRules;

Expand All @@ -35,13 +36,15 @@ public FileBasedSystemAccessControlRules(
@JsonProperty("queries") Optional<List<QueryAccessRule>> queryAccessRules,
@JsonProperty("impersonation") Optional<List<ImpersonationRule>> impersonationRules,
@JsonProperty("principals") Optional<List<PrincipalUserMatchRule>> principalUserMatchRules,
@JsonProperty("system_information") Optional<List<SystemInformationRule>> systemInformationRules,
@JsonProperty("schemas") Optional<List<SchemaAccessControlRule>> schemaAccessControlRules,
@JsonProperty("tables") Optional<List<TableAccessControlRule>> tableAccessControlRules)
{
this.catalogRules = catalogRules.map(ImmutableList::copyOf).orElse(ImmutableList.of());
this.queryAccessRules = queryAccessRules.map(ImmutableList::copyOf);
this.principalUserMatchRules = principalUserMatchRules.map(ImmutableList::copyOf);
this.impersonationRules = impersonationRules.map(ImmutableList::copyOf);
this.systemInformationRules = systemInformationRules.map(ImmutableList::copyOf);
this.schemaRules = schemaAccessControlRules.map(ImmutableList::copyOf);
this.tableRules = tableAccessControlRules.map(ImmutableList::copyOf);
}
Expand All @@ -66,6 +69,11 @@ public Optional<List<PrincipalUserMatchRule>> getPrincipalUserMatchRules()
return principalUserMatchRules;
}

public Optional<List<SystemInformationRule>> getSystemInformationRules()
{
return systemInformationRules;
}

public Optional<List<SchemaAccessControlRule>> getSchemaRules()
{
return schemaRules;
Expand Down
@@ -0,0 +1,99 @@
/*
* 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 com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonValue;
import com.google.common.collect.ImmutableSet;

import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.regex.Pattern;

import static com.google.common.base.MoreObjects.toStringHelper;
import static com.google.common.collect.ImmutableMap.toImmutableMap;
import static java.util.Arrays.stream;
import static java.util.Objects.requireNonNull;
import static java.util.function.Function.identity;

public class SystemInformationRule
{
private final Set<AccessMode> allow;
private final Optional<Pattern> userRegex;

@JsonCreator
public SystemInformationRule(
@JsonProperty("allow") Set<AccessMode> allow,
@JsonProperty("user") Optional<Pattern> userRegex)
{
this.allow = ImmutableSet.copyOf(requireNonNull(allow, "allow is null"));
this.userRegex = requireNonNull(userRegex, "userRegex is null");
}

public Optional<Set<AccessMode>> match(String user)
{
if (userRegex.map(regex -> regex.matcher(user).matches()).orElse(true)) {
return Optional.of(allow);
}
return Optional.empty();
}

@Override
public String toString()
{
return toStringHelper(this)
.omitNullValues()
.add("allow", allow)
.add("userRegex", userRegex.orElse(null))
.toString();
}

public enum AccessMode
{
READ("read"),
WRITE("write");

private static final Map<String, AccessMode> modeByName = stream(values()).collect(toImmutableMap(AccessMode::toString, identity()));

private final String stringValue;

AccessMode(String stringValue)
{
this.stringValue = requireNonNull(stringValue, "stringValue is null");
}

@JsonValue
@Override
public String toString()
{
return stringValue;
}

@JsonCreator
public static AccessMode fromJson(Object value)
{
if (value instanceof String) {
AccessMode accessMode = modeByName.get(((String) value).toLowerCase(Locale.US));
if (accessMode != null) {
return accessMode;
}
}

throw new IllegalArgumentException("Unknown " + AccessMode.class.getSimpleName() + ": " + value);
}
}
}
Expand Up @@ -406,7 +406,7 @@ public void testQueryNotSet()
}

@Test
public void testDocsExample()
public void testQueryDocsExample()
{
String rulesFile = new File("../presto-docs/src/main/sphinx/security/query-access.json").getAbsolutePath();
SystemAccessControl accessControlManager = newFileBasedSystemAccessControl(ImmutableMap.of("security.config-file", rulesFile));
Expand All @@ -427,6 +427,49 @@ public void testDocsExample()
assertThrows(AccessDeniedException.class, () -> accessControlManager.checkCanKillQueryOwnedBy(new SystemSecurityContext(bob, queryId), "any"));
}

@Test
public void testSystemInformation()
{
SystemAccessControl accessControlManager = newFileBasedSystemAccessControl("system-information.json");

accessControlManager.checkCanReadSystemInformation(new SystemSecurityContext(admin, Optional.empty()));
accessControlManager.checkCanWriteSystemInformation(new SystemSecurityContext(admin, Optional.empty()));

accessControlManager.checkCanReadSystemInformation(new SystemSecurityContext(alice, Optional.empty()));
assertThrows(AccessDeniedException.class, () -> accessControlManager.checkCanWriteSystemInformation(new SystemSecurityContext(alice, Optional.empty())));

assertThrows(AccessDeniedException.class, () -> accessControlManager.checkCanReadSystemInformation(new SystemSecurityContext(bob, Optional.empty())));
assertThrows(AccessDeniedException.class, () -> accessControlManager.checkCanWriteSystemInformation(new SystemSecurityContext(bob, Optional.empty())));

accessControlManager.checkCanReadSystemInformation(new SystemSecurityContext(nonAsciiUser, Optional.empty()));
accessControlManager.checkCanWriteSystemInformation(new SystemSecurityContext(nonAsciiUser, Optional.empty()));
}

@Test
public void testSystemInformationNotSet()
{
SystemAccessControl accessControlManager = newFileBasedSystemAccessControl("catalog.json");

assertThrows(AccessDeniedException.class, () -> accessControlManager.checkCanReadSystemInformation(new SystemSecurityContext(bob, Optional.empty())));
assertThrows(AccessDeniedException.class, () -> accessControlManager.checkCanWriteSystemInformation(new SystemSecurityContext(bob, Optional.empty())));
}

@Test
public void testSystemInformationDocsExample()
{
String rulesFile = new File("../presto-docs/src/main/sphinx/security/system-information-access.json").getAbsolutePath();
SystemAccessControl accessControlManager = newFileBasedSystemAccessControl(ImmutableMap.of("security.config-file", rulesFile));

accessControlManager.checkCanReadSystemInformation(new SystemSecurityContext(admin, Optional.empty()));
accessControlManager.checkCanWriteSystemInformation(new SystemSecurityContext(admin, Optional.empty()));

accessControlManager.checkCanReadSystemInformation(new SystemSecurityContext(alice, Optional.empty()));
assertThrows(AccessDeniedException.class, () -> accessControlManager.checkCanWriteSystemInformation(new SystemSecurityContext(alice, Optional.empty())));

assertThrows(AccessDeniedException.class, () -> accessControlManager.checkCanReadSystemInformation(new SystemSecurityContext(bob, Optional.empty())));
assertThrows(AccessDeniedException.class, () -> accessControlManager.checkCanWriteSystemInformation(new SystemSecurityContext(bob, Optional.empty())));
}

@Test
public void testSchemaOperations()
{
Expand Down

0 comments on commit 9638664

Please sign in to comment.