mirror of https://github.com/apache/druid.git
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:
parent
3c487ff5b4
commit
5de26cf6d9
|
@ -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.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.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.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`.
|
> 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
|
> These properties are no longer available. Since Druid 0.18.0, you can use `druid.server.http.maxSubqueryRows` to control the maximum
|
||||||
|
|
|
@ -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.
|
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.
|
* DATASOURCE – Each Druid table (i.e., `tables` in the `druid` schema in SQL) is a resource.
|
||||||
* CONFIG – Configuration resources exposed by the cluster components.
|
* CONFIG – Configuration resources exposed by the cluster components.
|
||||||
* STATE – Cluster-wide state resources.
|
* 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).
|
For specific resources associated with the types, see the endpoint list below and corresponding descriptions in [API Reference](./api-reference.md).
|
||||||
|
|
||||||
|
|
|
@ -72,6 +72,7 @@ druid_coordinator_kill_supervisor_on=true
|
||||||
druid_coordinator_kill_supervisor_period=PT10S
|
druid_coordinator_kill_supervisor_period=PT10S
|
||||||
druid_coordinator_kill_supervisor_durationToRetain=PT0M
|
druid_coordinator_kill_supervisor_durationToRetain=PT0M
|
||||||
druid_coordinator_period_metadataStoreManagementPeriod=PT10S
|
druid_coordinator_period_metadataStoreManagementPeriod=PT10S
|
||||||
|
druid_sql_planner_authorizeSystemTablesDirectly=true
|
||||||
|
|
||||||
# Testing the legacy config from https://github.com/apache/druid/pull/10267
|
# Testing the legacy config from https://github.com/apache/druid/pull/10267
|
||||||
# Can remove this when the flag is no longer needed
|
# Can remove this when the flag is no longer needed
|
||||||
|
|
|
@ -136,3 +136,21 @@ homeDirectory: /home/druid
|
||||||
uidNumber: 7
|
uidNumber: 7
|
||||||
gidNumber: 7
|
gidNumber: 7
|
||||||
userPassword: helloworld
|
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
|
||||||
|
|
|
@ -21,6 +21,7 @@ package org.apache.druid.tests.security;
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.type.TypeReference;
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import com.google.inject.Inject;
|
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.HttpClient;
|
||||||
import org.apache.druid.java.util.http.client.auth.BasicCredentials;
|
import org.apache.druid.java.util.http.client.auth.BasicCredentials;
|
||||||
import org.apache.druid.java.util.http.client.response.StatusResponseHolder;
|
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.sql.avatica.DruidAvaticaJsonHandler;
|
||||||
import org.apache.druid.testing.IntegrationTestingConfig;
|
import org.apache.druid.testing.IntegrationTestingConfig;
|
||||||
import org.apache.druid.testing.clients.CoordinatorResourceTestClient;
|
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.HttpMethod;
|
||||||
import org.jboss.netty.handler.codec.http.HttpResponseStatus;
|
import org.jboss.netty.handler.codec.http.HttpResponseStatus;
|
||||||
import org.testng.Assert;
|
import org.testng.Assert;
|
||||||
|
import org.testng.annotations.Test;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
@ -50,19 +56,17 @@ import java.sql.Connection;
|
||||||
import java.sql.DriverManager;
|
import java.sql.DriverManager;
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.Statement;
|
import java.sql.Statement;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public abstract class AbstractAuthConfigurationTest
|
public abstract class AbstractAuthConfigurationTest
|
||||||
{
|
{
|
||||||
private static final Logger LOG = new Logger(AbstractAuthConfigurationTest.class);
|
private static final Logger LOG = new Logger(AbstractAuthConfigurationTest.class);
|
||||||
|
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>>>()
|
|
||||||
{
|
|
||||||
};
|
|
||||||
|
|
||||||
static final String SYSTEM_SCHEMA_SEGMENTS_RESULTS_RESOURCE =
|
static final String SYSTEM_SCHEMA_SEGMENTS_RESULTS_RESOURCE =
|
||||||
"/results/auth_test_sys_schema_segments.json";
|
"/results/auth_test_sys_schema_segments.json";
|
||||||
|
@ -85,7 +89,84 @@ public abstract class AbstractAuthConfigurationTest
|
||||||
static final String SYS_SCHEMA_TASKS_QUERY =
|
static final String SYS_SCHEMA_TASKS_QUERY =
|
||||||
"SELECT * FROM sys.tasks WHERE datasource IN ('auth_test')";
|
"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>> adminSegments;
|
||||||
List<Map<String, Object>> adminTasks;
|
List<Map<String, Object>> adminTasks;
|
||||||
|
@ -107,11 +188,323 @@ public abstract class AbstractAuthConfigurationTest
|
||||||
|
|
||||||
HttpClient adminClient;
|
HttpClient adminClient;
|
||||||
HttpClient datasourceOnlyUserClient;
|
HttpClient datasourceOnlyUserClient;
|
||||||
|
HttpClient datasourceAndSysUserClient;
|
||||||
HttpClient datasourceWithStateUserClient;
|
HttpClient datasourceWithStateUserClient;
|
||||||
HttpClient stateOnlyUserClient;
|
HttpClient stateOnlyUserClient;
|
||||||
HttpClient internalSystemClient;
|
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)
|
void checkNodeAccess(HttpClient httpClient)
|
||||||
{
|
{
|
||||||
HttpUtil.makeRequest(httpClient, HttpMethod.GET, config.getCoordinatorUrl() + "/status", null);
|
HttpUtil.makeRequest(httpClient, HttpMethod.GET, config.getCoordinatorUrl() + "/status", null);
|
||||||
|
@ -302,7 +695,7 @@ public abstract class AbstractAuthConfigurationTest
|
||||||
testOptionsRequests(adminClient);
|
testOptionsRequests(adminClient);
|
||||||
}
|
}
|
||||||
|
|
||||||
void verifyAuthenticatioInvalidAuthNameFails()
|
void verifyAuthenticationInvalidAuthNameFails()
|
||||||
{
|
{
|
||||||
verifyInvalidAuthNameFails(StringUtils.format(
|
verifyInvalidAuthNameFails(StringUtils.format(
|
||||||
"%s/druid-ext/basic-security/authentication/listen/%s",
|
"%s/druid-ext/basic-security/authentication/listen/%s",
|
||||||
|
@ -370,7 +763,6 @@ public abstract class AbstractAuthConfigurationTest
|
||||||
setupTestSpecificHttpClients();
|
setupTestSpecificHttpClients();
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract void setupUsers() throws Exception;
|
|
||||||
|
|
||||||
void setupCommonHttpClients()
|
void setupCommonHttpClients()
|
||||||
{
|
{
|
||||||
|
@ -384,6 +776,11 @@ public abstract class AbstractAuthConfigurationTest
|
||||||
httpClient
|
httpClient
|
||||||
);
|
);
|
||||||
|
|
||||||
|
datasourceAndSysUserClient = new CredentialedHttpClient(
|
||||||
|
new BasicCredentials("datasourceAndSysUser", "helloworld"),
|
||||||
|
httpClient
|
||||||
|
);
|
||||||
|
|
||||||
datasourceWithStateUserClient = new CredentialedHttpClient(
|
datasourceWithStateUserClient = new CredentialedHttpClient(
|
||||||
new BasicCredentials("datasourceWithStateUser", "helloworld"),
|
new BasicCredentials("datasourceWithStateUser", "helloworld"),
|
||||||
httpClient
|
httpClient
|
||||||
|
@ -400,8 +797,6 @@ public abstract class AbstractAuthConfigurationTest
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract void setupTestSpecificHttpClients() throws Exception;
|
|
||||||
|
|
||||||
void setExpectedSystemSchemaObjects() throws IOException
|
void setExpectedSystemSchemaObjects() throws IOException
|
||||||
{
|
{
|
||||||
// initial setup is done now, run the system schema response content tests
|
// 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()));
|
json = StringUtils.replace(json, "%%NON_LEADER%%", String.valueOf(NullHandling.defaultLongValue()));
|
||||||
return json;
|
return json;
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract String getAuthenticatorName();
|
|
||||||
|
|
||||||
abstract String getAuthorizerName();
|
|
||||||
|
|
||||||
abstract String getExpectedAvaticaAuthError();
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,30 +19,23 @@
|
||||||
|
|
||||||
package org.apache.druid.tests.security;
|
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.StringUtils;
|
||||||
import org.apache.druid.java.util.common.logger.Logger;
|
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.CredentialedHttpClient;
|
||||||
import org.apache.druid.java.util.http.client.HttpClient;
|
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.auth.BasicCredentials;
|
||||||
import org.apache.druid.security.basic.authentication.entity.BasicAuthenticatorCredentialUpdate;
|
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.ResourceAction;
|
||||||
import org.apache.druid.server.security.ResourceType;
|
|
||||||
import org.apache.druid.testing.guice.DruidTestModuleFactory;
|
import org.apache.druid.testing.guice.DruidTestModuleFactory;
|
||||||
import org.apache.druid.testing.utils.HttpUtil;
|
import org.apache.druid.testing.utils.HttpUtil;
|
||||||
import org.apache.druid.testing.utils.ITRetryUtil;
|
import org.apache.druid.testing.utils.ITRetryUtil;
|
||||||
import org.apache.druid.tests.TestNGGroup;
|
import org.apache.druid.tests.TestNGGroup;
|
||||||
import org.jboss.netty.handler.codec.http.HttpMethod;
|
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.BeforeClass;
|
||||||
import org.testng.annotations.Guice;
|
import org.testng.annotations.Guice;
|
||||||
import org.testng.annotations.Test;
|
import org.testng.annotations.Test;
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
@Test(groups = TestNGGroup.SECURITY)
|
@Test(groups = TestNGGroup.SECURITY)
|
||||||
@Guice(moduleFactory = DruidTestModuleFactory.class)
|
@Guice(moduleFactory = DruidTestModuleFactory.class)
|
||||||
|
@ -65,313 +58,61 @@ public class ITBasicAuthConfigurationTest extends AbstractAuthConfigurationTest
|
||||||
() -> coordinatorClient.areSegmentsLoaded("auth_test"), "auth_test segment load"
|
() -> coordinatorClient.areSegmentsLoaded("auth_test"), "auth_test segment load"
|
||||||
);
|
);
|
||||||
|
|
||||||
setupHttpClients();
|
setupHttpClientsAndUsers();
|
||||||
setupUsers();
|
|
||||||
setExpectedSystemSchemaObjects();
|
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
|
@Test
|
||||||
public void test_druid99User_hasNodeAccess()
|
public void test_druid99User_hasNodeAccess()
|
||||||
{
|
{
|
||||||
checkNodeAccess(druid99);
|
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
|
@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(
|
createUserAndRoleWithPermissions(
|
||||||
adminClient,
|
adminClient,
|
||||||
"datasourceOnlyUser",
|
"datasourceOnlyUser",
|
||||||
"helloworld",
|
"helloworld",
|
||||||
"datasourceOnlyRole",
|
"datasourceOnlyRole",
|
||||||
readDatasourceOnlyPermissions
|
DATASOURCE_ONLY_PERMISSIONS
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// create a new user+role that can only read 'auth_test' + STATE read access
|
@Override
|
||||||
List<ResourceAction> readDatasourceWithStatePermissions = ImmutableList.of(
|
void setupDatasourceAndSysTableUser() throws Exception
|
||||||
new ResourceAction(
|
{
|
||||||
new Resource("auth_test", ResourceType.DATASOURCE),
|
createUserAndRoleWithPermissions(
|
||||||
Action.READ
|
adminClient,
|
||||||
),
|
"datasourceAndSysUser",
|
||||||
new ResourceAction(
|
"helloworld",
|
||||||
new Resource(".*", ResourceType.STATE),
|
"datasourceAndSysRole",
|
||||||
Action.READ
|
DATASOURCE_SYS_PERMISSIONS
|
||||||
)
|
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void setupDatasourceAndSysAndStateUser() throws Exception
|
||||||
|
{
|
||||||
createUserAndRoleWithPermissions(
|
createUserAndRoleWithPermissions(
|
||||||
adminClient,
|
adminClient,
|
||||||
"datasourceWithStateUser",
|
"datasourceWithStateUser",
|
||||||
"helloworld",
|
"helloworld",
|
||||||
"datasourceWithStateRole",
|
"datasourceWithStateRole",
|
||||||
readDatasourceWithStatePermissions
|
DATASOURCE_SYS_STATE_PERMISSIONS
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// create a new user+role with only STATE read access
|
@Override
|
||||||
List<ResourceAction> stateOnlyPermissions = ImmutableList.of(
|
void setupSysTableAndStateOnlyUser() throws Exception
|
||||||
new ResourceAction(
|
{
|
||||||
new Resource(".*", ResourceType.STATE),
|
|
||||||
Action.READ
|
|
||||||
)
|
|
||||||
);
|
|
||||||
createUserAndRoleWithPermissions(
|
createUserAndRoleWithPermissions(
|
||||||
adminClient,
|
adminClient,
|
||||||
"stateOnlyUser",
|
"stateOnlyUser",
|
||||||
"helloworld",
|
"helloworld",
|
||||||
"stateOnlyRole",
|
"stateOnlyRole",
|
||||||
stateOnlyPermissions
|
STATE_ONLY_PERMISSIONS
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -379,18 +120,12 @@ public class ITBasicAuthConfigurationTest extends AbstractAuthConfigurationTest
|
||||||
void setupTestSpecificHttpClients() throws Exception
|
void setupTestSpecificHttpClients() throws Exception
|
||||||
{
|
{
|
||||||
// create a new user+role that can read /status
|
// create a new user+role that can read /status
|
||||||
List<ResourceAction> permissions = Collections.singletonList(
|
|
||||||
new ResourceAction(
|
|
||||||
new Resource(".*", ResourceType.STATE),
|
|
||||||
Action.READ
|
|
||||||
)
|
|
||||||
);
|
|
||||||
createUserAndRoleWithPermissions(
|
createUserAndRoleWithPermissions(
|
||||||
adminClient,
|
adminClient,
|
||||||
"druid",
|
"druid",
|
||||||
"helloworld",
|
"helloworld",
|
||||||
"druidrole",
|
"druidrole",
|
||||||
permissions
|
STATE_ONLY_PERMISSIONS
|
||||||
);
|
);
|
||||||
|
|
||||||
// create 100 users
|
// create 100 users
|
||||||
|
|
|
@ -20,7 +20,6 @@
|
||||||
package org.apache.druid.tests.security;
|
package org.apache.druid.tests.security;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.google.common.collect.ImmutableList;
|
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import org.apache.druid.java.util.common.StringUtils;
|
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.HttpClient;
|
||||||
import org.apache.druid.java.util.http.client.auth.BasicCredentials;
|
import org.apache.druid.java.util.http.client.auth.BasicCredentials;
|
||||||
import org.apache.druid.security.basic.authorization.entity.BasicAuthorizerGroupMapping;
|
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.ResourceAction;
|
||||||
import org.apache.druid.server.security.ResourceType;
|
|
||||||
import org.apache.druid.testing.IntegrationTestingConfig;
|
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.guice.DruidTestModuleFactory;
|
||||||
import org.apache.druid.testing.utils.HttpUtil;
|
import org.apache.druid.testing.utils.HttpUtil;
|
||||||
import org.apache.druid.testing.utils.ITRetryUtil;
|
import org.apache.druid.testing.utils.ITRetryUtil;
|
||||||
import org.apache.druid.tests.TestNGGroup;
|
import org.apache.druid.tests.TestNGGroup;
|
||||||
import org.jboss.netty.handler.codec.http.HttpMethod;
|
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.BeforeClass;
|
||||||
import org.testng.annotations.Guice;
|
import org.testng.annotations.Guice;
|
||||||
import org.testng.annotations.Test;
|
import org.testng.annotations.Test;
|
||||||
|
@ -48,7 +42,6 @@ import org.testng.annotations.Test;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
@Test(groups = TestNGGroup.LDAP_SECURITY)
|
@Test(groups = TestNGGroup.LDAP_SECURITY)
|
||||||
@Guice(moduleFactory = DruidTestModuleFactory.class)
|
@Guice(moduleFactory = DruidTestModuleFactory.class)
|
||||||
|
@ -67,8 +60,6 @@ public class ITBasicAuthLdapConfigurationTest extends AbstractAuthConfigurationT
|
||||||
@Inject
|
@Inject
|
||||||
ObjectMapper jsonMapper;
|
ObjectMapper jsonMapper;
|
||||||
|
|
||||||
@Inject
|
|
||||||
private CoordinatorResourceTestClient coordinatorClient;
|
|
||||||
|
|
||||||
private HttpClient druidUserClient;
|
private HttpClient druidUserClient;
|
||||||
private HttpClient stateOnlyNoLdapGroupUserClient;
|
private HttpClient stateOnlyNoLdapGroupUserClient;
|
||||||
|
@ -81,177 +72,10 @@ public class ITBasicAuthLdapConfigurationTest extends AbstractAuthConfigurationT
|
||||||
() -> coordinatorClient.areSegmentsLoaded("auth_test"), "auth_test segment load"
|
() -> coordinatorClient.areSegmentsLoaded("auth_test"), "auth_test segment load"
|
||||||
);
|
);
|
||||||
|
|
||||||
setupHttpClients();
|
setupHttpClientsAndUsers();
|
||||||
setupUsers();
|
|
||||||
setExpectedSystemSchemaObjects();
|
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
|
@Test
|
||||||
public void test_systemSchemaAccess_stateOnlyNoLdapGroupUser() throws Exception
|
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
|
@Test
|
||||||
public void test_druidUser_hasNodeAccess()
|
public void test_druidUser_hasNodeAccess()
|
||||||
{
|
{
|
||||||
checkNodeAccess(druidUserClient);
|
checkNodeAccess(druidUserClient);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void test_avaticaQuery_broker()
|
|
||||||
{
|
|
||||||
testAvaticaQuery(getBrokerAvacticaUrl());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Override
|
||||||
public void test_avaticaQuery_router()
|
void setupDatasourceOnlyUser() throws Exception
|
||||||
{
|
{
|
||||||
testAvaticaQuery(getRouterAvacticaUrl());
|
createRoleWithPermissionsAndGroupMapping(
|
||||||
}
|
"datasourceOnlyGroup",
|
||||||
|
ImmutableMap.of("datasourceOnlyRole", DATASOURCE_ONLY_PERMISSIONS)
|
||||||
@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
|
@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(
|
createRoleWithPermissionsAndGroupMapping(
|
||||||
"datasourceOnlyGroup",
|
"datasourceWithSysGroup",
|
||||||
ImmutableMap.of("datasourceOnlyRole", readDatasourceOnlyPermissions)
|
ImmutableMap.of("datasourceWithSysRole", DATASOURCE_SYS_PERMISSIONS)
|
||||||
);
|
|
||||||
|
|
||||||
// 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
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void setupDatasourceAndSysAndStateUser() throws Exception
|
||||||
|
{
|
||||||
createRoleWithPermissionsAndGroupMapping(
|
createRoleWithPermissionsAndGroupMapping(
|
||||||
"datasourceWithStateGroup",
|
"datasourceWithStateGroup",
|
||||||
ImmutableMap.of("datasourceWithStateRole", readDatasourceWithStatePermissions)
|
ImmutableMap.of("datasourceWithStateRole", DATASOURCE_SYS_STATE_PERMISSIONS)
|
||||||
);
|
|
||||||
|
|
||||||
// create a new role with only STATE read access
|
|
||||||
List<ResourceAction> stateOnlyPermissions = ImmutableList.of(
|
|
||||||
new ResourceAction(
|
|
||||||
new Resource(".*", ResourceType.STATE),
|
|
||||||
Action.READ
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void setupSysTableAndStateOnlyUser() throws Exception
|
||||||
|
{
|
||||||
createRoleWithPermissionsAndGroupMapping(
|
createRoleWithPermissionsAndGroupMapping(
|
||||||
"stateOnlyGroup",
|
"stateOnlyGroup",
|
||||||
ImmutableMap.of("stateOnlyRole", stateOnlyPermissions)
|
ImmutableMap.of("stateOnlyRole", STATE_ONLY_PERMISSIONS)
|
||||||
);
|
);
|
||||||
|
|
||||||
// create a role that can read /status
|
// create a role that can read /status
|
||||||
createRoleWithPermissionsAndGroupMapping(
|
createRoleWithPermissionsAndGroupMapping(
|
||||||
"druidGroup",
|
"druidGroup",
|
||||||
ImmutableMap.of("druidrole", stateOnlyPermissions)
|
ImmutableMap.of("druidrole", STATE_ONLY_PERMISSIONS)
|
||||||
);
|
);
|
||||||
|
|
||||||
assignUserToRole("stateOnlyNoLdapGroup", "stateOnlyRole");
|
assignUserToRole("stateOnlyNoLdapGroup", "stateOnlyRole");
|
||||||
|
|
|
@ -32,6 +32,7 @@ public class ResourceType
|
||||||
public static final String VIEW = "VIEW";
|
public static final String VIEW = "VIEW";
|
||||||
public static final String CONFIG = "CONFIG";
|
public static final String CONFIG = "CONFIG";
|
||||||
public static final String STATE = "STATE";
|
public static final String STATE = "STATE";
|
||||||
|
public static final String SYSTEM_TABLE = "SYSTEM_TABLE";
|
||||||
|
|
||||||
private static final Set<String> KNOWN_TYPES = Sets.newConcurrentHashSet();
|
private static final Set<String> KNOWN_TYPES = Sets.newConcurrentHashSet();
|
||||||
|
|
||||||
|
@ -41,6 +42,7 @@ public class ResourceType
|
||||||
registerResourceType(VIEW);
|
registerResourceType(VIEW);
|
||||||
registerResourceType(CONFIG);
|
registerResourceType(CONFIG);
|
||||||
registerResourceType(STATE);
|
registerResourceType(STATE);
|
||||||
|
registerResourceType(SYSTEM_TABLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -67,6 +67,9 @@ public class PlannerConfig
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
private boolean computeInnerJoinCostAsFilter = true;
|
private boolean computeInnerJoinCostAsFilter = true;
|
||||||
|
|
||||||
|
@JsonProperty
|
||||||
|
private boolean authorizeSystemTablesDirectly = false;
|
||||||
|
|
||||||
public long getMetadataSegmentPollPeriod()
|
public long getMetadataSegmentPollPeriod()
|
||||||
{
|
{
|
||||||
return metadataSegmentPollPeriod;
|
return metadataSegmentPollPeriod;
|
||||||
|
@ -129,6 +132,11 @@ public class PlannerConfig
|
||||||
return computeInnerJoinCostAsFilter;
|
return computeInnerJoinCostAsFilter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isAuthorizeSystemTablesDirectly()
|
||||||
|
{
|
||||||
|
return authorizeSystemTablesDirectly;
|
||||||
|
}
|
||||||
|
|
||||||
public PlannerConfig withOverrides(final Map<String, Object> context)
|
public PlannerConfig withOverrides(final Map<String, Object> context)
|
||||||
{
|
{
|
||||||
if (context == null) {
|
if (context == null) {
|
||||||
|
@ -153,15 +161,18 @@ public class PlannerConfig
|
||||||
CTX_KEY_USE_APPROXIMATE_TOPN,
|
CTX_KEY_USE_APPROXIMATE_TOPN,
|
||||||
isUseApproximateTopN()
|
isUseApproximateTopN()
|
||||||
);
|
);
|
||||||
|
newConfig.computeInnerJoinCostAsFilter = getContextBoolean(
|
||||||
|
context,
|
||||||
|
CTX_COMPUTE_INNER_JOIN_COST_AS_FILTER,
|
||||||
|
computeInnerJoinCostAsFilter
|
||||||
|
);
|
||||||
newConfig.requireTimeCondition = isRequireTimeCondition();
|
newConfig.requireTimeCondition = isRequireTimeCondition();
|
||||||
newConfig.sqlTimeZone = getSqlTimeZone();
|
newConfig.sqlTimeZone = getSqlTimeZone();
|
||||||
newConfig.awaitInitializationOnStart = isAwaitInitializationOnStart();
|
newConfig.awaitInitializationOnStart = isAwaitInitializationOnStart();
|
||||||
newConfig.metadataSegmentCacheEnable = isMetadataSegmentCacheEnable();
|
newConfig.metadataSegmentCacheEnable = isMetadataSegmentCacheEnable();
|
||||||
newConfig.metadataSegmentPollPeriod = getMetadataSegmentPollPeriod();
|
newConfig.metadataSegmentPollPeriod = getMetadataSegmentPollPeriod();
|
||||||
newConfig.serializeComplexValues = shouldSerializeComplexValues();
|
newConfig.serializeComplexValues = shouldSerializeComplexValues();
|
||||||
newConfig.computeInnerJoinCostAsFilter = getContextBoolean(context,
|
newConfig.authorizeSystemTablesDirectly = isAuthorizeSystemTablesDirectly();
|
||||||
CTX_COMPUTE_INNER_JOIN_COST_AS_FILTER,
|
|
||||||
computeInnerJoinCostAsFilter);
|
|
||||||
return newConfig;
|
return newConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,10 @@ package org.apache.druid.sql.calcite.schema;
|
||||||
|
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import org.apache.calcite.schema.Schema;
|
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.
|
* 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";
|
public static final String NAME = "sys";
|
||||||
|
|
||||||
private final SystemSchema systemSchema;
|
private final SystemSchema systemSchema;
|
||||||
|
private final PlannerConfig plannerConfig;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public NamedSystemSchema(SystemSchema systemSchema)
|
public NamedSystemSchema(PlannerConfig plannerConfig, SystemSchema systemSchema)
|
||||||
{
|
{
|
||||||
|
this.plannerConfig = plannerConfig;
|
||||||
this.systemSchema = systemSchema;
|
this.systemSchema = systemSchema;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,4 +54,14 @@ public class NamedSystemSchema implements NamedSchema
|
||||||
{
|
{
|
||||||
return systemSchema;
|
return systemSchema;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public String getSchemaResourceType(String resourceName)
|
||||||
|
{
|
||||||
|
if (plannerConfig.isAuthorizeSystemTablesDirectly()) {
|
||||||
|
return ResourceType.SYSTEM_TABLE;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 DUMMY_SQL_ID = "dummy";
|
||||||
public static final String LOS_ANGELES = "America/Los_Angeles";
|
public static final String LOS_ANGELES = "America/Los_Angeles";
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ package org.apache.druid.sql.calcite;
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
import org.apache.druid.server.security.Resource;
|
import org.apache.druid.server.security.Resource;
|
||||||
import org.apache.druid.server.security.ResourceType;
|
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.apache.druid.sql.calcite.util.CalciteTests;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
@ -243,4 +244,39 @@ public class DruidPlannerResourceAnalyzeTest extends BaseCalciteQueryTest
|
||||||
requiredResources
|
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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,10 @@
|
||||||
|
|
||||||
package org.apache.druid.sql.calcite.schema;
|
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.apache.druid.sql.calcite.util.CalciteTestBase;
|
||||||
|
import org.easymock.EasyMock;
|
||||||
import org.easymock.Mock;
|
import org.easymock.Mock;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
@ -32,12 +35,15 @@ public class NamedSystemSchemaTest extends CalciteTestBase
|
||||||
@Mock
|
@Mock
|
||||||
private SystemSchema systemSchema;
|
private SystemSchema systemSchema;
|
||||||
|
|
||||||
|
private PlannerConfig plannerConfig;
|
||||||
|
|
||||||
private NamedSystemSchema target;
|
private NamedSystemSchema target;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp()
|
public void setUp()
|
||||||
{
|
{
|
||||||
target = new NamedSystemSchema(systemSchema);
|
plannerConfig = EasyMock.createMock(PlannerConfig.class);
|
||||||
|
target = new NamedSystemSchema(plannerConfig, systemSchema);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -51,4 +57,22 @@ public class NamedSystemSchemaTest extends CalciteTestBase
|
||||||
{
|
{
|
||||||
Assert.assertEquals(systemSchema, target.getSchema());
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1140,7 +1140,7 @@ public class CalciteTests
|
||||||
SchemaPlus rootSchema = CalciteSchema.createRootSchema(false, false).plus();
|
SchemaPlus rootSchema = CalciteSchema.createRootSchema(false, false).plus();
|
||||||
Set<NamedSchema> namedSchemas = ImmutableSet.of(
|
Set<NamedSchema> namedSchemas = ImmutableSet.of(
|
||||||
new NamedDruidSchema(druidSchema, CalciteTests.DRUID_SCHEMA_NAME),
|
new NamedDruidSchema(druidSchema, CalciteTests.DRUID_SCHEMA_NAME),
|
||||||
new NamedSystemSchema(systemSchema),
|
new NamedSystemSchema(plannerConfig, systemSchema),
|
||||||
new NamedLookupSchema(lookupSchema)
|
new NamedLookupSchema(lookupSchema)
|
||||||
);
|
);
|
||||||
DruidSchemaCatalog catalog = new DruidSchemaCatalog(
|
DruidSchemaCatalog catalog = new DruidSchemaCatalog(
|
||||||
|
@ -1178,7 +1178,7 @@ public class CalciteTests
|
||||||
SchemaPlus rootSchema = CalciteSchema.createRootSchema(false, false).plus();
|
SchemaPlus rootSchema = CalciteSchema.createRootSchema(false, false).plus();
|
||||||
Set<NamedSchema> namedSchemas = ImmutableSet.of(
|
Set<NamedSchema> namedSchemas = ImmutableSet.of(
|
||||||
new NamedDruidSchema(druidSchema, CalciteTests.DRUID_SCHEMA_NAME),
|
new NamedDruidSchema(druidSchema, CalciteTests.DRUID_SCHEMA_NAME),
|
||||||
new NamedSystemSchema(systemSchema),
|
new NamedSystemSchema(plannerConfig, systemSchema),
|
||||||
new NamedLookupSchema(lookupSchema),
|
new NamedLookupSchema(lookupSchema),
|
||||||
new NamedViewSchema(viewSchema)
|
new NamedViewSchema(viewSchema)
|
||||||
);
|
);
|
||||||
|
|
|
@ -180,6 +180,7 @@ SqlFirehose
|
||||||
SqlParameter
|
SqlParameter
|
||||||
SslContextFactory
|
SslContextFactory
|
||||||
StatsD
|
StatsD
|
||||||
|
SYSTEM_TABLE
|
||||||
TCP
|
TCP
|
||||||
TGT
|
TGT
|
||||||
TLS
|
TLS
|
||||||
|
|
Loading…
Reference in New Issue