Skip to content

Commit 708849c

Browse files
IGNITE-26838 Compatibility tests security (#6851)
1 parent b11d040 commit 708849c

File tree

3 files changed

+102
-20
lines changed

3 files changed

+102
-20
lines changed

modules/compatibility-tests/build.gradle

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,11 @@ dependencies {
5353
integrationTestImplementation project(':ignite-rest-api')
5454
integrationTestImplementation project(':ignite-metastorage-api')
5555
integrationTestImplementation project(':ignite-metastorage')
56+
integrationTestImplementation project(':ignite-jdbc')
57+
integrationTestImplementation project(':ignite-network-api')
5658
integrationTestAnnotationProcessor libs.micronaut.inject.annotation.processor
5759

60+
integrationTestImplementation libs.awaitility
5861
integrationTestImplementation libs.micronaut.junit5
5962
integrationTestImplementation libs.micronaut.http.client
6063

@@ -70,7 +73,7 @@ dependencies {
7073
testFixturesImplementation libs.cytodynamics.nucleus
7174
testFixturesImplementation libs.japicmp
7275
testFixturesImplementation libs.micronaut.http.client
73-
76+
testFixturesImplementation libs.typesafe.config
7477

7578
testFixturesImplementation testFixtures(project(':ignite-core'))
7679
testFixturesImplementation testFixtures(project(':ignite-runner'))

modules/compatibility-tests/src/testFixtures/java/org/apache/ignite/internal/IgniteCluster.java

Lines changed: 97 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,14 @@
3030
import static org.apache.ignite.internal.util.CollectionUtils.setListAtIndex;
3131
import static org.awaitility.Awaitility.await;
3232
import static org.hamcrest.MatcherAssert.assertThat;
33+
import static org.hamcrest.Matchers.anyOf;
3334
import static org.hamcrest.Matchers.equalTo;
3435
import static org.hamcrest.Matchers.is;
3536

3637
import com.fasterxml.jackson.core.JsonProcessingException;
3738
import com.fasterxml.jackson.databind.ObjectMapper;
39+
import com.typesafe.config.Config;
40+
import com.typesafe.config.ConfigFactory;
3841
import java.io.File;
3942
import java.io.IOException;
4043
import java.net.URI;
@@ -47,6 +50,7 @@
4750
import java.nio.file.Path;
4851
import java.util.ArrayList;
4952
import java.util.Arrays;
53+
import java.util.Base64;
5054
import java.util.Collections;
5155
import java.util.HashSet;
5256
import java.util.List;
@@ -61,7 +65,9 @@
6165
import org.apache.ignite.IgniteServer;
6266
import org.apache.ignite.InitParameters;
6367
import org.apache.ignite.InitParametersBuilder;
68+
import org.apache.ignite.client.BasicAuthenticator;
6469
import org.apache.ignite.client.IgniteClient;
70+
import org.apache.ignite.client.IgniteClientAuthenticator;
6571
import org.apache.ignite.internal.Cluster.ServerRegistration;
6672
import org.apache.ignite.internal.logger.IgniteLogger;
6773
import org.apache.ignite.internal.logger.Loggers;
@@ -94,6 +100,7 @@ public class IgniteCluster {
94100
private volatile boolean stopped = false;
95101

96102
private final ClusterConfiguration clusterConfiguration;
103+
private @Nullable IgniteClientAuthenticator authenticator;
97104

98105
IgniteCluster(ClusterConfiguration clusterConfiguration) {
99106
this.clusterConfiguration = clusterConfiguration;
@@ -236,36 +243,29 @@ public void initEmbedded(List<ServerRegistration> nodeRegistrations, Consumer<In
236243
* Initializes the cluster using REST API on the first node with default settings.
237244
*/
238245
public void init(Consumer<InitParametersBuilder> initParametersConfigurator) {
239-
init(new int[] { 0 }, initParametersConfigurator);
246+
int[] cmgNodes = { 0 };
247+
InitParameters initParameters = initParameters(cmgNodes, initParametersConfigurator);
248+
249+
authenticator = authenticator(initParameters);
250+
251+
init(initParameters);
240252
}
241253

242254
/**
243255
* Initializes the cluster using REST API on the first node with specified Metastorage and CMG nodes.
244-
*
245-
* @param cmgNodes Indices of the CMG nodes (also used as Metastorage group).
246256
*/
247-
void init(int[] cmgNodes, Consumer<InitParametersBuilder> initParametersConfigurator) {
257+
private void init(InitParameters initParameters) {
248258
// Wait for the node to start accepting requests
249259
await()
250260
.ignoreExceptions()
251261
.timeout(30, TimeUnit.SECONDS)
252262
.until(
253263
() -> send(get("/management/v1/node/state")).body(),
254-
hasJsonPath("$.state", is(equalTo("STARTING")))
264+
hasJsonPath("$.state", anyOf(equalTo("STARTING"), equalTo("STARTED")))
255265
);
256266

257267
// Initialize the cluster
258-
List<String> metaStorageAndCmgNodes = Arrays.stream(cmgNodes)
259-
.mapToObj(this::nodeName)
260-
.collect(toList());
261-
262-
InitParametersBuilder builder = InitParameters.builder()
263-
.metaStorageNodeNames(metaStorageAndCmgNodes)
264-
.clusterName(clusterConfiguration.clusterName());
265-
266-
initParametersConfigurator.accept(builder);
267-
268-
sendInitRequest(builder.build());
268+
sendInitRequest(initParameters);
269269

270270
// Wait for the cluster to be initialized
271271
await()
@@ -280,6 +280,20 @@ void init(int[] cmgNodes, Consumer<InitParametersBuilder> initParametersConfigur
280280
stopped = false;
281281
}
282282

283+
private InitParameters initParameters(int[] cmgNodes, Consumer<InitParametersBuilder> initParametersConfigurator) {
284+
List<String> metaStorageAndCmgNodes = Arrays.stream(cmgNodes)
285+
.mapToObj(this::nodeName)
286+
.collect(toList());
287+
288+
InitParametersBuilder builder = InitParameters.builder()
289+
.metaStorageNodeNames(metaStorageAndCmgNodes)
290+
.clusterName(clusterConfiguration.clusterName());
291+
292+
initParametersConfigurator.accept(builder);
293+
294+
return builder.build();
295+
}
296+
283297
private void sendInitRequest(InitParameters initParameters) {
284298
ObjectMapper mapper = new ObjectMapper();
285299
String requestBody;
@@ -304,7 +318,17 @@ private void sendInitRequest(InitParameters initParameters) {
304318
* @return Ignite client instance.
305319
*/
306320
public IgniteClient createClient() {
307-
return IgniteClient.builder().addresses("localhost:" + clusterConfiguration.baseClientPort()).build();
321+
return createClient(authenticator);
322+
}
323+
324+
private IgniteClient createClient(@Nullable IgniteClientAuthenticator authenticator) {
325+
IgniteClient.Builder builder = IgniteClient.builder().addresses("localhost:" + clusterConfiguration.baseClientPort());
326+
327+
if (authenticator != null) {
328+
builder.authenticator(authenticator);
329+
}
330+
331+
return builder.build();
308332
}
309333

310334
/**
@@ -413,8 +437,26 @@ private HttpRequest get(String path) {
413437
return newBuilder(path).build();
414438
}
415439

440+
private HttpRequest get(String path, int nodeIndex) {
441+
return newBuilder(path, nodeIndex).build();
442+
}
443+
444+
private Builder newBuilder(String path, int nodeIndex) {
445+
Builder builder = HttpRequest.newBuilder(URI.create("http://localhost:" + port(nodeIndex) + path));
446+
447+
if (authenticator instanceof BasicAuthenticator) {
448+
builder.header("Authorization", basicAuthenticationHeader((BasicAuthenticator) authenticator));
449+
}
450+
451+
return builder;
452+
}
453+
416454
private Builder newBuilder(String path) {
417-
return HttpRequest.newBuilder(URI.create("http://localhost:" + clusterConfiguration.baseHttpPort() + path));
455+
return newBuilder(path, 0);
456+
}
457+
458+
private int port(int nodeIndex) {
459+
return clusterConfiguration.baseHttpPort() + nodeIndex;
418460
}
419461

420462
private HttpResponse<String> send(HttpRequest request) {
@@ -495,4 +537,41 @@ private static File getArgsFile(ProjectConnection connection, String igniteVersi
495537

496538
return constructArgFile(connection, dependenciesListNotation, false);
497539
}
540+
541+
private static String basicAuthenticationHeader(BasicAuthenticator authenticator) {
542+
String valueToEncode = authenticator.identity() + ":" + authenticator.secret();
543+
return "Basic " + Base64.getEncoder().encodeToString(valueToEncode.getBytes());
544+
}
545+
546+
/**
547+
* Parses the cluster configuration and returns {@link BasicAuthenticator} if there is a user with "system" role.
548+
*
549+
* @see ClusterSecurityConfigurationBuilder
550+
*/
551+
private static @Nullable IgniteClientAuthenticator authenticator(InitParameters initParameters) {
552+
if (initParameters.clusterConfiguration() == null) {
553+
return null;
554+
}
555+
556+
Config cfg = ConfigFactory.parseString(initParameters.clusterConfiguration());
557+
558+
if (!cfg.hasPath("ignite.security.enabled")
559+
|| !cfg.getBoolean("ignite.security.enabled")
560+
|| !cfg.hasPath("ignite.security.authentication.providers")) {
561+
return null;
562+
}
563+
564+
return cfg.getConfigList("ignite.security.authentication.providers")
565+
.stream()
566+
.filter(provider -> "basic".equalsIgnoreCase(provider.getString("type")))
567+
.flatMap(provider -> provider.getConfigList("users").stream())
568+
.filter(user -> user.getStringList("roles").contains("system"))
569+
.findAny()
570+
.map(user -> BasicAuthenticator.builder()
571+
.username(user.getString("username"))
572+
.password(user.getString("password"))
573+
.build()
574+
)
575+
.orElseThrow();
576+
}
498577
}

modules/core/src/testFixtures/java/org/apache/ignite/internal/testframework/IgniteTestUtils.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -354,7 +354,7 @@ public static Throwable assertThrowsWithCause(
354354
run.run();
355355
} catch (Throwable e) {
356356
if (!hasCause(e, cls, msg)) {
357-
fail("Exception is neither of a specified class, nor has a cause of the specified class: " + cls, e);
357+
fail("Expected exception not found in stacktrace. [class=" + cls.getName() + "; message='" + msg + "']", e);
358358
}
359359

360360
return e;

0 commit comments

Comments
 (0)