Skip to content

Commit b8417a5

Browse files
authored
Migrate revised security IT to embedded tests (#18358)
Main changes: - Move `ITSecurityBasicQuery` to `BasicAuthMsqTest` - Add new test `BasicAuthIndexingTest` which extends `IndexTaskTest` - Add `EmbeddedServiceClient` to allow creation of a custom client that can talk to various services in an embedded cluster Other changes: - Refactor `SecurityClient` to work with `EmbeddedServiceClient` - Simplify `CompactionResourceTestClient` to work with `EmbeddedServiceClient` - Bind annotated `ServiceClient` instances for Coordinator, Overlord and Broker in `ServiceClientModule` - Replace usages of `CoordinatorServiceClient` with `@Coordinator ServiceClient` - Add resource `MsqExportDirectory`
1 parent 949f67e commit b8417a5

File tree

39 files changed

+1047
-1233
lines changed

39 files changed

+1047
-1233
lines changed

.github/workflows/revised-its.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ jobs:
6767
fail-fast: false
6868
matrix:
6969
jdk: [17]
70-
it: [HighAvailability, MultiStageQuery, Catalog, BatchIndex, MultiStageQueryWithMM, InputSource, InputFormat, Security, Query, DruidExactCountBitmap]
70+
it: [HighAvailability, MultiStageQuery, Catalog, BatchIndex, MultiStageQueryWithMM, InputSource, InputFormat, Query, DruidExactCountBitmap]
7171
indexer: [middleManager]
7272
uses: ./.github/workflows/reusable-revised-its.yml
7373
if: ${{ needs.changes.outputs.core == 'true' || needs.changes.outputs.common-extensions == 'true' }}

embedded-tests/pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,12 @@
8686
<version>${project.parent.version}</version>
8787
<scope>test</scope>
8888
</dependency>
89+
<dependency>
90+
<groupId>org.apache.druid.extensions</groupId>
91+
<artifactId>druid-basic-security</artifactId>
92+
<version>${project.parent.version}</version>
93+
<scope>test</scope>
94+
</dependency>
8995
<dependency>
9096
<groupId>joda-time</groupId>
9197
<artifactId>joda-time</artifactId>
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.apache.druid.testing.embedded.auth;
21+
22+
import com.fasterxml.jackson.core.type.TypeReference;
23+
import org.apache.druid.rpc.RequestBuilder;
24+
import org.apache.druid.testing.embedded.EmbeddedDruidCluster;
25+
import org.apache.druid.testing.embedded.EmbeddedRouter;
26+
import org.apache.druid.testing.embedded.indexing.IndexTaskTest;
27+
import org.jboss.netty.handler.codec.http.HttpMethod;
28+
import org.junit.jupiter.api.Assertions;
29+
import org.junit.jupiter.api.Test;
30+
31+
import java.util.List;
32+
33+
public class BasicAuthIndexingTest extends IndexTaskTest
34+
{
35+
@Override
36+
public EmbeddedDruidCluster createCluster()
37+
{
38+
return EmbeddedDruidCluster
39+
.withEmbeddedDerbyAndZookeeper()
40+
.addResource(new EmbeddedBasicAuthResource())
41+
.useLatchableEmitter()
42+
.addServer(coordinator)
43+
.addServer(overlord)
44+
.addServer(indexer)
45+
.addServer(historical)
46+
.addServer(broker)
47+
.addServer(new EmbeddedRouter())
48+
.addCommonProperty("druid.indexer.autoscale.doAutoscale", "true");
49+
}
50+
51+
@Test
52+
public void test_getScalingStats_redirectFromCoordinatorToOverlord()
53+
{
54+
final List<Object> response = cluster.callApi().serviceClient().onLeaderCoordinator(
55+
mapper -> new RequestBuilder(HttpMethod.GET, "/druid/indexer/v1/scaling"),
56+
new TypeReference<>() {}
57+
);
58+
Assertions.assertNotNull(response);
59+
Assertions.assertTrue(response.isEmpty());
60+
}
61+
}

integration-tests-ex/cases/src/test/java/org/apache/druid/testsEx/auth/ITSecurityBasicQuery.java renamed to embedded-tests/src/test/java/org/apache/druid/testing/embedded/auth/BasicAuthMSQTest.java

Lines changed: 122 additions & 146 deletions
Large diffs are not rendered by default.
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.apache.druid.testing.embedded.auth;
21+
22+
import org.apache.druid.java.util.common.StringUtils;
23+
import org.apache.druid.security.basic.BasicSecurityDruidModule;
24+
import org.apache.druid.testing.embedded.EmbeddedDruidCluster;
25+
import org.apache.druid.testing.embedded.EmbeddedResource;
26+
27+
/**
28+
* Resource to enable the basic auth extension in embedded tests.
29+
*/
30+
public class EmbeddedBasicAuthResource implements EmbeddedResource
31+
{
32+
public static final String ADMIN_PASSWORD = "priest";
33+
public static final String SYSTEM_PASSWORD = "warlock";
34+
public static final String SYSTEM_USER = "druid_system";
35+
36+
private static final String AUTHORIZER_NAME = "basic";
37+
private static final String AUTHENTICATOR_NAME = "basic";
38+
39+
@Override
40+
public void start()
41+
{
42+
// Do nothing
43+
}
44+
45+
@Override
46+
public void onStarted(EmbeddedDruidCluster cluster)
47+
{
48+
cluster
49+
.addExtension(BasicSecurityDruidModule.class)
50+
.addCommonProperty("druid.auth.authenticatorChain", StringUtils.format("[\"%s\"]", AUTHENTICATOR_NAME))
51+
.addCommonProperty(authenticatorProp("type"), "basic")
52+
.addCommonProperty(authenticatorProp("initialAdminPassword"), ADMIN_PASSWORD)
53+
.addCommonProperty(authenticatorProp("initialInternalClientPassword"), SYSTEM_PASSWORD)
54+
.addCommonProperty(authenticatorProp("authorizerName"), AUTHORIZER_NAME)
55+
.addCommonProperty("druid.auth.authorizers", StringUtils.format("[\"%s\"]", AUTHORIZER_NAME))
56+
.addCommonProperty(authorizerProp("type"), "basic")
57+
.addCommonProperty(escalatorProp("type"), "basic")
58+
.addCommonProperty(escalatorProp("internalClientPassword"), SYSTEM_PASSWORD)
59+
.addCommonProperty(escalatorProp("internalClientUsername"), SYSTEM_USER)
60+
.addCommonProperty(escalatorProp("authorizerName"), AUTHORIZER_NAME);
61+
}
62+
63+
@Override
64+
public void stop()
65+
{
66+
// Do nothing
67+
}
68+
69+
private String authenticatorProp(String name)
70+
{
71+
return StringUtils.format("druid.auth.authenticator.%s.%s", AUTHENTICATOR_NAME, name);
72+
}
73+
74+
private String authorizerProp(String name)
75+
{
76+
return StringUtils.format("druid.auth.authorizer.%s.%s", AUTHORIZER_NAME, name);
77+
}
78+
79+
private String escalatorProp(String name)
80+
{
81+
return StringUtils.format("druid.escalator.%s", name);
82+
}
83+
}
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.apache.druid.testing.embedded.auth;
21+
22+
import com.fasterxml.jackson.databind.ObjectMapper;
23+
import org.apache.druid.java.util.common.StringUtils;
24+
import org.apache.druid.rpc.RequestBuilder;
25+
import org.apache.druid.server.security.ResourceAction;
26+
import org.apache.druid.testing.embedded.EmbeddedServiceClient;
27+
import org.jboss.netty.handler.codec.http.HttpMethod;
28+
29+
import java.util.List;
30+
import java.util.Map;
31+
import java.util.function.Function;
32+
33+
/**
34+
* Client to call various basic auth APIs on the Coordinator.
35+
*/
36+
public class SecurityClient
37+
{
38+
private static final String AUTHENTICATOR_URL = "/druid-ext/basic-security/authentication/db/basic";
39+
private static final String AUTHORIZER_URL = "/druid-ext/basic-security/authorization/db/basic";
40+
41+
private final EmbeddedServiceClient clients;
42+
43+
SecurityClient(EmbeddedServiceClient clients)
44+
{
45+
this.clients = clients;
46+
}
47+
48+
public void createAuthenticationUser(String username)
49+
{
50+
final RequestBuilder request = new RequestBuilder(
51+
HttpMethod.POST,
52+
StringUtils.format(
53+
"%s/users/%s",
54+
AUTHENTICATOR_URL,
55+
StringUtils.urlEncode(username)
56+
)
57+
);
58+
sendRequest(mapper -> request);
59+
}
60+
61+
public void deleteAuthenticationUser(String username)
62+
{
63+
final RequestBuilder request = new RequestBuilder(
64+
HttpMethod.DELETE,
65+
StringUtils.format(
66+
"%s/users/%s",
67+
AUTHENTICATOR_URL,
68+
StringUtils.urlEncode(username)
69+
)
70+
);
71+
sendRequest(mapper -> request);
72+
}
73+
74+
public void setUserPassword(String username, String password)
75+
{
76+
final RequestBuilder request = new RequestBuilder(
77+
HttpMethod.POST,
78+
StringUtils.format(
79+
"%s/users/%s/credentials",
80+
AUTHENTICATOR_URL,
81+
StringUtils.urlEncode(username)
82+
)
83+
);
84+
85+
sendRequest(mapper -> request.jsonContent(mapper, Map.of("password", password)));
86+
}
87+
88+
public void createAuthorizerUser(String username)
89+
{
90+
final RequestBuilder request = new RequestBuilder(
91+
HttpMethod.POST,
92+
StringUtils.format(
93+
"%s/users/%s",
94+
AUTHORIZER_URL,
95+
StringUtils.urlEncode(username)
96+
)
97+
);
98+
sendRequest(mapper -> request);
99+
}
100+
101+
public void deleteAuthorizerUser(String username)
102+
{
103+
final RequestBuilder request = new RequestBuilder(
104+
HttpMethod.DELETE,
105+
StringUtils.format(
106+
"%s/users/%s",
107+
AUTHORIZER_URL,
108+
StringUtils.urlEncode(username)
109+
)
110+
);
111+
sendRequest(mapper -> request);
112+
}
113+
114+
public void createAuthorizerRole(String role)
115+
{
116+
final RequestBuilder request = new RequestBuilder(
117+
HttpMethod.POST,
118+
StringUtils.format(
119+
"%s/roles/%s",
120+
AUTHORIZER_URL,
121+
StringUtils.urlEncode(role)
122+
)
123+
);
124+
sendRequest(mapper -> request);
125+
}
126+
127+
public void deleteAuthorizerRole(String role)
128+
{
129+
final RequestBuilder request = new RequestBuilder(
130+
HttpMethod.DELETE,
131+
StringUtils.format(
132+
"%s/roles/%s",
133+
AUTHORIZER_URL,
134+
StringUtils.urlEncode(role)
135+
)
136+
);
137+
sendRequest(mapper -> request);
138+
}
139+
140+
public void assignUserToRole(String user, String role)
141+
{
142+
final RequestBuilder request = new RequestBuilder(
143+
HttpMethod.POST,
144+
StringUtils.format(
145+
"%s/users/%s/roles/%s",
146+
AUTHORIZER_URL,
147+
StringUtils.urlEncode(user),
148+
StringUtils.urlEncode(role)
149+
)
150+
);
151+
sendRequest(mapper -> request);
152+
}
153+
154+
public void setPermissionsToRole(String role, List<ResourceAction> permissions)
155+
{
156+
final RequestBuilder request = new RequestBuilder(
157+
HttpMethod.POST,
158+
StringUtils.format(
159+
"%s/roles/%s/permissions/",
160+
AUTHORIZER_URL,
161+
StringUtils.urlEncode(role)
162+
)
163+
);
164+
sendRequest(mapper -> request.jsonContent(mapper, permissions));
165+
}
166+
167+
private void sendRequest(Function<ObjectMapper, RequestBuilder> request)
168+
{
169+
clients.onLeaderCoordinator(request, null);
170+
}
171+
}

0 commit comments

Comments
 (0)