add optional system schema authorization (#11720)

* add optional system schema authorization

* remove unused

* adjust docs

* doc fixes, missing ldap config change for integration tests

* style
This commit is contained in:
Clint Wylie 2021-09-21 13:28:26 -07:00 committed by GitHub
parent 3c487ff5b4
commit 5de26cf6d9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 579 additions and 600 deletions

View File

@ -1763,6 +1763,7 @@ The Druid SQL server is configured through the following properties on the Broke
|`druid.sql.planner.sqlTimeZone`|Sets the default time zone for the server, which will affect how time functions and timestamp literals behave. Should be a time zone name like "America/Los_Angeles" or offset like "-08:00".|UTC|
|`druid.sql.planner.metadataSegmentCacheEnable`|Whether to keep a cache of published segments in broker. If true, broker polls coordinator in background to get segments from metadata store and maintains a local cache. If false, coordinator's REST API will be invoked when broker needs published segments info.|false|
|`druid.sql.planner.metadataSegmentPollPeriod`|How often to poll coordinator for published segments list if `druid.sql.planner.metadataSegmentCacheEnable` is set to true. Poll period is in milliseconds. |60000|
|`druid.sql.planner.authorizeSystemTablesDirectly`|If true, Druid authorizes queries against any of the system schema tables (`sys` in SQL) as `SYSTEM_TABLE` resources which require `READ` access, in addition to permissions based content filtering.|false|
> Previous versions of Druid had properties named `druid.sql.planner.maxQueryCount` and `druid.sql.planner.maxSemiJoinRowsInMemory`.
> These properties are no longer available. Since Druid 0.18.0, you can use `druid.server.http.maxSubqueryRows` to control the maximum

View File

@ -29,11 +29,12 @@ This document describes the Druid security model that extensions use to enable u
At the center of the Druid user authentication and authorization model are _resources_ and _actions_. A resource is something that authenticated users are trying to access or modify. An action is something that users are trying to do.
There are three resource types:
Druid uses the following resource types:
* DATASOURCE – Each Druid table (i.e., `tables` in the `druid` schema in SQL) is a resource.
* CONFIG – Configuration resources exposed by the cluster components.
* STATE – Cluster-wide state resources.
* SYSTEM_TABLE – if `druid.sql.planner.authorizeSystemTablesDirectly` is enabled, then Druid authorizes system tables, the `sys` schema in SQL, using this resource type.
For specific resources associated with the types, see the endpoint list below and corresponding descriptions in [API Reference](./api-reference.md).

View File

@ -72,6 +72,7 @@ druid_coordinator_kill_supervisor_on=true
druid_coordinator_kill_supervisor_period=PT10S
druid_coordinator_kill_supervisor_durationToRetain=PT0M
druid_coordinator_period_metadataStoreManagementPeriod=PT10S
druid_sql_planner_authorizeSystemTablesDirectly=true
# Testing the legacy config from https://github.com/apache/druid/pull/10267
# Can remove this when the flag is no longer needed

View File

@ -136,3 +136,21 @@ homeDirectory: /home/druid
uidNumber: 7
gidNumber: 7
userPassword: helloworld
dn: uid=datasourceAndSysUser,ou=Users,dc=example,dc=org
uid: datasourceAndSysUser
cn: datasourceAndSysUser
sn: datasourceAndSysUser
objectClass: top
objectClass: posixAccount
objectClass: inetOrgPerson
homeDirectory: /home/datasourceAndSysUser
uidNumber: 8
gidNumber: 8
userPassword: helloworld
dn: cn=datasourceWithSysGroup,ou=Groups,dc=example,dc=org
objectClass: groupOfUniqueNames
cn: datasourceWithSysGroup
description: datasourceWithSysGroup users
uniqueMember: uid=datasourceAndSysUser,ou=Users,dc=example,dc=org

View File

@ -21,6 +21,7 @@ package org.apache.druid.tests.security;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.inject.Inject;
@ -34,6 +35,10 @@ import org.apache.druid.java.util.http.client.CredentialedHttpClient;
import org.apache.druid.java.util.http.client.HttpClient;
import org.apache.druid.java.util.http.client.auth.BasicCredentials;
import org.apache.druid.java.util.http.client.response.StatusResponseHolder;
import org.apache.druid.server.security.Action;
import org.apache.druid.server.security.Resource;
import org.apache.druid.server.security.ResourceAction;
import org.apache.druid.server.security.ResourceType;
import org.apache.druid.sql.avatica.DruidAvaticaJsonHandler;
import org.apache.druid.testing.IntegrationTestingConfig;
import org.apache.druid.testing.clients.CoordinatorResourceTestClient;
@ -43,6 +48,7 @@ import org.apache.druid.tests.indexer.AbstractIndexerTest;
import org.jboss.netty.handler.codec.http.HttpMethod;
import org.jboss.netty.handler.codec.http.HttpResponseStatus;
import org.testng.Assert;
import org.testng.annotations.Test;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
@ -50,19 +56,17 @@ import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.stream.Collectors;
public abstract class AbstractAuthConfigurationTest
{
private static final Logger LOG = new Logger(AbstractAuthConfigurationTest.class);
static final TypeReference<List<Map<String, Object>>> SYS_SCHEMA_RESULTS_TYPE_REFERENCE =
new TypeReference<List<Map<String, Object>>>()
{
};
private static final String INVALID_NAME = "invalid%2Fname";
static final String SYSTEM_SCHEMA_SEGMENTS_RESULTS_RESOURCE =
"/results/auth_test_sys_schema_segments.json";
@ -85,7 +89,84 @@ public abstract class AbstractAuthConfigurationTest
static final String SYS_SCHEMA_TASKS_QUERY =
"SELECT * FROM sys.tasks WHERE datasource IN ('auth_test')";
private static final String INVALID_NAME = "invalid%2Fname";
static final TypeReference<List<Map<String, Object>>> SYS_SCHEMA_RESULTS_TYPE_REFERENCE =
new TypeReference<List<Map<String, Object>>>()
{
};
/**
* create a ResourceAction set of permissions that can only read a 'auth_test' datasource, for Authorizer
* implementations which use ResourceAction pattern matching
*/
static final List<ResourceAction> DATASOURCE_ONLY_PERMISSIONS = Collections.singletonList(
new ResourceAction(
new Resource("auth_test", ResourceType.DATASOURCE),
Action.READ
)
);
/**
* create a ResourceAction set of permissions that can only read 'auth_test' + partial SYSTEM_TABLE, for Authorizer
* implementations which use ResourceAction pattern matching
*/
static final List<ResourceAction> DATASOURCE_SYS_PERMISSIONS = ImmutableList.of(
new ResourceAction(
new Resource("auth_test", ResourceType.DATASOURCE),
Action.READ
),
new ResourceAction(
new Resource("segments", ResourceType.SYSTEM_TABLE),
Action.READ
),
// test missing state permission but having servers permission
new ResourceAction(
new Resource("servers", ResourceType.SYSTEM_TABLE),
Action.READ
),
// test missing state permission but having server_segments permission
new ResourceAction(
new Resource("server_segments", ResourceType.SYSTEM_TABLE),
Action.READ
),
new ResourceAction(
new Resource("tasks", ResourceType.SYSTEM_TABLE),
Action.READ
)
);
/**
* create a ResourceAction set of permissions that can only read 'auth_test' + STATE + SYSTEM_TABLE read access, for
* Authorizer implementations which use ResourceAction pattern matching
*/
static final List<ResourceAction> DATASOURCE_SYS_STATE_PERMISSIONS = ImmutableList.of(
new ResourceAction(
new Resource("auth_test", ResourceType.DATASOURCE),
Action.READ
),
new ResourceAction(
new Resource(".*", ResourceType.SYSTEM_TABLE),
Action.READ
),
new ResourceAction(
new Resource(".*", ResourceType.STATE),
Action.READ
)
);
/**
* create a ResourceAction set of permissions with only STATE and SYSTEM_TABLE read access, for Authorizer
* implementations which use ResourceAction pattern matching
*/
static final List<ResourceAction> STATE_ONLY_PERMISSIONS = ImmutableList.of(
new ResourceAction(
new Resource(".*", ResourceType.STATE),
Action.READ
),
new ResourceAction(
new Resource(".*", ResourceType.SYSTEM_TABLE),
Action.READ
)
);
List<Map<String, Object>> adminSegments;
List<Map<String, Object>> adminTasks;
@ -107,11 +188,323 @@ public abstract class AbstractAuthConfigurationTest
HttpClient adminClient;
HttpClient datasourceOnlyUserClient;
HttpClient datasourceAndSysUserClient;
HttpClient datasourceWithStateUserClient;
HttpClient stateOnlyUserClient;
HttpClient internalSystemClient;
abstract void setupDatasourceOnlyUser() throws Exception;
abstract void setupDatasourceAndSysTableUser() throws Exception;
abstract void setupDatasourceAndSysAndStateUser() throws Exception;
abstract void setupSysTableAndStateOnlyUser() throws Exception;
abstract void setupTestSpecificHttpClients() throws Exception;
abstract String getAuthenticatorName();
abstract String getAuthorizerName();
abstract String getExpectedAvaticaAuthError();
@Test
public void test_systemSchemaAccess_admin() throws Exception
{
// check that admin access works on all nodes
checkNodeAccess(adminClient);
// as admin
LOG.info("Checking sys.segments query as admin...");
verifySystemSchemaQuery(
adminClient,
SYS_SCHEMA_SEGMENTS_QUERY,
adminSegments
);
LOG.info("Checking sys.servers query as admin...");
verifySystemSchemaServerQuery(
adminClient,
SYS_SCHEMA_SERVERS_QUERY,
getServersWithoutCurrentSize(adminServers)
);
LOG.info("Checking sys.server_segments query as admin...");
verifySystemSchemaQuery(
adminClient,
SYS_SCHEMA_SERVER_SEGMENTS_QUERY,
adminServerSegments
);
LOG.info("Checking sys.tasks query as admin...");
verifySystemSchemaQuery(
adminClient,
SYS_SCHEMA_TASKS_QUERY,
adminTasks
);
}
@Test
public void test_systemSchemaAccess_datasourceOnlyUser() throws Exception
{
// check that we can access a datasource-permission restricted resource on the broker
HttpUtil.makeRequest(
datasourceOnlyUserClient,
HttpMethod.GET,
config.getBrokerUrl() + "/druid/v2/datasources/auth_test",
null
);
// as user that can only read auth_test
LOG.info("Checking sys.segments query as datasourceOnlyUser...");
verifySystemSchemaQueryFailure(
datasourceOnlyUserClient,
SYS_SCHEMA_SEGMENTS_QUERY,
HttpResponseStatus.FORBIDDEN,
"{\"Access-Check-Result\":\"Allowed:false, Message:\"}"
);
LOG.info("Checking sys.servers query as datasourceOnlyUser...");
verifySystemSchemaQueryFailure(
datasourceOnlyUserClient,
SYS_SCHEMA_SERVERS_QUERY,
HttpResponseStatus.FORBIDDEN,
"{\"Access-Check-Result\":\"Allowed:false, Message:\"}"
);
LOG.info("Checking sys.server_segments query as datasourceOnlyUser...");
verifySystemSchemaQueryFailure(
datasourceOnlyUserClient,
SYS_SCHEMA_SERVER_SEGMENTS_QUERY,
HttpResponseStatus.FORBIDDEN,
"{\"Access-Check-Result\":\"Allowed:false, Message:\"}"
);
LOG.info("Checking sys.tasks query as datasourceOnlyUser...");
verifySystemSchemaQueryFailure(
datasourceOnlyUserClient,
SYS_SCHEMA_TASKS_QUERY,
HttpResponseStatus.FORBIDDEN,
"{\"Access-Check-Result\":\"Allowed:false, Message:\"}"
);
}
@Test
public void test_systemSchemaAccess_datasourceAndSysUser() throws Exception
{
// check that we can access a datasource-permission restricted resource on the broker
HttpUtil.makeRequest(
datasourceAndSysUserClient,
HttpMethod.GET,
config.getBrokerUrl() + "/druid/v2/datasources/auth_test",
null
);
// as user that can only read auth_test
LOG.info("Checking sys.segments query as datasourceAndSysUser...");
verifySystemSchemaQuery(
datasourceAndSysUserClient,
SYS_SCHEMA_SEGMENTS_QUERY,
adminSegments.stream()
.filter((segmentEntry) -> "auth_test".equals(segmentEntry.get("datasource")))
.collect(Collectors.toList())
);
LOG.info("Checking sys.servers query as datasourceAndSysUser...");
verifySystemSchemaQueryFailure(
datasourceAndSysUserClient,
SYS_SCHEMA_SERVERS_QUERY,
HttpResponseStatus.FORBIDDEN,
"{\"Access-Check-Result\":\"Insufficient permission to view servers : Allowed:false, Message:\"}"
);
LOG.info("Checking sys.server_segments query as datasourceAndSysUser...");
verifySystemSchemaQueryFailure(
datasourceAndSysUserClient,
SYS_SCHEMA_SERVER_SEGMENTS_QUERY,
HttpResponseStatus.FORBIDDEN,
"{\"Access-Check-Result\":\"Insufficient permission to view servers : Allowed:false, Message:\"}"
);
LOG.info("Checking sys.tasks query as datasourceAndSysUser...");
verifySystemSchemaQuery(
datasourceAndSysUserClient,
SYS_SCHEMA_TASKS_QUERY,
adminTasks.stream()
.filter((taskEntry) -> "auth_test".equals(taskEntry.get("datasource")))
.collect(Collectors.toList())
);
}
@Test
public void test_systemSchemaAccess_datasourceAndSysWithStateUser() throws Exception
{
// check that we can access a state-permission restricted resource on the broker
HttpUtil.makeRequest(
datasourceWithStateUserClient,
HttpMethod.GET,
config.getBrokerUrl() + "/status",
null
);
// as user that can read auth_test and STATE
LOG.info("Checking sys.segments query as datasourceWithStateUser...");
verifySystemSchemaQuery(
datasourceWithStateUserClient,
SYS_SCHEMA_SEGMENTS_QUERY,
adminSegments.stream()
.filter((segmentEntry) -> "auth_test".equals(segmentEntry.get("datasource")))
.collect(Collectors.toList())
);
LOG.info("Checking sys.servers query as datasourceWithStateUser...");
verifySystemSchemaServerQuery(
datasourceWithStateUserClient,
SYS_SCHEMA_SERVERS_QUERY,
adminServers
);
LOG.info("Checking sys.server_segments query as datasourceWithStateUser...");
verifySystemSchemaQuery(
datasourceWithStateUserClient,
SYS_SCHEMA_SERVER_SEGMENTS_QUERY,
adminServerSegments.stream()
.filter((serverSegmentEntry) -> ((String) serverSegmentEntry.get("segment_id")).contains(
"auth_test"))
.collect(Collectors.toList())
);
LOG.info("Checking sys.tasks query as datasourceWithStateUser...");
verifySystemSchemaQuery(
datasourceWithStateUserClient,
SYS_SCHEMA_TASKS_QUERY,
adminTasks.stream()
.filter((taskEntry) -> "auth_test".equals(taskEntry.get("datasource")))
.collect(Collectors.toList())
);
}
@Test
public void test_systemSchemaAccess_stateOnlyUser() throws Exception
{
HttpUtil.makeRequest(stateOnlyUserClient, HttpMethod.GET, config.getBrokerUrl() + "/status", null);
// as user that can only read STATE
LOG.info("Checking sys.segments query as stateOnlyUser...");
verifySystemSchemaQuery(
stateOnlyUserClient,
SYS_SCHEMA_SEGMENTS_QUERY,
Collections.emptyList()
);
LOG.info("Checking sys.servers query as stateOnlyUser...");
verifySystemSchemaServerQuery(
stateOnlyUserClient,
SYS_SCHEMA_SERVERS_QUERY,
adminServers
);
LOG.info("Checking sys.server_segments query as stateOnlyUser...");
verifySystemSchemaQuery(
stateOnlyUserClient,
SYS_SCHEMA_SERVER_SEGMENTS_QUERY,
Collections.emptyList()
);
LOG.info("Checking sys.tasks query as stateOnlyUser...");
verifySystemSchemaQuery(
stateOnlyUserClient,
SYS_SCHEMA_TASKS_QUERY,
Collections.emptyList()
);
}
@Test
public void test_unsecuredPathWithoutCredentials_allowed()
{
// check that we are allowed to access unsecured path without credentials.
checkUnsecuredCoordinatorLoadQueuePath(httpClient);
}
@Test
public void test_admin_loadStatus() throws Exception
{
checkLoadStatus(adminClient);
}
@Test
public void test_admin_hasNodeAccess()
{
checkNodeAccess(adminClient);
}
@Test
public void test_internalSystemUser_hasNodeAccess()
{
checkNodeAccess(internalSystemClient);
}
@Test
public void test_avaticaQuery_broker()
{
testAvaticaQuery(getBrokerAvacticaUrl());
}
@Test
public void test_avaticaQuery_router()
{
testAvaticaQuery(getRouterAvacticaUrl());
}
@Test
public void test_avaticaQueryAuthFailure_broker() throws Exception
{
testAvaticaAuthFailure(getBrokerAvacticaUrl());
}
@Test
public void test_avaticaQueryAuthFailure_router() throws Exception
{
testAvaticaAuthFailure(getRouterAvacticaUrl());
}
@Test
public void test_admin_optionsRequest()
{
verifyAdminOptionsRequest();
}
@Test
public void test_authentication_invalidAuthName_fails()
{
verifyAuthenticationInvalidAuthNameFails();
}
@Test
public void test_authorization_invalidAuthName_fails()
{
verifyAuthorizationInvalidAuthNameFails();
}
@Test
public void test_groupMappings_invalidAuthName_fails()
{
verifyGroupMappingsInvalidAuthNameFails();
}
@Test
public void testMaliciousUser()
{
verifyMaliciousUser();
}
void setupHttpClientsAndUsers() throws Exception
{
setupHttpClients();
setupDatasourceOnlyUser();
setupDatasourceAndSysTableUser();
setupDatasourceAndSysAndStateUser();
setupSysTableAndStateOnlyUser();
}
void checkNodeAccess(HttpClient httpClient)
{
HttpUtil.makeRequest(httpClient, HttpMethod.GET, config.getCoordinatorUrl() + "/status", null);
@ -302,7 +695,7 @@ public abstract class AbstractAuthConfigurationTest
testOptionsRequests(adminClient);
}
void verifyAuthenticatioInvalidAuthNameFails()
void verifyAuthenticationInvalidAuthNameFails()
{
verifyInvalidAuthNameFails(StringUtils.format(
"%s/druid-ext/basic-security/authentication/listen/%s",
@ -370,7 +763,6 @@ public abstract class AbstractAuthConfigurationTest
setupTestSpecificHttpClients();
}
abstract void setupUsers() throws Exception;
void setupCommonHttpClients()
{
@ -384,6 +776,11 @@ public abstract class AbstractAuthConfigurationTest
httpClient
);
datasourceAndSysUserClient = new CredentialedHttpClient(
new BasicCredentials("datasourceAndSysUser", "helloworld"),
httpClient
);
datasourceWithStateUserClient = new CredentialedHttpClient(
new BasicCredentials("datasourceWithStateUser", "helloworld"),
httpClient
@ -400,8 +797,6 @@ public abstract class AbstractAuthConfigurationTest
);
}
abstract void setupTestSpecificHttpClients() throws Exception;
void setExpectedSystemSchemaObjects() throws IOException
{
// initial setup is done now, run the system schema response content tests
@ -462,10 +857,4 @@ public abstract class AbstractAuthConfigurationTest
json = StringUtils.replace(json, "%%NON_LEADER%%", String.valueOf(NullHandling.defaultLongValue()));
return json;
}
abstract String getAuthenticatorName();
abstract String getAuthorizerName();
abstract String getExpectedAvaticaAuthError();
}

View File

@ -19,30 +19,23 @@
package org.apache.druid.tests.security;
import com.google.common.collect.ImmutableList;
import org.apache.druid.java.util.common.StringUtils;
import org.apache.druid.java.util.common.logger.Logger;
import org.apache.druid.java.util.http.client.CredentialedHttpClient;
import org.apache.druid.java.util.http.client.HttpClient;
import org.apache.druid.java.util.http.client.auth.BasicCredentials;
import org.apache.druid.security.basic.authentication.entity.BasicAuthenticatorCredentialUpdate;
import org.apache.druid.server.security.Action;
import org.apache.druid.server.security.Resource;
import org.apache.druid.server.security.ResourceAction;
import org.apache.druid.server.security.ResourceType;
import org.apache.druid.testing.guice.DruidTestModuleFactory;
import org.apache.druid.testing.utils.HttpUtil;
import org.apache.druid.testing.utils.ITRetryUtil;
import org.apache.druid.tests.TestNGGroup;
import org.jboss.netty.handler.codec.http.HttpMethod;
import org.jboss.netty.handler.codec.http.HttpResponseStatus;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Guice;
import org.testng.annotations.Test;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
@Test(groups = TestNGGroup.SECURITY)
@Guice(moduleFactory = DruidTestModuleFactory.class)
@ -65,313 +58,61 @@ public class ITBasicAuthConfigurationTest extends AbstractAuthConfigurationTest
() -> coordinatorClient.areSegmentsLoaded("auth_test"), "auth_test segment load"
);
setupHttpClients();
setupUsers();
setupHttpClientsAndUsers();
setExpectedSystemSchemaObjects();
}
@Test
public void test_systemSchemaAccess_admin() throws Exception
{
// check that admin access works on all nodes
checkNodeAccess(adminClient);
// as admin
LOG.info("Checking sys.segments query as admin...");
verifySystemSchemaQuery(
adminClient,
SYS_SCHEMA_SEGMENTS_QUERY,
adminSegments
);
LOG.info("Checking sys.servers query as admin...");
verifySystemSchemaServerQuery(
adminClient,
SYS_SCHEMA_SERVERS_QUERY,
getServersWithoutCurrentSize(adminServers)
);
LOG.info("Checking sys.server_segments query as admin...");
verifySystemSchemaQuery(
adminClient,
SYS_SCHEMA_SERVER_SEGMENTS_QUERY,
adminServerSegments
);
LOG.info("Checking sys.tasks query as admin...");
verifySystemSchemaQuery(
adminClient,
SYS_SCHEMA_TASKS_QUERY,
adminTasks
);
}
@Test
public void test_systemSchemaAccess_datasourceOnlyUser() throws Exception
{
// check that we can access a datasource-permission restricted resource on the broker
HttpUtil.makeRequest(
datasourceOnlyUserClient,
HttpMethod.GET,
config.getBrokerUrl() + "/druid/v2/datasources/auth_test",
null
);
// as user that can only read auth_test
LOG.info("Checking sys.segments query as datasourceOnlyUser...");
verifySystemSchemaQuery(
datasourceOnlyUserClient,
SYS_SCHEMA_SEGMENTS_QUERY,
adminSegments.stream()
.filter((segmentEntry) -> "auth_test".equals(segmentEntry.get("datasource")))
.collect(Collectors.toList())
);
LOG.info("Checking sys.servers query as datasourceOnlyUser...");
verifySystemSchemaQueryFailure(
datasourceOnlyUserClient,
SYS_SCHEMA_SERVERS_QUERY,
HttpResponseStatus.FORBIDDEN,
"{\"Access-Check-Result\":\"Insufficient permission to view servers : Allowed:false, Message:\"}"
);
LOG.info("Checking sys.server_segments query as datasourceOnlyUser...");
verifySystemSchemaQueryFailure(
datasourceOnlyUserClient,
SYS_SCHEMA_SERVER_SEGMENTS_QUERY,
HttpResponseStatus.FORBIDDEN,
"{\"Access-Check-Result\":\"Insufficient permission to view servers : Allowed:false, Message:\"}"
);
LOG.info("Checking sys.tasks query as datasourceOnlyUser...");
verifySystemSchemaQuery(
datasourceOnlyUserClient,
SYS_SCHEMA_TASKS_QUERY,
adminTasks.stream()
.filter((taskEntry) -> "auth_test".equals(taskEntry.get("datasource")))
.collect(Collectors.toList())
);
}
@Test
public void test_systemSchemaAccess_datasourceWithStateUser() throws Exception
{
// check that we can access a state-permission restricted resource on the broker
HttpUtil.makeRequest(
datasourceWithStateUserClient,
HttpMethod.GET,
config.getBrokerUrl() + "/status",
null
);
// as user that can read auth_test and STATE
LOG.info("Checking sys.segments query as datasourceWithStateUser...");
verifySystemSchemaQuery(
datasourceWithStateUserClient,
SYS_SCHEMA_SEGMENTS_QUERY,
adminSegments.stream()
.filter((segmentEntry) -> "auth_test".equals(segmentEntry.get("datasource")))
.collect(Collectors.toList())
);
LOG.info("Checking sys.servers query as datasourceWithStateUser...");
verifySystemSchemaServerQuery(
datasourceWithStateUserClient,
SYS_SCHEMA_SERVERS_QUERY,
adminServers
);
LOG.info("Checking sys.server_segments query as datasourceWithStateUser...");
verifySystemSchemaQuery(
datasourceWithStateUserClient,
SYS_SCHEMA_SERVER_SEGMENTS_QUERY,
adminServerSegments.stream()
.filter((serverSegmentEntry) -> ((String) serverSegmentEntry.get("segment_id")).contains(
"auth_test"))
.collect(Collectors.toList())
);
LOG.info("Checking sys.tasks query as datasourceWithStateUser...");
verifySystemSchemaQuery(
datasourceWithStateUserClient,
SYS_SCHEMA_TASKS_QUERY,
adminTasks.stream()
.filter((taskEntry) -> "auth_test".equals(taskEntry.get("datasource")))
.collect(Collectors.toList())
);
}
@Test
public void test_systemSchemaAccess_stateOnlyUser() throws Exception
{
HttpUtil.makeRequest(stateOnlyUserClient, HttpMethod.GET, config.getBrokerUrl() + "/status", null);
// as user that can only read STATE
LOG.info("Checking sys.segments query as stateOnlyUser...");
verifySystemSchemaQuery(
stateOnlyUserClient,
SYS_SCHEMA_SEGMENTS_QUERY,
Collections.emptyList()
);
LOG.info("Checking sys.servers query as stateOnlyUser...");
verifySystemSchemaServerQuery(
stateOnlyUserClient,
SYS_SCHEMA_SERVERS_QUERY,
adminServers
);
LOG.info("Checking sys.server_segments query as stateOnlyUser...");
verifySystemSchemaQuery(
stateOnlyUserClient,
SYS_SCHEMA_SERVER_SEGMENTS_QUERY,
Collections.emptyList()
);
LOG.info("Checking sys.tasks query as stateOnlyUser...");
verifySystemSchemaQuery(
stateOnlyUserClient,
SYS_SCHEMA_TASKS_QUERY,
Collections.emptyList()
);
}
@Test
public void test_unsecuredPathWithoutCredentials_allowed()
{
// check that we are allowed to access unsecured path without credentials.
checkUnsecuredCoordinatorLoadQueuePath(httpClient);
}
@Test
public void test_admin_hasNodeAccess()
{
checkNodeAccess(adminClient);
}
@Test
public void test_admin_loadStatus() throws Exception
{
checkLoadStatus(adminClient);
}
@Test
public void test_internalSystemUser_hasNodeAccess()
{
checkNodeAccess(internalSystemClient);
}
@Test
public void test_druid99User_hasNodeAccess()
{
checkNodeAccess(druid99);
}
@Test
public void test_avaticaQuery_broker()
{
testAvaticaQuery(getBrokerAvacticaUrl());
}
@Test
public void test_avaticaQuery_router()
{
testAvaticaQuery(getRouterAvacticaUrl());
}
@Test
public void test_avaticaQueryAuthFailure_broker() throws Exception
{
testAvaticaAuthFailure(getBrokerAvacticaUrl());
}
@Test
public void test_avaticaQueryAuthFailure_router() throws Exception
{
testAvaticaAuthFailure(getRouterAvacticaUrl());
}
@Test
public void test_admin_optionsRequest()
{
verifyAdminOptionsRequest();
}
@Test
public void test_authentication_invalidAuthName_fails()
{
verifyAuthenticatioInvalidAuthNameFails();
}
@Test
public void test_authorization_invalidAuthName_fails()
{
verifyAuthorizationInvalidAuthNameFails();
}
@Test
public void test_groupMappings_invalidAuthName_fails()
{
verifyGroupMappingsInvalidAuthNameFails();
}
@Test
public void testMaliciousUser()
{
verifyMaliciousUser();
}
@Override
void setupUsers() throws Exception
void setupDatasourceOnlyUser() throws Exception
{
// create a new user+role that can only read 'auth_test'
List<ResourceAction> readDatasourceOnlyPermissions = Collections.singletonList(
new ResourceAction(
new Resource("auth_test", ResourceType.DATASOURCE),
Action.READ
)
);
createUserAndRoleWithPermissions(
adminClient,
"datasourceOnlyUser",
"helloworld",
"datasourceOnlyRole",
readDatasourceOnlyPermissions
DATASOURCE_ONLY_PERMISSIONS
);
}
// create a new user+role that can only read 'auth_test' + STATE read access
List<ResourceAction> readDatasourceWithStatePermissions = ImmutableList.of(
new ResourceAction(
new Resource("auth_test", ResourceType.DATASOURCE),
Action.READ
),
new ResourceAction(
new Resource(".*", ResourceType.STATE),
Action.READ
)
@Override
void setupDatasourceAndSysTableUser() throws Exception
{
createUserAndRoleWithPermissions(
adminClient,
"datasourceAndSysUser",
"helloworld",
"datasourceAndSysRole",
DATASOURCE_SYS_PERMISSIONS
);
}
@Override
void setupDatasourceAndSysAndStateUser() throws Exception
{
createUserAndRoleWithPermissions(
adminClient,
"datasourceWithStateUser",
"helloworld",
"datasourceWithStateRole",
readDatasourceWithStatePermissions
DATASOURCE_SYS_STATE_PERMISSIONS
);
}
// create a new user+role with only STATE read access
List<ResourceAction> stateOnlyPermissions = ImmutableList.of(
new ResourceAction(
new Resource(".*", ResourceType.STATE),
Action.READ
)
);
@Override
void setupSysTableAndStateOnlyUser() throws Exception
{
createUserAndRoleWithPermissions(
adminClient,
"stateOnlyUser",
"helloworld",
"stateOnlyRole",
stateOnlyPermissions
STATE_ONLY_PERMISSIONS
);
}
@ -379,18 +120,12 @@ public class ITBasicAuthConfigurationTest extends AbstractAuthConfigurationTest
void setupTestSpecificHttpClients() throws Exception
{
// create a new user+role that can read /status
List<ResourceAction> permissions = Collections.singletonList(
new ResourceAction(
new Resource(".*", ResourceType.STATE),
Action.READ
)
);
createUserAndRoleWithPermissions(
adminClient,
"druid",
"helloworld",
"druidrole",
permissions
STATE_ONLY_PERMISSIONS
);
// create 100 users

View File

@ -20,7 +20,6 @@
package org.apache.druid.tests.security;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.inject.Inject;
import org.apache.druid.java.util.common.StringUtils;
@ -29,18 +28,13 @@ import org.apache.druid.java.util.http.client.CredentialedHttpClient;
import org.apache.druid.java.util.http.client.HttpClient;
import org.apache.druid.java.util.http.client.auth.BasicCredentials;
import org.apache.druid.security.basic.authorization.entity.BasicAuthorizerGroupMapping;
import org.apache.druid.server.security.Action;
import org.apache.druid.server.security.Resource;
import org.apache.druid.server.security.ResourceAction;
import org.apache.druid.server.security.ResourceType;
import org.apache.druid.testing.IntegrationTestingConfig;
import org.apache.druid.testing.clients.CoordinatorResourceTestClient;
import org.apache.druid.testing.guice.DruidTestModuleFactory;
import org.apache.druid.testing.utils.HttpUtil;
import org.apache.druid.testing.utils.ITRetryUtil;
import org.apache.druid.tests.TestNGGroup;
import org.jboss.netty.handler.codec.http.HttpMethod;
import org.jboss.netty.handler.codec.http.HttpResponseStatus;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Guice;
import org.testng.annotations.Test;
@ -48,7 +42,6 @@ import org.testng.annotations.Test;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Test(groups = TestNGGroup.LDAP_SECURITY)
@Guice(moduleFactory = DruidTestModuleFactory.class)
@ -67,8 +60,6 @@ public class ITBasicAuthLdapConfigurationTest extends AbstractAuthConfigurationT
@Inject
ObjectMapper jsonMapper;
@Inject
private CoordinatorResourceTestClient coordinatorClient;
private HttpClient druidUserClient;
private HttpClient stateOnlyNoLdapGroupUserClient;
@ -81,177 +72,10 @@ public class ITBasicAuthLdapConfigurationTest extends AbstractAuthConfigurationT
() -> coordinatorClient.areSegmentsLoaded("auth_test"), "auth_test segment load"
);
setupHttpClients();
setupUsers();
setupHttpClientsAndUsers();
setExpectedSystemSchemaObjects();
}
@Test
public void test_systemSchemaAccess_admin() throws Exception
{
// check that admin access works on all nodes
checkNodeAccess(adminClient);
// as admin
LOG.info("Checking sys.segments query as admin...");
verifySystemSchemaQuery(
adminClient,
SYS_SCHEMA_SEGMENTS_QUERY,
adminSegments
);
LOG.info("Checking sys.servers query as admin...");
verifySystemSchemaServerQuery(
adminClient,
SYS_SCHEMA_SERVERS_QUERY,
getServersWithoutCurrentSize(adminServers)
);
LOG.info("Checking sys.server_segments query as admin...");
verifySystemSchemaQuery(
adminClient,
SYS_SCHEMA_SERVER_SEGMENTS_QUERY,
adminServerSegments
);
LOG.info("Checking sys.tasks query as admin...");
verifySystemSchemaQuery(
adminClient,
SYS_SCHEMA_TASKS_QUERY,
adminTasks
);
}
@Test
public void test_systemSchemaAccess_datasourceOnlyUser() throws Exception
{
// check that we can access a datasource-permission restricted resource on the broker
HttpUtil.makeRequest(
datasourceOnlyUserClient,
HttpMethod.GET,
config.getBrokerUrl() + "/druid/v2/datasources/auth_test",
null
);
// as user that can only read auth_test
LOG.info("Checking sys.segments query as datasourceOnlyUser...");
verifySystemSchemaQuery(
datasourceOnlyUserClient,
SYS_SCHEMA_SEGMENTS_QUERY,
adminSegments.stream()
.filter((segmentEntry) -> "auth_test".equals(segmentEntry.get("datasource")))
.collect(Collectors.toList())
);
LOG.info("Checking sys.servers query as datasourceOnlyUser...");
verifySystemSchemaQueryFailure(
datasourceOnlyUserClient,
SYS_SCHEMA_SERVERS_QUERY,
HttpResponseStatus.FORBIDDEN,
"{\"Access-Check-Result\":\"Insufficient permission to view servers : Allowed:false, Message:\"}"
);
LOG.info("Checking sys.server_segments query as datasourceOnlyUser...");
verifySystemSchemaQueryFailure(
datasourceOnlyUserClient,
SYS_SCHEMA_SERVER_SEGMENTS_QUERY,
HttpResponseStatus.FORBIDDEN,
"{\"Access-Check-Result\":\"Insufficient permission to view servers : Allowed:false, Message:\"}"
);
LOG.info("Checking sys.tasks query as datasourceOnlyUser...");
verifySystemSchemaQuery(
datasourceOnlyUserClient,
SYS_SCHEMA_TASKS_QUERY,
adminTasks.stream()
.filter((taskEntry) -> "auth_test".equals(taskEntry.get("datasource")))
.collect(Collectors.toList())
);
}
@Test
public void test_systemSchemaAccess_datasourceWithStateUser() throws Exception
{
// check that we can access a state-permission restricted resource on the broker
HttpUtil.makeRequest(
datasourceWithStateUserClient,
HttpMethod.GET,
config.getBrokerUrl() + "/status",
null
);
// as user that can read auth_test and STATE
LOG.info("Checking sys.segments query as datasourceWithStateUser...");
verifySystemSchemaQuery(
datasourceWithStateUserClient,
SYS_SCHEMA_SEGMENTS_QUERY,
adminSegments.stream()
.filter((segmentEntry) -> "auth_test".equals(segmentEntry.get("datasource")))
.collect(Collectors.toList())
);
LOG.info("Checking sys.servers query as datasourceWithStateUser...");
verifySystemSchemaServerQuery(
datasourceWithStateUserClient,
SYS_SCHEMA_SERVERS_QUERY,
adminServers
);
LOG.info("Checking sys.server_segments query as datasourceWithStateUser...");
verifySystemSchemaQuery(
datasourceWithStateUserClient,
SYS_SCHEMA_SERVER_SEGMENTS_QUERY,
adminServerSegments.stream()
.filter((serverSegmentEntry) -> ((String) serverSegmentEntry.get("segment_id")).contains(
"auth_test"))
.collect(Collectors.toList())
);
LOG.info("Checking sys.tasks query as datasourceWithStateUser...");
verifySystemSchemaQuery(
datasourceWithStateUserClient,
SYS_SCHEMA_TASKS_QUERY,
adminTasks.stream()
.filter((taskEntry) -> "auth_test".equals(taskEntry.get("datasource")))
.collect(Collectors.toList())
);
}
@Test
public void test_systemSchemaAccess_stateOnlyUser() throws Exception
{
HttpUtil.makeRequest(stateOnlyUserClient, HttpMethod.GET, config.getBrokerUrl() + "/status", null);
// as user that can only read STATE
LOG.info("Checking sys.segments query as stateOnlyUser...");
verifySystemSchemaQuery(
stateOnlyUserClient,
SYS_SCHEMA_SEGMENTS_QUERY,
Collections.emptyList()
);
LOG.info("Checking sys.servers query as stateOnlyUser...");
verifySystemSchemaServerQuery(
stateOnlyUserClient,
SYS_SCHEMA_SERVERS_QUERY,
adminServers
);
LOG.info("Checking sys.server_segments query as stateOnlyUser...");
verifySystemSchemaQuery(
stateOnlyUserClient,
SYS_SCHEMA_SERVER_SEGMENTS_QUERY,
Collections.emptyList()
);
LOG.info("Checking sys.tasks query as stateOnlyUser...");
verifySystemSchemaQuery(
stateOnlyUserClient,
SYS_SCHEMA_TASKS_QUERY,
Collections.emptyList()
);
}
@Test
public void test_systemSchemaAccess_stateOnlyNoLdapGroupUser() throws Exception
{
@ -287,141 +111,52 @@ public class ITBasicAuthLdapConfigurationTest extends AbstractAuthConfigurationT
);
}
@Test
public void test_unsecuredPathWithoutCredentials_allowed()
{
// check that we are allowed to access unsecured path without credentials.
checkUnsecuredCoordinatorLoadQueuePath(httpClient);
}
@Test
public void test_admin_loadStatus() throws Exception
{
checkLoadStatus(adminClient);
}
@Test
public void test_admin_hasNodeAccess()
{
checkNodeAccess(adminClient);
}
@Test
public void test_internalSystemUser_hasNodeAccess()
{
checkNodeAccess(internalSystemClient);
}
@Test
public void test_druidUser_hasNodeAccess()
{
checkNodeAccess(druidUserClient);
}
@Test
public void test_avaticaQuery_broker()
{
testAvaticaQuery(getBrokerAvacticaUrl());
}
@Test
public void test_avaticaQuery_router()
@Override
void setupDatasourceOnlyUser() throws Exception
{
testAvaticaQuery(getRouterAvacticaUrl());
}
@Test
public void test_avaticaQueryAuthFailure_broker() throws Exception
{
testAvaticaAuthFailure(getBrokerAvacticaUrl());
}
@Test
public void test_avaticaQueryAuthFailure_router() throws Exception
{
testAvaticaAuthFailure(getRouterAvacticaUrl());
}
@Test
public void test_admin_optionsRequest()
{
verifyAdminOptionsRequest();
}
@Test
public void test_authentication_invalidAuthName_fails()
{
verifyAuthenticatioInvalidAuthNameFails();
}
@Test
public void test_authorization_invalidAuthName_fails()
{
verifyAuthorizationInvalidAuthNameFails();
}
@Test
public void test_groupMappings_invalidAuthName_fails()
{
verifyGroupMappingsInvalidAuthNameFails();
}
@Test
public void testMaliciousUser()
{
verifyMaliciousUser();
createRoleWithPermissionsAndGroupMapping(
"datasourceOnlyGroup",
ImmutableMap.of("datasourceOnlyRole", DATASOURCE_ONLY_PERMISSIONS)
);
}
@Override
void setupUsers() throws Exception
void setupDatasourceAndSysTableUser() throws Exception
{
// create a role that can only read 'auth_test'
List<ResourceAction> readDatasourceOnlyPermissions = Collections.singletonList(
new ResourceAction(
new Resource("auth_test", ResourceType.DATASOURCE),
Action.READ
)
);
createRoleWithPermissionsAndGroupMapping(
"datasourceOnlyGroup",
ImmutableMap.of("datasourceOnlyRole", readDatasourceOnlyPermissions)
);
// create a new role that can only read 'auth_test' + STATE read access
List<ResourceAction> readDatasourceWithStatePermissions = ImmutableList.of(
new ResourceAction(
new Resource("auth_test", ResourceType.DATASOURCE),
Action.READ
),
new ResourceAction(
new Resource(".*", ResourceType.STATE),
Action.READ
)
"datasourceWithSysGroup",
ImmutableMap.of("datasourceWithSysRole", DATASOURCE_SYS_PERMISSIONS)
);
}
@Override
void setupDatasourceAndSysAndStateUser() throws Exception
{
createRoleWithPermissionsAndGroupMapping(
"datasourceWithStateGroup",
ImmutableMap.of("datasourceWithStateRole", readDatasourceWithStatePermissions)
);
// create a new role with only STATE read access
List<ResourceAction> stateOnlyPermissions = ImmutableList.of(
new ResourceAction(
new Resource(".*", ResourceType.STATE),
Action.READ
)
ImmutableMap.of("datasourceWithStateRole", DATASOURCE_SYS_STATE_PERMISSIONS)
);
}
@Override
void setupSysTableAndStateOnlyUser() throws Exception
{
createRoleWithPermissionsAndGroupMapping(
"stateOnlyGroup",
ImmutableMap.of("stateOnlyRole", stateOnlyPermissions)
ImmutableMap.of("stateOnlyRole", STATE_ONLY_PERMISSIONS)
);
// create a role that can read /status
createRoleWithPermissionsAndGroupMapping(
"druidGroup",
ImmutableMap.of("druidrole", stateOnlyPermissions)
ImmutableMap.of("druidrole", STATE_ONLY_PERMISSIONS)
);
assignUserToRole("stateOnlyNoLdapGroup", "stateOnlyRole");

View File

@ -32,6 +32,7 @@ public class ResourceType
public static final String VIEW = "VIEW";
public static final String CONFIG = "CONFIG";
public static final String STATE = "STATE";
public static final String SYSTEM_TABLE = "SYSTEM_TABLE";
private static final Set<String> KNOWN_TYPES = Sets.newConcurrentHashSet();
@ -41,6 +42,7 @@ public class ResourceType
registerResourceType(VIEW);
registerResourceType(CONFIG);
registerResourceType(STATE);
registerResourceType(SYSTEM_TABLE);
}
/**

View File

@ -67,6 +67,9 @@ public class PlannerConfig
@JsonProperty
private boolean computeInnerJoinCostAsFilter = true;
@JsonProperty
private boolean authorizeSystemTablesDirectly = false;
public long getMetadataSegmentPollPeriod()
{
return metadataSegmentPollPeriod;
@ -129,6 +132,11 @@ public class PlannerConfig
return computeInnerJoinCostAsFilter;
}
public boolean isAuthorizeSystemTablesDirectly()
{
return authorizeSystemTablesDirectly;
}
public PlannerConfig withOverrides(final Map<String, Object> context)
{
if (context == null) {
@ -153,15 +161,18 @@ public class PlannerConfig
CTX_KEY_USE_APPROXIMATE_TOPN,
isUseApproximateTopN()
);
newConfig.computeInnerJoinCostAsFilter = getContextBoolean(
context,
CTX_COMPUTE_INNER_JOIN_COST_AS_FILTER,
computeInnerJoinCostAsFilter
);
newConfig.requireTimeCondition = isRequireTimeCondition();
newConfig.sqlTimeZone = getSqlTimeZone();
newConfig.awaitInitializationOnStart = isAwaitInitializationOnStart();
newConfig.metadataSegmentCacheEnable = isMetadataSegmentCacheEnable();
newConfig.metadataSegmentPollPeriod = getMetadataSegmentPollPeriod();
newConfig.serializeComplexValues = shouldSerializeComplexValues();
newConfig.computeInnerJoinCostAsFilter = getContextBoolean(context,
CTX_COMPUTE_INNER_JOIN_COST_AS_FILTER,
computeInnerJoinCostAsFilter);
newConfig.authorizeSystemTablesDirectly = isAuthorizeSystemTablesDirectly();
return newConfig;
}

View File

@ -21,6 +21,10 @@ package org.apache.druid.sql.calcite.schema;
import com.google.inject.Inject;
import org.apache.calcite.schema.Schema;
import org.apache.druid.server.security.ResourceType;
import org.apache.druid.sql.calcite.planner.PlannerConfig;
import javax.annotation.Nullable;
/**
* The schema for Druid system tables to be accessible via SQL.
@ -30,10 +34,12 @@ public class NamedSystemSchema implements NamedSchema
public static final String NAME = "sys";
private final SystemSchema systemSchema;
private final PlannerConfig plannerConfig;
@Inject
public NamedSystemSchema(SystemSchema systemSchema)
public NamedSystemSchema(PlannerConfig plannerConfig, SystemSchema systemSchema)
{
this.plannerConfig = plannerConfig;
this.systemSchema = systemSchema;
}
@ -48,4 +54,14 @@ public class NamedSystemSchema implements NamedSchema
{
return systemSchema;
}
@Nullable
@Override
public String getSchemaResourceType(String resourceName)
{
if (plannerConfig.isAuthorizeSystemTablesDirectly()) {
return ResourceType.SYSTEM_TABLE;
}
return null;
}
}

View File

@ -175,6 +175,15 @@ public class BaseCalciteQueryTest extends CalciteTestBase
}
};
public static final PlannerConfig PLANNER_CONFIG_AUTHORIZE_SYS_TABLES = new PlannerConfig()
{
@Override
public boolean isAuthorizeSystemTablesDirectly()
{
return true;
}
};
public static final String DUMMY_SQL_ID = "dummy";
public static final String LOS_ANGELES = "America/Los_Angeles";

View File

@ -22,6 +22,7 @@ package org.apache.druid.sql.calcite;
import com.google.common.collect.ImmutableSet;
import org.apache.druid.server.security.Resource;
import org.apache.druid.server.security.ResourceType;
import org.apache.druid.sql.calcite.planner.PlannerConfig;
import org.apache.druid.sql.calcite.util.CalciteTests;
import org.junit.Assert;
import org.junit.Test;
@ -243,4 +244,39 @@ public class DruidPlannerResourceAnalyzeTest extends BaseCalciteQueryTest
requiredResources
);
}
@Test
public void testSysTables()
{
testSysTable("SELECT * FROM sys.segments", null, PLANNER_CONFIG_DEFAULT);
testSysTable("SELECT * FROM sys.servers", null, PLANNER_CONFIG_DEFAULT);
testSysTable("SELECT * FROM sys.server_segments", null, PLANNER_CONFIG_DEFAULT);
testSysTable("SELECT * FROM sys.tasks", null, PLANNER_CONFIG_DEFAULT);
testSysTable("SELECT * FROM sys.supervisors", null, PLANNER_CONFIG_DEFAULT);
testSysTable("SELECT * FROM sys.segments", "segments", PLANNER_CONFIG_AUTHORIZE_SYS_TABLES);
testSysTable("SELECT * FROM sys.servers", "servers", PLANNER_CONFIG_AUTHORIZE_SYS_TABLES);
testSysTable("SELECT * FROM sys.server_segments", "server_segments", PLANNER_CONFIG_AUTHORIZE_SYS_TABLES);
testSysTable("SELECT * FROM sys.tasks", "tasks", PLANNER_CONFIG_AUTHORIZE_SYS_TABLES);
testSysTable("SELECT * FROM sys.supervisors", "supervisors", PLANNER_CONFIG_AUTHORIZE_SYS_TABLES);
}
private void testSysTable(String sql, String name, PlannerConfig plannerConfig)
{
Set<Resource> requiredResources = analyzeResources(
plannerConfig,
sql,
CalciteTests.REGULAR_USER_AUTH_RESULT
);
if (name == null) {
Assert.assertEquals(0, requiredResources.size());
} else {
Assert.assertEquals(
ImmutableSet.of(
new Resource(name, ResourceType.SYSTEM_TABLE)
),
requiredResources
);
}
}
}

View File

@ -19,7 +19,10 @@
package org.apache.druid.sql.calcite.schema;
import org.apache.druid.server.security.ResourceType;
import org.apache.druid.sql.calcite.planner.PlannerConfig;
import org.apache.druid.sql.calcite.util.CalciteTestBase;
import org.easymock.EasyMock;
import org.easymock.Mock;
import org.junit.Assert;
import org.junit.Before;
@ -32,12 +35,15 @@ public class NamedSystemSchemaTest extends CalciteTestBase
@Mock
private SystemSchema systemSchema;
private PlannerConfig plannerConfig;
private NamedSystemSchema target;
@Before
public void setUp()
{
target = new NamedSystemSchema(systemSchema);
plannerConfig = EasyMock.createMock(PlannerConfig.class);
target = new NamedSystemSchema(plannerConfig, systemSchema);
}
@Test
@ -51,4 +57,22 @@ public class NamedSystemSchemaTest extends CalciteTestBase
{
Assert.assertEquals(systemSchema, target.getSchema());
}
@Test
public void testResourceTypeAuthDisabled()
{
EasyMock.expect(plannerConfig.isAuthorizeSystemTablesDirectly()).andReturn(false).once();
EasyMock.replay(plannerConfig);
Assert.assertNull(target.getSchemaResourceType("servers"));
EasyMock.verify(plannerConfig);
}
@Test
public void testResourceTypeAuthEnabled()
{
EasyMock.expect(plannerConfig.isAuthorizeSystemTablesDirectly()).andReturn(true).once();
EasyMock.replay(plannerConfig);
Assert.assertEquals(ResourceType.SYSTEM_TABLE, target.getSchemaResourceType("servers"));
EasyMock.verify(plannerConfig);
}
}

View File

@ -1140,7 +1140,7 @@ public class CalciteTests
SchemaPlus rootSchema = CalciteSchema.createRootSchema(false, false).plus();
Set<NamedSchema> namedSchemas = ImmutableSet.of(
new NamedDruidSchema(druidSchema, CalciteTests.DRUID_SCHEMA_NAME),
new NamedSystemSchema(systemSchema),
new NamedSystemSchema(plannerConfig, systemSchema),
new NamedLookupSchema(lookupSchema)
);
DruidSchemaCatalog catalog = new DruidSchemaCatalog(
@ -1178,7 +1178,7 @@ public class CalciteTests
SchemaPlus rootSchema = CalciteSchema.createRootSchema(false, false).plus();
Set<NamedSchema> namedSchemas = ImmutableSet.of(
new NamedDruidSchema(druidSchema, CalciteTests.DRUID_SCHEMA_NAME),
new NamedSystemSchema(systemSchema),
new NamedSystemSchema(plannerConfig, systemSchema),
new NamedLookupSchema(lookupSchema),
new NamedViewSchema(viewSchema)
);

View File

@ -180,6 +180,7 @@ SqlFirehose
SqlParameter
SslContextFactory
StatsD
SYSTEM_TABLE
TCP
TGT
TLS