This is an automated email from the ASF dual-hosted git repository.
apkhmv pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git
The following commit(s) were added to refs/heads/main by this push:
new 2b341b4fec IGNITE-20643 Migrate basic auth provider to users list
(#2921)
2b341b4fec is described below
commit 2b341b4fecd88886e6001401261b77e64bceb8e0
Author: Mikhail <[email protected]>
AuthorDate: Thu Dec 7 21:35:58 2023 +0300
IGNITE-20643 Migrate basic auth provider to users list (#2921)
---
.../configuration/ItConfigCommandTest.java | 14 +-
.../cluster-configuration-with-enabled-auth.conf | 27 +--
.../ignite/client/handler/ItClientHandlerTest.java | 3 +-
.../handler/ClientInboundMessageHandlerTest.java | 107 ++++-----
.../ignite/client/ClientAuthenticationTest.java | 14 +-
modules/distribution-zones/build.gradle | 1 +
...niteDistributionZoneManagerNodeRestartTest.java | 8 +-
.../ignite/jdbc/ItJdbcAuthenticationTest.java | 10 +-
.../rest/ItInitializedClusterRestTest.java | 2 +-
.../rest/authentication/ItAuthenticationTest.java | 265 +++++++--------------
.../runner/app/PlatformTestNodeRunner.java | 5 +-
.../AuthenticationProviderEqualityVerifier.java | 22 +-
.../authentication/AuthenticatorFactory.java | 8 +-
.../SecurityConfigurationModule.java | 15 +-
...cAuthenticationProviderConfigurationSchema.java | 16 +-
.../authentication/basic/BasicAuthenticator.java | 37 +--
...iderConfigurationSchema.java => BasicUser.java} | 35 +--
...hema.java => BasicUserConfigurationSchema.java} | 15 +-
.../AuthenticationProvidersValidatorImpl.java | 39 ++-
.../AuthenticationManagerImplTest.java | 10 +-
.../AuthenticationProvidersValidatorImplTest.java | 5 +-
.../SecurityConfigurationModuleTest.java | 13 +-
...henticationProviderConfigurationSchemaTest.java | 10 +-
.../basic/BasicAuthenticatorTest.java | 6 +-
24 files changed, 329 insertions(+), 358 deletions(-)
diff --git
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/configuration/ItConfigCommandTest.java
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/configuration/ItConfigCommandTest.java
index e7263f0df7..ff9453151a 100644
---
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/configuration/ItConfigCommandTest.java
+++
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/configuration/ItConfigCommandTest.java
@@ -119,7 +119,7 @@ class ItConfigCommandTest extends
CliCommandTestInitializedIntegrationBase {
@DisplayName("Should update config with key-value format when valid
cluster-endpoint-url is given")
void updateClusterConfigWithoutQuoting() {
execute("cluster", "config", "update", "--cluster-endpoint-url",
NODE_URL,
-
"security.authentication.providers.basic1={type=basic,username=asd,password=asadf}");
+
"security.authentication.providers.default={type=basic,users=[{username=asd,password=pass1}]}");
assertAll(
this::assertExitCodeIsZero,
@@ -130,7 +130,7 @@ class ItConfigCommandTest extends
CliCommandTestInitializedIntegrationBase {
//Emulate config with spaces
execute("cluster", "config", "update", "--cluster-endpoint-url",
NODE_URL,
- "security.authentication.providers.basic2", "=", "{",
"type=basic,", "username=asd,", "password=asadf}");
+ "security.authentication.providers.default", "=", "{",
"type=basic,", "users=[{", "username=asd,", "password=pass2}]}");
assertAll(
this::assertExitCodeIsZero,
@@ -144,7 +144,7 @@ class ItConfigCommandTest extends
CliCommandTestInitializedIntegrationBase {
void updateClusterWithQuotedArgs() {
//Emulate quoting config
execute("cluster", "config", "update", "--cluster-endpoint-url",
NODE_URL,
-
"\"security.authentication.providers.basic3={type=basic,username=asd,password=asadf}\"");
+
"\"security.authentication.providers.default={type=basic,users=[{username=asd,password=pass3}]}\"");
assertAll(
this::assertExitCodeIsZero,
@@ -154,7 +154,7 @@ class ItConfigCommandTest extends
CliCommandTestInitializedIntegrationBase {
//Emulate quoting config
execute("cluster", "config", "update", "--cluster-endpoint-url",
NODE_URL,
- "\"security.authentication.providers.basic4\"",
"\"={type=basic,username=asd,password=asadf}\"");
+ "\"security.authentication.providers.default\"",
"\"={type=basic,users=[{username=asd,password=pass4}]}\"");
assertAll(
this::assertExitCodeIsZero,
@@ -164,7 +164,7 @@ class ItConfigCommandTest extends
CliCommandTestInitializedIntegrationBase {
//Emulate quoting config
execute("cluster", "config", "update", "--cluster-endpoint-url",
NODE_URL,
- "security.authentication.providers.basic5",
"\"={type=basic,username=asd,password=asadf}\"");
+ "security.authentication.providers.default",
"\"={type=basic,users=[{username=asd,password=pass5}]}\"");
assertAll(
this::assertExitCodeIsZero,
@@ -177,7 +177,7 @@ class ItConfigCommandTest extends
CliCommandTestInitializedIntegrationBase {
@DisplayName("Test using arguments in parameters")
void useOptionsInArguments() {
execute("cluster", "config", "update", "--cluster-endpoint-url",
NODE_URL,
-
"security.authentication.providers.basic6={type=basic,username:", "--verbose,",
"password=--verbose}");
+
"security.authentication.providers.default={type=basic,users=[{username:",
"--verbose,", "password=--verbose}]}");
assertAll(
() -> assertExitCodeIs(2),
@@ -188,7 +188,7 @@ class ItConfigCommandTest extends
CliCommandTestInitializedIntegrationBase {
resetOutput();
execute("cluster", "config", "update", "--cluster-endpoint-url",
NODE_URL,
-
"\"security.authentication.providers.basic7={type=basic,username: --verbose,
password=--verbose}\"");
+
"\"security.authentication.providers.default={type=basic,users=[{username:
--verbose, password=--verbose}]}\"");
assertAll(
this::assertExitCodeIsZero,
diff --git
a/modules/cli/src/testFixtures/resources/cluster-configuration-with-enabled-auth.conf
b/modules/cli/src/testFixtures/resources/cluster-configuration-with-enabled-auth.conf
index f9461400ef..211f31d24d 100644
---
a/modules/cli/src/testFixtures/resources/cluster-configuration-with-enabled-auth.conf
+++
b/modules/cli/src/testFixtures/resources/cluster-configuration-with-enabled-auth.conf
@@ -1,19 +1,18 @@
security: {
enabled: true,
authentication: {
- providers: [
- {
- name: basic,
- type: basic,
- username: admin,
- password: password
- },
- {
- name: basic1,
- type: basic,
- username: admin1,
- password: password
- }
- ]
+ providers.default: {
+ type: basic,
+ users: [
+ {
+ username: admin,
+ password: password
+ },
+ {
+ username: admin1,
+ password: password
+ }
+ ]
+ }
}
}
diff --git
a/modules/client-handler/src/integrationTest/java/org/apache/ignite/client/handler/ItClientHandlerTest.java
b/modules/client-handler/src/integrationTest/java/org/apache/ignite/client/handler/ItClientHandlerTest.java
index 4d4adaae6f..288552f77b 100644
---
a/modules/client-handler/src/integrationTest/java/org/apache/ignite/client/handler/ItClientHandlerTest.java
+++
b/modules/client-handler/src/integrationTest/java/org/apache/ignite/client/handler/ItClientHandlerTest.java
@@ -460,8 +460,7 @@ public class ItClientHandlerTest extends
BaseIgniteAbstractTest {
change.changeEnabled(true);
change.changeAuthentication().changeProviders().create("basic",
authenticationProviderChange -> {
authenticationProviderChange.convert(BasicAuthenticationProviderChange.class)
- .changeUsername(username)
- .changePassword(password);
+ .changeUsers(users -> users.create(username, user ->
user.changePassword(password)));
});
}).join();
}
diff --git
a/modules/client-handler/src/test/java/org/apache/ignite/client/handler/ClientInboundMessageHandlerTest.java
b/modules/client-handler/src/test/java/org/apache/ignite/client/handler/ClientInboundMessageHandlerTest.java
index 595d4b4656..db97e65c78 100644
---
a/modules/client-handler/src/test/java/org/apache/ignite/client/handler/ClientInboundMessageHandlerTest.java
+++
b/modules/client-handler/src/test/java/org/apache/ignite/client/handler/ClientInboundMessageHandlerTest.java
@@ -17,7 +17,9 @@
package org.apache.ignite.client.handler;
+import static
org.apache.ignite.internal.testframework.matchers.CompletableFutureMatcher.willCompleteSuccessfully;
import static org.awaitility.Awaitility.await;
+import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doReturn;
@@ -69,6 +71,8 @@ import org.msgpack.core.MessagePack;
class ClientInboundMessageHandlerTest extends BaseIgniteAbstractTest {
private static final Duration TIMEOUT_OF_DURING = Duration.ofSeconds(2);
+ private static final String PROVIDER_NAME = "basic";
+
@InjectConfiguration
private ClientConnectorConfiguration configuration;
@@ -166,22 +170,20 @@ class ClientInboundMessageHandlerTest extends
BaseIgniteAbstractTest {
authenticationManager.listen(handler);
securityConfiguration.listen(authenticationManager);
- securityConfiguration.change(change -> {
+ CompletableFuture<Void> future = securityConfiguration.change(change
-> {
change.changeEnabled(true);
change.changeAuthentication(authChange -> {
authChange.changeProviders(providersChange -> {
- providersChange.create("basic", basicChange -> {
-
basicChange.convert(BasicAuthenticationProviderChange.class)
- .changeUsername("admin")
- .changePassword("password");
- }).create("basic1", basicChange -> {
+ providersChange.create(PROVIDER_NAME, basicChange -> {
basicChange.convert(BasicAuthenticationProviderChange.class)
- .changeUsername("admin1")
- .changePassword("password");
+ .changeUsers(users ->
+ users.create("admin", user ->
user.changePassword("password"))
+ .create("admin1", user ->
user.changePassword("password")));
});
});
});
- }).join();
+ });
+ assertThat(future, willCompleteSuccessfully());
handler.channelRegistered(ctx);
}
@@ -190,80 +192,49 @@ class ClientInboundMessageHandlerTest extends
BaseIgniteAbstractTest {
void disableAuthentication() throws IOException {
handshake();
- securityConfiguration.change(change -> {
+ CompletableFuture<Void> future = securityConfiguration.change(change
-> {
change.changeEnabled(false);
- }).join();
+ });
+ assertThat(future, willCompleteSuccessfully());
await().during(TIMEOUT_OF_DURING).untilAtomic(ctxClosed, is(false));
}
@Test
- void enableAuthentication() throws InterruptedException, IOException {
- securityConfiguration.change(change -> {
+ void enableAuthentication() throws IOException {
+ CompletableFuture<Void> future1 = securityConfiguration.change(change
-> {
change.changeEnabled(false);
- }).join();
-
- handshake();
-
- securityConfiguration.change(change -> {
- change.changeEnabled(true);
- }).join();
-
- await().untilAtomic(ctxClosed, is(true));
- }
+ });
+ assertThat(future1, willCompleteSuccessfully());
- @Test
- void changeCurrentProvider() throws IOException {
handshake();
- securityConfiguration.change(change -> {
+ CompletableFuture<Void> future = securityConfiguration.change(change
-> {
change.changeEnabled(true);
- change.changeAuthentication(authChange -> {
- authChange.changeProviders(providersChange -> {
- providersChange.update("basic", basicChange -> {
-
basicChange.convert(BasicAuthenticationProviderChange.class)
- .changeUsername("admin")
- .changePassword("new-password");
- });
- });
- });
- }).join();
+ });
+ assertThat(future, willCompleteSuccessfully());
await().untilAtomic(ctxClosed, is(true));
}
@Test
- void changeAnotherProvider() throws IOException {
+ void changeProvider() throws IOException {
handshake();
- securityConfiguration.change(change -> {
+ CompletableFuture<Void> future = securityConfiguration.change(change
-> {
change.changeEnabled(true);
change.changeAuthentication(authChange -> {
authChange.changeProviders(providersChange -> {
- providersChange.update("basic1", basicChange -> {
+ providersChange.update(PROVIDER_NAME, basicChange -> {
basicChange.convert(BasicAuthenticationProviderChange.class)
- .changeUsername("admin1")
- .changePassword("new-password");
+ .changeUsers(users ->
+ users.update("admin", user ->
user.changePassword("new-password"))
+ );
});
});
});
- }).join();
-
- await().during(TIMEOUT_OF_DURING).untilAtomic(ctxClosed, is(false));
- }
-
- @Test
- void deleteCurrentProvider() throws IOException {
- handshake();
-
- securityConfiguration.change(change -> {
- change.changeEnabled(true);
- change.changeAuthentication(authChange -> {
- authChange.changeProviders(providersChange -> {
- providersChange.delete("basic");
- });
- });
- }).join();
+ });
+ assertThat(future, willCompleteSuccessfully());
await().untilAtomic(ctxClosed, is(true));
}
@@ -272,34 +243,38 @@ class ClientInboundMessageHandlerTest extends
BaseIgniteAbstractTest {
void deleteAnotherProvider() throws IOException {
handshake();
- securityConfiguration.change(change -> {
+ CompletableFuture<Void> future = securityConfiguration.change(change
-> {
change.changeEnabled(true);
change.changeAuthentication(authChange -> {
authChange.changeProviders(providersChange -> {
providersChange.delete("basic1");
});
});
- }).join();
+ });
+ assertThat(future, willCompleteSuccessfully());
await().during(TIMEOUT_OF_DURING).untilAtomic(ctxClosed, is(false));
}
@Test
void createNewProvider() throws IOException {
+ String newProviderName = "basic2";
handshake();
- securityConfiguration.change(change -> {
+ CompletableFuture<Void> future = securityConfiguration.change(change
-> {
change.changeEnabled(true);
change.changeAuthentication(authChange -> {
authChange.changeProviders(providersChange -> {
- providersChange.create("basic2", basicChange -> {
+ providersChange.create(newProviderName, basicChange -> {
basicChange.convert(BasicAuthenticationProviderChange.class)
- .changeUsername("admin2")
- .changePassword("admin");
+ .changeUsers(users ->
+ users.create("admin2", user ->
user.changePassword("admin"))
+ );
});
});
});
- }).join();
+ });
+ assertThat(future, willCompleteSuccessfully());
await().during(TIMEOUT_OF_DURING).untilAtomic(ctxClosed, is(false));
}
@@ -315,7 +290,7 @@ class ClientInboundMessageHandlerTest extends
BaseIgniteAbstractTest {
packer.packBinaryHeader(0); // Features.
packer.packInt(3); // Extensions.
packer.packString("authn-type");
- packer.packString("basic");
+ packer.packString(PROVIDER_NAME);
packer.packString("authn-identity");
packer.packString("admin");
packer.packString("authn-secret");
diff --git
a/modules/client/src/test/java/org/apache/ignite/client/ClientAuthenticationTest.java
b/modules/client/src/test/java/org/apache/ignite/client/ClientAuthenticationTest.java
index 7c109a5667..5741879c3f 100644
---
a/modules/client/src/test/java/org/apache/ignite/client/ClientAuthenticationTest.java
+++
b/modules/client/src/test/java/org/apache/ignite/client/ClientAuthenticationTest.java
@@ -115,12 +115,14 @@ public class ClientAuthenticationTest extends
BaseIgniteAbstractTest {
null);
if (basicAuthn) {
- securityConfiguration.change(change -> {
- change.changeEnabled(true);
-
change.changeAuthentication().changeProviders().create("basic",
authenticationProviderChange ->
-
authenticationProviderChange.convert(BasicAuthenticationProviderChange.class)
- .changeUsername("usr")
- .changePassword("pwd"));
+ securityConfiguration.change(securityChange -> {
+ securityChange.changeEnabled(true);
+
securityChange.changeAuthentication().changeProviders().create("basic", change
->
+ change.convert(BasicAuthenticationProviderChange.class)
+ .changeUsers(users -> users.create("usr", user
->
+ user.changePassword("pwd"))
+ )
+ );
}).join();
}
diff --git a/modules/distribution-zones/build.gradle
b/modules/distribution-zones/build.gradle
index 33fb0e3ccc..7e948adc28 100644
--- a/modules/distribution-zones/build.gradle
+++ b/modules/distribution-zones/build.gradle
@@ -88,6 +88,7 @@ dependencies {
integrationTestImplementation project(':ignite-affinity')
integrationTestImplementation project(':ignite-network-api')
integrationTestImplementation project(':ignite-network')
+ integrationTestImplementation project(':ignite-security')
integrationTestImplementation project(':ignite-system-view-api')
integrationTestImplementation testFixtures(project(':ignite-core'))
integrationTestImplementation testFixtures(project(':ignite-runner'))
diff --git
a/modules/distribution-zones/src/integrationTest/java/org/apache/ignite/internal/distributionzones/ItIgniteDistributionZoneManagerNodeRestartTest.java
b/modules/distribution-zones/src/integrationTest/java/org/apache/ignite/internal/distributionzones/ItIgniteDistributionZoneManagerNodeRestartTest.java
index b21564d4c0..5095725906 100644
---
a/modules/distribution-zones/src/integrationTest/java/org/apache/ignite/internal/distributionzones/ItIgniteDistributionZoneManagerNodeRestartTest.java
+++
b/modules/distribution-zones/src/integrationTest/java/org/apache/ignite/internal/distributionzones/ItIgniteDistributionZoneManagerNodeRestartTest.java
@@ -56,6 +56,7 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@@ -65,6 +66,7 @@ import java.util.concurrent.ConcurrentSkipListMap;
import java.util.function.Consumer;
import java.util.function.LongFunction;
import java.util.stream.Stream;
+import org.apache.ignite.configuration.validation.Validator;
import org.apache.ignite.internal.BaseIgniteRestartTest;
import org.apache.ignite.internal.catalog.CatalogManager;
import org.apache.ignite.internal.catalog.CatalogManagerImpl;
@@ -98,6 +100,7 @@ import
org.apache.ignite.internal.metastorage.server.TestRocksDbKeyValueStorage;
import
org.apache.ignite.internal.metastorage.server.raft.MetaStorageWriteHandler;
import org.apache.ignite.internal.network.configuration.NetworkConfiguration;
import org.apache.ignite.internal.network.recovery.VaultStateIds;
+import
org.apache.ignite.internal.security.authentication.validator.AuthenticationProvidersValidatorImpl;
import org.apache.ignite.internal.testframework.TestIgnitionManager;
import org.apache.ignite.internal.util.ByteUtils;
import org.apache.ignite.internal.vault.VaultManager;
@@ -219,11 +222,14 @@ public class
ItIgniteDistributionZoneManagerNodeRestartTest extends BaseIgniteRe
modules.distributed().polymorphicSchemaExtensions()
);
+ Set<Validator<?, ?>> validators = new
HashSet<>(modules.distributed().validators());
+ validators.remove(AuthenticationProvidersValidatorImpl.INSTANCE);
+
var clusterCfgMgr = new ConfigurationManager(
modules.distributed().rootKeys(),
cfgStorage,
distributedConfigurationGenerator,
-
ConfigurationValidatorImpl.withDefaultValidators(distributedConfigurationGenerator,
modules.distributed().validators())
+
ConfigurationValidatorImpl.withDefaultValidators(distributedConfigurationGenerator,
validators)
);
ConfigurationRegistry clusterConfigRegistry =
clusterCfgMgr.configurationRegistry();
diff --git
a/modules/jdbc/src/integrationTest/java/org/apache/ignite/jdbc/ItJdbcAuthenticationTest.java
b/modules/jdbc/src/integrationTest/java/org/apache/ignite/jdbc/ItJdbcAuthenticationTest.java
index e264223ecd..8ea08ba7f7 100644
---
a/modules/jdbc/src/integrationTest/java/org/apache/ignite/jdbc/ItJdbcAuthenticationTest.java
+++
b/modules/jdbc/src/integrationTest/java/org/apache/ignite/jdbc/ItJdbcAuthenticationTest.java
@@ -76,10 +76,14 @@ class ItJdbcAuthenticationTest extends
IgniteIntegrationTest {
+ " \"authentication\": {\n"
+ " \"providers\": [\n"
+ " {\n"
- + " \"name\": \"basic\",\n"
+ + " \"name\": \"default\",\n"
+ " \"type\": \"basic\",\n"
- + " \"username\": \"usr\",\n"
- + " \"password\": \"pwd\"\n"
+ + " \"users\": [\n"
+ + " {\n"
+ + " \"username\": \"usr\",\n"
+ + " \"password\": \"pwd\"\n"
+ + " }\n"
+ + " ]\n"
+ " }\n"
+ " ]\n"
+ " }\n"
diff --git
a/modules/rest/src/integrationTest/java/org/apache/ignite/internal/rest/ItInitializedClusterRestTest.java
b/modules/rest/src/integrationTest/java/org/apache/ignite/internal/rest/ItInitializedClusterRestTest.java
index d04eafa616..3ee30dba4c 100644
---
a/modules/rest/src/integrationTest/java/org/apache/ignite/internal/rest/ItInitializedClusterRestTest.java
+++
b/modules/rest/src/integrationTest/java/org/apache/ignite/internal/rest/ItInitializedClusterRestTest.java
@@ -173,7 +173,7 @@ public class ItInitializedClusterRestTest extends
AbstractRestTestBase {
400,
containsString(
"Validation did not pass for keys: "
- + "[security.authentication.providers,
Providers must be present, if security is enabled]"
+ + "[security.authentication.providers,
Default provider default is not removable.]"
)
)
);
diff --git
a/modules/rest/src/integrationTest/java/org/apache/ignite/internal/rest/authentication/ItAuthenticationTest.java
b/modules/rest/src/integrationTest/java/org/apache/ignite/internal/rest/authentication/ItAuthenticationTest.java
index b87527c357..7b14d7196b 100644
---
a/modules/rest/src/integrationTest/java/org/apache/ignite/internal/rest/authentication/ItAuthenticationTest.java
+++
b/modules/rest/src/integrationTest/java/org/apache/ignite/internal/rest/authentication/ItAuthenticationTest.java
@@ -17,258 +17,179 @@
package org.apache.ignite.internal.rest.authentication;
-
-import static java.util.stream.Collectors.toList;
-import static
org.apache.ignite.internal.testframework.IgniteTestUtils.testNodeName;
import static
org.apache.ignite.internal.testframework.IgniteTestUtils.waitForCondition;
-import static
org.apache.ignite.internal.testframework.matchers.CompletableFutureMatcher.willCompleteSuccessfully;
import static
org.apache.ignite.internal.testframework.matchers.HttpResponseMatcher.hasStatusCode;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.IOException;
+import java.net.InetAddress;
import java.net.URI;
+import java.net.UnknownHostException;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpRequest.BodyPublishers;
import java.net.http.HttpResponse;
import java.net.http.HttpResponse.BodyHandlers;
-import java.nio.file.Path;
import java.time.Duration;
import java.util.Base64;
-import java.util.Collections;
-import java.util.List;
-import java.util.stream.IntStream;
-import org.apache.ignite.internal.rest.RestNode;
-import org.apache.ignite.internal.testframework.BaseIgniteAbstractTest;
-import org.apache.ignite.internal.testframework.WorkDirectory;
+import java.util.Set;
+import java.util.stream.Collectors;
+import org.apache.ignite.InitParametersBuilder;
+import org.apache.ignite.internal.ClusterPerTestIntegrationTest;
+import org.apache.ignite.internal.app.IgniteImpl;
import org.apache.ignite.internal.testframework.WorkDirectoryExtension;
-import org.junit.jupiter.api.AfterEach;
+import org.apache.ignite.network.NetworkAddress;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInfo;
import org.junit.jupiter.api.extension.ExtendWith;
-
/** Tests for the REST authentication configuration. */
@ExtendWith(WorkDirectoryExtension.class)
-public class ItAuthenticationTest extends BaseIgniteAbstractTest {
-
+public class ItAuthenticationTest extends ClusterPerTestIntegrationTest {
/** HTTP client that is expected to be defined in subclasses. */
private HttpClient client;
- private List<RestNode> nodes;
+ private String username = "admin";
+
+ private String password = "password";
- @WorkDirectory
- private Path workDir;
+ private boolean enableAuth = true;
@BeforeEach
- void setUp(TestInfo testInfo) {
+ @Override
+ public void setup(TestInfo testInfo) {
client = HttpClient.newBuilder()
.build();
-
- nodes = IntStream.range(0, 3)
- .mapToObj(id -> {
- return RestNode.builder()
- .workDir(workDir)
- .name(testNodeName(testInfo, id))
- .networkPort(3344 + id)
- .httpPort(10300 + id)
- .httpsPort(10400 + id)
- .sslEnabled(false)
- .dualProtocol(false)
- .build();
- })
- .collect(toList());
-
- nodes.stream().parallel().forEach(RestNode::start);
}
- @Test
- public void disabledAuthentication(TestInfo testInfo) throws IOException,
InterruptedException {
- RestNode metaStorageNode = nodes.get(0);
+ @Override
+ protected void customizeInitParameters(InitParametersBuilder builder) {
+ String config = "security.enabled=" + enableAuth;
+
+ if (username != null && password != null) {
+ config += ",\n"
+ + "security.authentication.providers.default={"
+ + "type=basic,"
+ + "users=[{username=" + username
+ + ",password=" + password
+ + "}]}";
+ }
- // When.
- String initClusterBody = "{\n"
- + " \"metaStorageNodes\": [\n"
- + " \"" + metaStorageNode.name() + "\"\n"
- + " ],\n"
- + " \"cmgNodes\": [],\n"
- + " \"clusterName\": \"cluster\"\n"
- + "}";
+ builder.clusterConfiguration(config);
+ }
- initCluster(metaStorageNode.httpAddress(), initClusterBody);
+ @Test
+ public void disabledAuthentication(TestInfo testInfo) throws Exception {
+ enableAuth = false;
+ super.setup(testInfo);
// Then.
- for (RestNode node : nodes) {
- assertTrue(isRestAvailable(node.httpAddress(), "", ""));
- }
+ cluster.runningNodes().forEach(node ->
+ assertTrue(isRestAvailable(node.restHttpAddress(), "", ""))
+ );
}
@Test
- public void defaultUser(TestInfo testInfo) throws InterruptedException,
IOException {
- // When.
- RestNode metaStorageNode = nodes.get(0);
-
- String initClusterBody = "{\n"
- + " \"metaStorageNodes\": [\n"
- + " \"" + metaStorageNode.name() + "\"\n"
- + " ],\n"
- + " \"cmgNodes\": [],\n"
- + " \"clusterName\": \"cluster\",\n"
- + " \"clusterConfiguration\": \"{"
- + " security.enabled:true"
- + " }\"\n"
- + " }";
-
- initCluster(metaStorageNode.httpAddress(), initClusterBody);
+ public void defaultUser(TestInfo testInfo) throws Exception {
+ username = null;
+ password = null;
+ super.setup(testInfo);
- // Then.
// Authentication is enabled.
- for (RestNode node : nodes) {
- assertTrue(waitForCondition(() ->
isRestNotAvailable(node.httpAddress(), "", ""),
+ Set<IgniteImpl> nodes =
cluster.runningNodes().collect(Collectors.toSet());
+ for (IgniteImpl node : nodes) {
+ assertTrue(waitForCondition(() ->
isRestNotAvailable(node.restHttpAddress(), "", ""),
Duration.ofSeconds(5).toMillis()));
}
// REST is available with valid credentials
- for (RestNode node : nodes) {
- assertTrue(isRestAvailable(node.httpAddress(), "ignite",
"ignite"));
+ for (IgniteImpl node : nodes) {
+ assertTrue(isRestAvailable(node.restHttpAddress(), "ignite",
"ignite"));
}
}
@Test
- public void changeCredentials(TestInfo testInfo) throws
InterruptedException, IOException {
- // When.
- RestNode metaStorageNode = nodes.get(0);
-
- String initClusterBody = "{\n"
- + " \"metaStorageNodes\": [\n"
- + " \"" + metaStorageNode.name() + "\"\n"
- + " ],\n"
- + " \"cmgNodes\": [],\n"
- + " \"clusterName\": \"cluster\",\n"
- + " \"clusterConfiguration\": \"{"
- + " security.enabled:true, "
- + "
security.authentication.providers:[{name:basic,password:password,type:basic,username:admin}]"
- + " }\"\n"
- + " }";
-
- initCluster(metaStorageNode.httpAddress(), initClusterBody);
+ public void changeCredentials(TestInfo testInfo) throws Exception {
+ super.setup(testInfo);
+ Set<IgniteImpl> nodes =
cluster.runningNodes().collect(Collectors.toSet());
// Then.
// Authentication is enabled.
- for (RestNode node : nodes) {
- assertTrue(waitForCondition(() ->
isRestNotAvailable(node.httpAddress(), "", ""),
+ for (IgniteImpl node : nodes) {
+ assertTrue(waitForCondition(() ->
isRestNotAvailable(node.restHttpAddress(), "", ""),
Duration.ofSeconds(5).toMillis()));
}
// REST is available with valid credentials
- for (RestNode node : nodes) {
- assertTrue(isRestAvailable(node.httpAddress(), "admin",
"password"));
+ for (IgniteImpl node : nodes) {
+ assertTrue(isRestAvailable(node.restHttpAddress(), "admin",
"password"));
}
// REST is not available with invalid credentials
- for (RestNode node : nodes) {
- assertFalse(isRestAvailable(node.httpAddress(), "admin",
"wrong-password"));
+ for (IgniteImpl node : nodes) {
+ assertFalse(isRestAvailable(node.restHttpAddress(), "admin",
"wrong-password"));
}
// Change credentials.
- String updateRestAuthConfigBody = "{\n"
- + " \"security\": {\n"
- + " \"enabled\": true,\n"
- + " \"authentication\": {\n"
- + " \"providers\": [\n"
- + " {\n"
- + " \"name\": \"basic\",\n"
- + " \"type\": \"basic\",\n"
- + " \"username\": \"admin\",\n"
- + " \"password\": \"new-password\"\n"
- + " }\n"
- + " ]\n"
- + " }\n"
- + " }\n"
- + "}";
-
- updateClusterConfiguration(metaStorageNode.httpAddress(), "admin",
"password", updateRestAuthConfigBody);
+ String updateRestAuthConfigBody =
"security.authentication.providers.default.users.admin.password=new-password";
+
+ updateClusterConfiguration(node(0).restHttpAddress(), "admin",
"password", updateRestAuthConfigBody);
// REST is not available with old credentials
- for (RestNode node : nodes) {
- assertTrue(waitForCondition(() ->
isRestNotAvailable(node.httpAddress(), "admin", "password"),
+ for (IgniteImpl node : nodes) {
+ assertTrue(waitForCondition(() ->
isRestNotAvailable(node.restHttpAddress(), "admin", "password"),
Duration.ofSeconds(5).toMillis()));
}
// REST is available with new credentials
- for (RestNode node : nodes) {
- assertTrue(isRestAvailable(node.httpAddress(), "admin",
"new-password"));
+ for (IgniteImpl node : nodes) {
+ assertTrue(isRestAvailable(node.restHttpAddress(), "admin",
"new-password"));
}
}
@Test
- public void enableAuthenticationAndRestartNode(TestInfo testInfo) throws
InterruptedException, IOException {
- // When.
- RestNode metaStorageNode = nodes.get(0);
-
- String initClusterBody = "{\n"
- + " \"metaStorageNodes\": [\n"
- + " \"" + metaStorageNode.name() + "\"\n"
- + " ],\n"
- + " \"cmgNodes\": [],\n"
- + " \"clusterName\": \"cluster\",\n"
- + " \"clusterConfiguration\": \"{"
- + " security.enabled:true, "
- + "
security.authentication.providers:[{name:basic,password:password,type:basic,username:admin}]}\"\n"
- + " }";
-
- initCluster(metaStorageNode.httpAddress(), initClusterBody);
+ public void enableAuthenticationAndRestartNode(TestInfo testInfo) throws
Exception {
+ super.setup(testInfo);
+ Set<IgniteImpl> nodes =
cluster.runningNodes().collect(Collectors.toSet());
// Then.
// Authentication is enabled.
- for (RestNode node : nodes) {
- assertTrue(waitForCondition(() ->
isRestNotAvailable(node.httpAddress(), "", ""),
+ for (IgniteImpl node : nodes) {
+ assertTrue(waitForCondition(() ->
isRestNotAvailable(node.restHttpAddress(), "", ""),
Duration.ofSeconds(5).toMillis()));
}
// REST is available with valid credentials
- for (RestNode node : nodes) {
- assertTrue(isRestAvailable(node.httpAddress(), "admin",
"password"));
+ for (IgniteImpl node : nodes) {
+ assertTrue(isRestAvailable(node.restHttpAddress(), "admin",
"password"));
}
// REST is not available with invalid credentials
- for (RestNode node : nodes) {
- assertFalse(isRestAvailable(node.httpAddress(), "admin",
"wrong-password"));
+ for (IgniteImpl node : nodes) {
+ assertFalse(isRestAvailable(node.restHttpAddress(), "admin",
"wrong-password"));
}
// Restart a node.
- RestNode nodeToRestart = nodes.get(2);
- nodeToRestart.restart();
- waitForAllNodesStarted(Collections.singletonList(nodeToRestart));
+ restartNode(2);
+
+ nodes = cluster.runningNodes().collect(Collectors.toSet());
// REST is available with valid credentials
- for (RestNode node : nodes) {
- assertTrue(isRestAvailable(node.httpAddress(), "admin",
"password"));
+ for (IgniteImpl node : nodes) {
+ assertTrue(isRestAvailable(node.restHttpAddress(), "admin",
"password"));
}
// REST is not available with invalid credentials
- for (RestNode node : nodes) {
- assertFalse(isRestAvailable(node.httpAddress(), "admin",
"wrong-password"));
+ for (IgniteImpl node : nodes) {
+ assertFalse(isRestAvailable(node.restHttpAddress(), "admin",
"wrong-password"));
}
}
- private void initCluster(String baseUrl, String initClusterBody) throws
IOException, InterruptedException {
- URI clusterInitUri = URI.create(baseUrl +
"/management/v1/cluster/init");
- HttpRequest initRequest = HttpRequest.newBuilder(clusterInitUri)
- .header("content-type", "application/json")
- .POST(BodyPublishers.ofString(initClusterBody))
- .build();
-
- assertThat(sendRequest(client, initRequest), hasStatusCode(200));
-
- waitForAllNodesStarted(nodes);
- }
-
- private void updateClusterConfiguration(String baseUrl, String username,
String password, String configToApply) {
- URI updateClusterConfigUri = URI.create(baseUrl +
"/management/v1/configuration/cluster/");
+ private void updateClusterConfiguration(NetworkAddress address, String
username, String password, String configToApply) {
+ URI updateClusterConfigUri = URI.create("http://" + hostUrl(address) +
"/management/v1/configuration/cluster/");
HttpRequest updateClusterConfigRequest =
HttpRequest.newBuilder(updateClusterConfigUri)
.header("content-type", "text/plain")
.header("Authorization", basicAuthenticationHeader(username,
password))
@@ -278,12 +199,12 @@ public class ItAuthenticationTest extends
BaseIgniteAbstractTest {
assertThat(sendRequest(client, updateClusterConfigRequest),
hasStatusCode(200));
}
- private boolean isRestNotAvailable(String baseUrl, String username, String
password) {
+ private boolean isRestNotAvailable(NetworkAddress baseUrl, String
username, String password) {
return !isRestAvailable(baseUrl, username, password);
}
- private boolean isRestAvailable(String baseUrl, String username, String
password) {
- URI clusterConfigUri = URI.create(baseUrl +
"/management/v1/configuration/cluster/");
+ private boolean isRestAvailable(NetworkAddress address, String username,
String password) {
+ URI clusterConfigUri = URI.create("http://" + hostUrl(address) +
"/management/v1/configuration/cluster/");
HttpRequest clusterConfigRequest =
HttpRequest.newBuilder(clusterConfigUri)
.header("Authorization", basicAuthenticationHeader(username,
password))
.build();
@@ -294,7 +215,16 @@ public class ItAuthenticationTest extends
BaseIgniteAbstractTest {
} else if (code == 401) {
return false;
} else {
- throw new IllegalStateException("Unexpected response code: " +
code + ", body: " + response.body());
+ throw new IllegalStateException("Unexpected response code: " +
code + ", body: " + response.body());
+ }
+ }
+
+ private static String hostUrl(NetworkAddress networkAddress) {
+ try {
+ InetAddress host = InetAddress.getByName(networkAddress.host());
+ return host.getHostAddress() + ":" + networkAddress.port();
+ } catch (UnknownHostException e) {
+ throw new RuntimeException(e);
}
}
@@ -306,17 +236,6 @@ public class ItAuthenticationTest extends
BaseIgniteAbstractTest {
}
}
- @AfterEach
- void tearDown() {
- nodes.stream().parallel().forEach(RestNode::stop);
- }
-
- private static void waitForAllNodesStarted(List<RestNode> nodes) {
- nodes.stream()
- .map(RestNode::igniteNodeFuture)
- .forEach(future -> assertThat(future,
willCompleteSuccessfully()));
- }
-
private static String basicAuthenticationHeader(String username, String
password) {
String valueToEncode = username + ":" + password;
return "Basic " +
Base64.getEncoder().encodeToString(valueToEncode.getBytes());
diff --git
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/PlatformTestNodeRunner.java
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/PlatformTestNodeRunner.java
index 575d9cb772..f93581611d 100644
---
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/PlatformTestNodeRunner.java
+++
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/PlatformTestNodeRunner.java
@@ -715,8 +715,9 @@ public class PlatformTestNodeRunner {
if (enable) {
change.changeProviders().create("basic", authenticationProviderChange -> {
authenticationProviderChange.convert(BasicAuthenticationProviderChange.class)
- .changeUsername("user-1")
-
.changePassword("password-1");
+ .changeUsers(users ->
+
users.create("user-1", user -> user.changePassword("password-1"))
+ );
});
}
}
diff --git
a/modules/security/src/main/java/org/apache/ignite/internal/security/authentication/AuthenticationProviderEqualityVerifier.java
b/modules/security/src/main/java/org/apache/ignite/internal/security/authentication/AuthenticationProviderEqualityVerifier.java
index 8d8673d5e3..59f1a316ea 100644
---
a/modules/security/src/main/java/org/apache/ignite/internal/security/authentication/AuthenticationProviderEqualityVerifier.java
+++
b/modules/security/src/main/java/org/apache/ignite/internal/security/authentication/AuthenticationProviderEqualityVerifier.java
@@ -17,7 +17,10 @@
package org.apache.ignite.internal.security.authentication;
+import java.util.Objects;
+import org.apache.ignite.configuration.NamedListView;
import
org.apache.ignite.internal.security.authentication.basic.BasicAuthenticationProviderView;
+import org.apache.ignite.internal.security.authentication.basic.BasicUserView;
import
org.apache.ignite.internal.security.authentication.configuration.AuthenticationProviderView;
import org.jetbrains.annotations.Nullable;
@@ -61,6 +64,23 @@ public class AuthenticationProviderEqualityVerifier {
}
private static boolean areEqual(BasicAuthenticationProviderView o1,
BasicAuthenticationProviderView o2) {
- return o1.username().equals(o2.username()) &&
o1.password().equals(o2.password());
+ NamedListView<? extends BasicUserView> users1 = o1.users();
+ NamedListView<? extends BasicUserView> users2 = o2.users();
+ if (users1.size() != users2.size()) {
+ return false;
+ }
+
+ for (BasicUserView basicUser1View : users1) {
+ BasicUserView basicUser2View =
users2.get(basicUser1View.username());
+ if (basicUser2View == null) {
+ return false;
+ }
+ if (!Objects.equals(basicUser1View.username(),
basicUser2View.username())
+ || !Objects.equals(basicUser1View.password(),
basicUser2View.password())) {
+ return false;
+ }
+ }
+
+ return true;
}
}
diff --git
a/modules/security/src/main/java/org/apache/ignite/internal/security/authentication/AuthenticatorFactory.java
b/modules/security/src/main/java/org/apache/ignite/internal/security/authentication/AuthenticatorFactory.java
index 441e703cfa..d9da4fc7b8 100644
---
a/modules/security/src/main/java/org/apache/ignite/internal/security/authentication/AuthenticatorFactory.java
+++
b/modules/security/src/main/java/org/apache/ignite/internal/security/authentication/AuthenticatorFactory.java
@@ -17,8 +17,10 @@
package org.apache.ignite.internal.security.authentication;
+import java.util.stream.Collectors;
import
org.apache.ignite.internal.security.authentication.basic.BasicAuthenticationProviderView;
import
org.apache.ignite.internal.security.authentication.basic.BasicAuthenticator;
+import org.apache.ignite.internal.security.authentication.basic.BasicUser;
import
org.apache.ignite.internal.security.authentication.configuration.AuthenticationProviderView;
import org.apache.ignite.security.AuthenticationType;
@@ -28,7 +30,11 @@ class AuthenticatorFactory {
AuthenticationType type = AuthenticationType.parse(view.type());
if (type == AuthenticationType.BASIC) {
BasicAuthenticationProviderView basicAuthProviderView =
(BasicAuthenticationProviderView) view;
- return new BasicAuthenticator(view.name(),
basicAuthProviderView.username(), basicAuthProviderView.password());
+ return new BasicAuthenticator(
+ view.name(),
+ basicAuthProviderView.users().stream().map(basicUserView
-> new BasicUser(basicUserView.username(),
+ basicUserView.password())).collect(Collectors.toList())
+ );
} else {
throw new IllegalArgumentException("Unexpected authentication
type: " + type);
}
diff --git
a/modules/security/src/main/java/org/apache/ignite/internal/security/authentication/SecurityConfigurationModule.java
b/modules/security/src/main/java/org/apache/ignite/internal/security/authentication/SecurityConfigurationModule.java
index 31c5800b96..3b4a4cb7fc 100644
---
a/modules/security/src/main/java/org/apache/ignite/internal/security/authentication/SecurityConfigurationModule.java
+++
b/modules/security/src/main/java/org/apache/ignite/internal/security/authentication/SecurityConfigurationModule.java
@@ -28,7 +28,7 @@ import
org.apache.ignite.configuration.annotation.ConfigurationType;
import org.apache.ignite.configuration.validation.Validator;
import
org.apache.ignite.internal.security.authentication.basic.BasicAuthenticationProviderChange;
import
org.apache.ignite.internal.security.authentication.basic.BasicAuthenticationProviderConfigurationSchema;
-import
org.apache.ignite.internal.security.authentication.configuration.validator.AuthenticationProvidersValidatorImpl;
+import
org.apache.ignite.internal.security.authentication.validator.AuthenticationProvidersValidatorImpl;
import org.apache.ignite.internal.security.configuration.SecurityConfiguration;
/**
@@ -36,7 +36,7 @@ import
org.apache.ignite.internal.security.configuration.SecurityConfiguration;
*/
@AutoService(ConfigurationModule.class)
public class SecurityConfigurationModule implements ConfigurationModule {
- private static final String DEFAULT_PROVIDER_NAME = "default";
+ public static final String DEFAULT_PROVIDER_NAME = "default";
private static final String DEFAULT_USERNAME = "ignite";
@@ -66,11 +66,12 @@ public class SecurityConfigurationModule implements
ConfigurationModule {
public void patchConfigurationWithDynamicDefaults(SuperRootChange
rootChange) {
rootChange.changeRoot(SecurityConfiguration.KEY).changeAuthentication(authenticationChange
-> {
if (authenticationChange.changeProviders().size() == 0) {
-
authenticationChange.changeProviders().create(DEFAULT_PROVIDER_NAME, change -> {
- change.convert(BasicAuthenticationProviderChange.class)
- .changeUsername(DEFAULT_USERNAME)
- .changePassword(DEFAULT_PASSWORD);
- });
+
authenticationChange.changeProviders().create(DEFAULT_PROVIDER_NAME, change ->
+ change.convert(BasicAuthenticationProviderChange.class)
+ .changeUsers(users ->
users.create(DEFAULT_USERNAME, user ->
+ user.changePassword(DEFAULT_PASSWORD))
+ )
+ );
}
});
}
diff --git
a/modules/security/src/main/java/org/apache/ignite/internal/security/authentication/basic/BasicAuthenticationProviderConfigurationSchema.java
b/modules/security/src/main/java/org/apache/ignite/internal/security/authentication/basic/BasicAuthenticationProviderConfigurationSchema.java
index de0f410d7c..762509754a 100644
---
a/modules/security/src/main/java/org/apache/ignite/internal/security/authentication/basic/BasicAuthenticationProviderConfigurationSchema.java
+++
b/modules/security/src/main/java/org/apache/ignite/internal/security/authentication/basic/BasicAuthenticationProviderConfigurationSchema.java
@@ -17,23 +17,13 @@
package org.apache.ignite.internal.security.authentication.basic;
+import org.apache.ignite.configuration.annotation.NamedConfigValue;
import org.apache.ignite.configuration.annotation.PolymorphicConfigInstance;
-import org.apache.ignite.configuration.annotation.Secret;
-import org.apache.ignite.configuration.annotation.Value;
-import org.apache.ignite.configuration.validation.NotBlank;
import
org.apache.ignite.internal.security.authentication.configuration.AuthenticationProviderConfigurationSchema;
/** Basic authentication configuration. */
@PolymorphicConfigInstance(AuthenticationProviderConfigurationSchema.TYPE_BASIC)
public class BasicAuthenticationProviderConfigurationSchema extends
AuthenticationProviderConfigurationSchema {
- /** Username. */
- @NotBlank
- @Value
- public String username;
-
- /** Password. */
- @Secret
- @NotBlank
- @Value
- public String password;
+ @NamedConfigValue(syntheticKeyName = "username")
+ public BasicUserConfigurationSchema users;
}
diff --git
a/modules/security/src/main/java/org/apache/ignite/internal/security/authentication/basic/BasicAuthenticator.java
b/modules/security/src/main/java/org/apache/ignite/internal/security/authentication/basic/BasicAuthenticator.java
index 9372867b61..f004b8d1ee 100644
---
a/modules/security/src/main/java/org/apache/ignite/internal/security/authentication/basic/BasicAuthenticator.java
+++
b/modules/security/src/main/java/org/apache/ignite/internal/security/authentication/basic/BasicAuthenticator.java
@@ -17,6 +17,11 @@
package org.apache.ignite.internal.security.authentication.basic;
+import static java.util.function.Function.identity;
+
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
import
org.apache.ignite.internal.security.authentication.AuthenticationRequest;
import org.apache.ignite.internal.security.authentication.Authenticator;
import org.apache.ignite.internal.security.authentication.UserDetails;
@@ -26,23 +31,19 @@ import
org.apache.ignite.security.exception.UnsupportedAuthenticationTypeExcepti
/** Implementation of basic authenticator. */
public class BasicAuthenticator implements Authenticator {
- private final String authenticatorName;
-
- private final String username;
+ private final String providerName;
- private final String password;
+ private final Map<String, BasicUser> users;
/**
* Constructor.
*
- * @param authenticatorName Authenticator name.
- * @param username Username.
- * @param password Password.
+ * @param providerName Provider name.
+ * @param users List of registered users.
*/
- public BasicAuthenticator(String authenticatorName, String username,
String password) {
- this.authenticatorName = authenticatorName;
- this.username = username;
- this.password = password;
+ public BasicAuthenticator(String providerName, List<BasicUser> users) {
+ this.providerName = providerName;
+ this.users = users.stream().collect(Collectors.toMap(BasicUser::name,
identity()));
}
@Override
@@ -53,10 +54,16 @@ public class BasicAuthenticator implements Authenticator {
);
}
- if (username.equals(authenticationRequest.getIdentity()) &&
password.equals(authenticationRequest.getSecret())) {
- return new UserDetails(username, authenticatorName);
- } else {
- throw new InvalidCredentialsException("Invalid credentials");
+ Object requestUsername = authenticationRequest.getIdentity();
+ Object requestPassword = authenticationRequest.getSecret();
+
+ BasicUser basicUser = users.get(requestUsername);
+ if (basicUser != null) {
+ if (basicUser.password().equals(requestPassword)) {
+ return new UserDetails(basicUser.name(), providerName);
+ }
}
+
+ throw new InvalidCredentialsException("Invalid credentials");
}
}
diff --git
a/modules/security/src/main/java/org/apache/ignite/internal/security/authentication/basic/BasicAuthenticationProviderConfigurationSchema.java
b/modules/security/src/main/java/org/apache/ignite/internal/security/authentication/basic/BasicUser.java
similarity index 53%
copy from
modules/security/src/main/java/org/apache/ignite/internal/security/authentication/basic/BasicAuthenticationProviderConfigurationSchema.java
copy to
modules/security/src/main/java/org/apache/ignite/internal/security/authentication/basic/BasicUser.java
index de0f410d7c..2b8b0900a5 100644
---
a/modules/security/src/main/java/org/apache/ignite/internal/security/authentication/basic/BasicAuthenticationProviderConfigurationSchema.java
+++
b/modules/security/src/main/java/org/apache/ignite/internal/security/authentication/basic/BasicUser.java
@@ -17,23 +17,24 @@
package org.apache.ignite.internal.security.authentication.basic;
-import org.apache.ignite.configuration.annotation.PolymorphicConfigInstance;
-import org.apache.ignite.configuration.annotation.Secret;
-import org.apache.ignite.configuration.annotation.Value;
-import org.apache.ignite.configuration.validation.NotBlank;
-import
org.apache.ignite.internal.security.authentication.configuration.AuthenticationProviderConfigurationSchema;
+/**
+ * Data class for user information.
+ */
+public class BasicUser {
+ private final String name;
+
+ private final String password;
+
+ public BasicUser(String name, String password) {
+ this.name = name;
+ this.password = password;
+ }
-/** Basic authentication configuration. */
-@PolymorphicConfigInstance(AuthenticationProviderConfigurationSchema.TYPE_BASIC)
-public class BasicAuthenticationProviderConfigurationSchema extends
AuthenticationProviderConfigurationSchema {
- /** Username. */
- @NotBlank
- @Value
- public String username;
+ public String name() {
+ return name;
+ }
- /** Password. */
- @Secret
- @NotBlank
- @Value
- public String password;
+ public String password() {
+ return password;
+ }
}
diff --git
a/modules/security/src/main/java/org/apache/ignite/internal/security/authentication/basic/BasicAuthenticationProviderConfigurationSchema.java
b/modules/security/src/main/java/org/apache/ignite/internal/security/authentication/basic/BasicUserConfigurationSchema.java
similarity index 72%
copy from
modules/security/src/main/java/org/apache/ignite/internal/security/authentication/basic/BasicAuthenticationProviderConfigurationSchema.java
copy to
modules/security/src/main/java/org/apache/ignite/internal/security/authentication/basic/BasicUserConfigurationSchema.java
index de0f410d7c..69b18faecf 100644
---
a/modules/security/src/main/java/org/apache/ignite/internal/security/authentication/basic/BasicAuthenticationProviderConfigurationSchema.java
+++
b/modules/security/src/main/java/org/apache/ignite/internal/security/authentication/basic/BasicUserConfigurationSchema.java
@@ -17,18 +17,19 @@
package org.apache.ignite.internal.security.authentication.basic;
-import org.apache.ignite.configuration.annotation.PolymorphicConfigInstance;
+import org.apache.ignite.configuration.annotation.Config;
+import org.apache.ignite.configuration.annotation.InjectedName;
import org.apache.ignite.configuration.annotation.Secret;
import org.apache.ignite.configuration.annotation.Value;
import org.apache.ignite.configuration.validation.NotBlank;
-import
org.apache.ignite.internal.security.authentication.configuration.AuthenticationProviderConfigurationSchema;
-/** Basic authentication configuration. */
-@PolymorphicConfigInstance(AuthenticationProviderConfigurationSchema.TYPE_BASIC)
-public class BasicAuthenticationProviderConfigurationSchema extends
AuthenticationProviderConfigurationSchema {
+/**
+ * Basic user configuration schema.
+ */
+@Config
+public class BasicUserConfigurationSchema {
/** Username. */
- @NotBlank
- @Value
+ @InjectedName
public String username;
/** Password. */
diff --git
a/modules/security-api/src/main/java/org/apache/ignite/internal/security/authentication/configuration/validator/AuthenticationProvidersValidatorImpl.java
b/modules/security/src/main/java/org/apache/ignite/internal/security/authentication/validator/AuthenticationProvidersValidatorImpl.java
similarity index 52%
rename from
modules/security-api/src/main/java/org/apache/ignite/internal/security/authentication/configuration/validator/AuthenticationProvidersValidatorImpl.java
rename to
modules/security/src/main/java/org/apache/ignite/internal/security/authentication/validator/AuthenticationProvidersValidatorImpl.java
index 84f6ccff3c..b6de765161 100644
---
a/modules/security-api/src/main/java/org/apache/ignite/internal/security/authentication/configuration/validator/AuthenticationProvidersValidatorImpl.java
+++
b/modules/security/src/main/java/org/apache/ignite/internal/security/authentication/validator/AuthenticationProvidersValidatorImpl.java
@@ -15,13 +15,18 @@
* limitations under the License.
*/
-package
org.apache.ignite.internal.security.authentication.configuration.validator;
+package org.apache.ignite.internal.security.authentication.validator;
+
+import static
org.apache.ignite.internal.security.authentication.SecurityConfigurationModule.DEFAULT_PROVIDER_NAME;
import org.apache.ignite.configuration.NamedListView;
import org.apache.ignite.configuration.validation.ValidationContext;
import org.apache.ignite.configuration.validation.ValidationIssue;
import org.apache.ignite.configuration.validation.Validator;
+import
org.apache.ignite.internal.security.authentication.basic.BasicAuthenticationProviderConfiguration;
+import
org.apache.ignite.internal.security.authentication.basic.BasicAuthenticationProviderView;
import
org.apache.ignite.internal.security.authentication.configuration.AuthenticationProviderView;
+import
org.apache.ignite.internal.security.authentication.configuration.validator.AuthenticationProvidersValidator;
import org.apache.ignite.internal.security.configuration.SecurityConfiguration;
/**
@@ -39,8 +44,36 @@ public class AuthenticationProvidersValidatorImpl implements
boolean enabled = ctx.getNewRoot(SecurityConfiguration.KEY).enabled();
NamedListView<? extends AuthenticationProviderView> view =
ctx.getNewValue();
- if (enabled && view.size() == 0) {
- ctx.addIssue(new ValidationIssue(ctx.currentKey(), "Providers must
be present, if security is enabled"));
+ BasicAuthenticationProviderView basicProvider =
(BasicAuthenticationProviderView) view.get(DEFAULT_PROVIDER_NAME);
+ if (basicProvider == null) {
+ ctx.addIssue(new ValidationIssue(ctx.currentKey(), "Default
provider " + DEFAULT_PROVIDER_NAME + " is not removable."));
+ return;
+ }
+
+ if (!checkOnlyOneBasicProvider(ctx, view)) {
+ return;
+ }
+
+ if (enabled && view.size() == 1 && basicProvider.users().size() == 0) {
+ ctx.addIssue(new ValidationIssue(ctx.currentKey(), "Basic provider
must have at least one user "
+ + "in case when no other providers present."));
+ }
+ }
+
+ private static boolean checkOnlyOneBasicProvider(
+ ValidationContext<NamedListView<? extends
AuthenticationProviderView>> ctx,
+ NamedListView<? extends AuthenticationProviderView> view
+ ) {
+ boolean basicAlreadyFound = false;
+ for (AuthenticationProviderView authenticationProviderView : view) {
+ if (authenticationProviderView instanceof
BasicAuthenticationProviderConfiguration) {
+ if (basicAlreadyFound) {
+ ctx.addIssue(new ValidationIssue(ctx.currentKey(), "Only
one basic provider supported."));
+ return false;
+ }
+ basicAlreadyFound = true;
+ }
}
+ return true;
}
}
diff --git
a/modules/security/src/test/java/org/apache/ignite/internal/security/authentication/AuthenticationManagerImplTest.java
b/modules/security/src/test/java/org/apache/ignite/internal/security/authentication/AuthenticationManagerImplTest.java
index 92b1e21c06..5a941be645 100644
---
a/modules/security/src/test/java/org/apache/ignite/internal/security/authentication/AuthenticationManagerImplTest.java
+++
b/modules/security/src/test/java/org/apache/ignite/internal/security/authentication/AuthenticationManagerImplTest.java
@@ -187,8 +187,9 @@ class AuthenticationManagerImplTest extends
BaseIgniteAbstractTest {
securityConfiguration, change -> {
change.changeAuthentication().changeProviders(providers ->
providers.update(PROVIDER, provider -> {
provider.convert(BasicAuthenticationProviderChange.class)
- .changeUsername(USERNAME)
- .changePassword("new-password");
+ .changeUsers(users ->
+ users.update(USERNAME, user ->
user.changePassword("new-password"))
+ );
}));
})
.value();
@@ -244,8 +245,9 @@ class AuthenticationManagerImplTest extends
BaseIgniteAbstractTest {
securityConfiguration, change -> {
change.changeAuthentication().changeProviders(providers ->
providers.create(PROVIDER, provider -> {
provider.convert(BasicAuthenticationProviderChange.class)
- .changeUsername(USERNAME)
- .changePassword(PASSWORD);
+ .changeUsers(users ->
+ users.create(USERNAME, user ->
user.changePassword(PASSWORD))
+ );
}));
change.changeEnabled(true);
})
diff --git
a/modules/security/src/test/java/org/apache/ignite/internal/security/authentication/AuthenticationProvidersValidatorImplTest.java
b/modules/security/src/test/java/org/apache/ignite/internal/security/authentication/AuthenticationProvidersValidatorImplTest.java
index 522d793a0b..5f0fccea18 100644
---
a/modules/security/src/test/java/org/apache/ignite/internal/security/authentication/AuthenticationProvidersValidatorImplTest.java
+++
b/modules/security/src/test/java/org/apache/ignite/internal/security/authentication/AuthenticationProvidersValidatorImplTest.java
@@ -19,6 +19,7 @@ package org.apache.ignite.internal.security.authentication;
import static
org.apache.ignite.internal.configuration.validation.TestValidationUtil.mockValidationContext;
import static
org.apache.ignite.internal.configuration.validation.TestValidationUtil.validate;
+import static
org.apache.ignite.internal.security.authentication.SecurityConfigurationModule.DEFAULT_PROVIDER_NAME;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
@@ -30,7 +31,7 @@ import
org.apache.ignite.internal.configuration.testframework.ConfigurationExten
import
org.apache.ignite.internal.configuration.testframework.InjectConfiguration;
import
org.apache.ignite.internal.security.authentication.configuration.AuthenticationProviderView;
import
org.apache.ignite.internal.security.authentication.configuration.validator.AuthenticationProvidersValidator;
-import
org.apache.ignite.internal.security.authentication.configuration.validator.AuthenticationProvidersValidatorImpl;
+import
org.apache.ignite.internal.security.authentication.validator.AuthenticationProvidersValidatorImpl;
import org.apache.ignite.internal.security.configuration.SecurityChange;
import org.apache.ignite.internal.security.configuration.SecurityConfiguration;
import org.apache.ignite.internal.security.configuration.SecurityView;
@@ -62,7 +63,7 @@ class AuthenticationProvidersValidatorImplTest extends
BaseIgniteAbstractTest {
AuthenticationProvidersValidatorImpl.INSTANCE,
mock(AuthenticationProvidersValidator.class),
ctx,
- "Providers must be present, if security is enabled"
+ "Default provider " + DEFAULT_PROVIDER_NAME + " is not
removable"
);
}
diff --git
a/modules/security/src/test/java/org/apache/ignite/internal/security/authentication/SecurityConfigurationModuleTest.java
b/modules/security/src/test/java/org/apache/ignite/internal/security/authentication/SecurityConfigurationModuleTest.java
index ec51859953..17b354270b 100644
---
a/modules/security/src/test/java/org/apache/ignite/internal/security/authentication/SecurityConfigurationModuleTest.java
+++
b/modules/security/src/test/java/org/apache/ignite/internal/security/authentication/SecurityConfigurationModuleTest.java
@@ -34,7 +34,7 @@ import
org.apache.ignite.internal.configuration.SuperRootChangeImpl;
import
org.apache.ignite.internal.security.authentication.basic.BasicAuthenticationProviderChange;
import
org.apache.ignite.internal.security.authentication.basic.BasicAuthenticationProviderConfigurationSchema;
import
org.apache.ignite.internal.security.authentication.basic.BasicAuthenticationProviderView;
-import
org.apache.ignite.internal.security.authentication.configuration.validator.AuthenticationProvidersValidatorImpl;
+import
org.apache.ignite.internal.security.authentication.validator.AuthenticationProvidersValidatorImpl;
import org.apache.ignite.internal.security.configuration.SecurityConfiguration;
import org.apache.ignite.internal.security.configuration.SecurityView;
import org.junit.jupiter.api.AfterEach;
@@ -107,8 +107,8 @@ class SecurityConfigurationModuleTest {
.get(0);
assertAll(
- () -> assertThat(defaultProvider.username(),
equalTo("ignite")),
- () -> assertThat(defaultProvider.password(), equalTo("ignite"))
+ () -> assertThat(defaultProvider.users().get(0).username(),
equalTo("ignite")),
+ () -> assertThat(defaultProvider.users().get(0).password(),
equalTo("ignite"))
);
}
@@ -117,8 +117,7 @@ class SecurityConfigurationModuleTest {
rootChange.changeRoot(SecurityConfiguration.KEY).changeAuthentication(authenticationChange
-> {
authenticationChange.changeProviders().create("basic", change -> {
change.convert(BasicAuthenticationProviderChange.class)
- .changeUsername("admin")
- .changePassword("password");
+ .changeUsers(users -> users.create("admin", user ->
user.changePassword("password")));
});
});
@@ -133,8 +132,8 @@ class SecurityConfigurationModuleTest {
.get(0);
assertAll(
- () -> assertThat(defaultProvider.username(), equalTo("admin")),
- () -> assertThat(defaultProvider.password(),
equalTo("password"))
+ () -> assertThat(defaultProvider.users().get(0).username(),
equalTo("admin")),
+ () -> assertThat(defaultProvider.users().get(0).password(),
equalTo("password"))
);
}
}
diff --git
a/modules/security/src/test/java/org/apache/ignite/internal/security/authentication/basic/BasicAuthenticationProviderConfigurationSchemaTest.java
b/modules/security/src/test/java/org/apache/ignite/internal/security/authentication/basic/BasicAuthenticationProviderConfigurationSchemaTest.java
index 3a2aca2b78..78f616799b 100644
---
a/modules/security/src/test/java/org/apache/ignite/internal/security/authentication/basic/BasicAuthenticationProviderConfigurationSchemaTest.java
+++
b/modules/security/src/test/java/org/apache/ignite/internal/security/authentication/basic/BasicAuthenticationProviderConfigurationSchemaTest.java
@@ -21,25 +21,25 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
import java.lang.reflect.Field;
import java.util.Arrays;
+import org.apache.ignite.configuration.annotation.InjectedName;
import org.apache.ignite.configuration.annotation.Secret;
import org.apache.ignite.configuration.validation.NotBlank;
-import
org.apache.ignite.internal.security.authentication.basic.BasicAuthenticationProviderConfigurationSchema;
import org.junit.jupiter.api.Test;
class BasicAuthenticationProviderConfigurationSchemaTest {
@Test
- public void usernameIsNotBlank() {
- Field username =
Arrays.stream(BasicAuthenticationProviderConfigurationSchema.class.getDeclaredFields())
+ public void usernameIsInjected() {
+ Field username =
Arrays.stream(BasicUserConfigurationSchema.class.getDeclaredFields())
.filter(it -> it.getName().equals("username"))
.findFirst()
.orElseThrow();
- assertTrue(username.isAnnotationPresent(NotBlank.class));
+ assertTrue(username.isAnnotationPresent(InjectedName.class));
}
@Test
public void passwordIsSecretAndNotBlank() {
- Field password =
Arrays.stream(BasicAuthenticationProviderConfigurationSchema.class.getDeclaredFields())
+ Field password =
Arrays.stream(BasicUserConfigurationSchema.class.getDeclaredFields())
.filter(it -> it.getName().equals("password"))
.findFirst()
.orElseThrow();
diff --git
a/modules/security/src/test/java/org/apache/ignite/internal/security/authentication/basic/BasicAuthenticatorTest.java
b/modules/security/src/test/java/org/apache/ignite/internal/security/authentication/basic/BasicAuthenticatorTest.java
index 7fe9cce0bd..2f4bce91c5 100644
---
a/modules/security/src/test/java/org/apache/ignite/internal/security/authentication/basic/BasicAuthenticatorTest.java
+++
b/modules/security/src/test/java/org/apache/ignite/internal/security/authentication/basic/BasicAuthenticatorTest.java
@@ -20,6 +20,7 @@ package
org.apache.ignite.internal.security.authentication.basic;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
+import java.util.List;
import org.apache.ignite.internal.security.authentication.AnonymousRequest;
import org.apache.ignite.internal.security.authentication.UserDetails;
import
org.apache.ignite.internal.security.authentication.UsernamePasswordRequest;
@@ -28,7 +29,10 @@ import
org.apache.ignite.security.exception.UnsupportedAuthenticationTypeExcepti
import org.junit.jupiter.api.Test;
class BasicAuthenticatorTest {
- private final BasicAuthenticator authenticator = new
BasicAuthenticator("basic", "admin", "password");
+ private final BasicAuthenticator authenticator = new BasicAuthenticator(
+ "basic",
+ List.of(new BasicUser("admin", "password"))
+ );
@Test
void authenticate() {