allow for the Environment to be optional in the ClientSSLService
This change makes the environment optional for the ClientSSLService, which is used for Transport Clients and also used for LDAP client connections. Since we use the ClientSSLService for LDAP connections, we still need the environment when running as a node under the security manager to resolve paths from the configuration directory. Closes elastic/elasticsearch#573 Original commit: elastic/x-pack-elasticsearch@862fafffe3
This commit is contained in:
parent
e17b3894d3
commit
94dbf3f2a7
|
@ -24,6 +24,7 @@ import java.io.InputStream;
|
|||
import java.net.InetAddress;
|
||||
import java.net.Socket;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.security.KeyStore;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
@ -46,8 +47,8 @@ public abstract class AbstractSSLService extends AbstractComponent {
|
|||
static final int DEFAULT_SESSION_CACHE_SIZE = 1000;
|
||||
static final String DEFAULT_PROTOCOL = "TLSv1.2";
|
||||
|
||||
protected final Environment env;
|
||||
protected final LoadingCache<SSLSettings, SSLContext> sslContexts = CacheBuilder.newBuilder().build(new SSLContextCacheLoader());
|
||||
protected Environment env;
|
||||
|
||||
public AbstractSSLService(Settings settings, Environment environment) {
|
||||
super(settings);
|
||||
|
@ -174,6 +175,10 @@ public abstract class AbstractSSLService extends AbstractComponent {
|
|||
return requestedCiphersList.toArray(new String[requestedCiphersList.size()]);
|
||||
}
|
||||
|
||||
protected Path resolvePath(String location) {
|
||||
return env.configFile().resolve(location);
|
||||
}
|
||||
|
||||
private class SSLContextCacheLoader extends CacheLoader<SSLSettings, SSLContext> {
|
||||
|
||||
@Override
|
||||
|
@ -238,7 +243,7 @@ public abstract class AbstractSSLService extends AbstractComponent {
|
|||
}
|
||||
|
||||
private KeyStore readKeystore(String path, String password) throws Exception {
|
||||
try (InputStream in = Files.newInputStream(env.binFile().getParent().resolve(path))) {
|
||||
try (InputStream in = Files.newInputStream(resolvePath(path))) {
|
||||
// Load TrustStore
|
||||
KeyStore ks = KeyStore.getInstance("jks");
|
||||
assert password != null;
|
||||
|
|
|
@ -5,15 +5,26 @@
|
|||
*/
|
||||
package org.elasticsearch.shield.ssl;
|
||||
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.SuppressForbidden;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.io.PathUtils;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.env.Environment;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
@SuppressForbidden(reason = "we don't have the environment to resolve files from when running in a transport client")
|
||||
public class ClientSSLService extends AbstractSSLService {
|
||||
|
||||
@Inject
|
||||
public ClientSSLService(Settings settings, Environment environment) {
|
||||
super(settings, environment);
|
||||
public ClientSSLService(Settings settings) {
|
||||
super(settings, null);
|
||||
}
|
||||
|
||||
@Inject(optional = true)
|
||||
public void setEnvironment(Environment environment) {
|
||||
this.env = environment;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -34,4 +45,12 @@ public class ClientSSLService extends AbstractSSLService {
|
|||
|
||||
return sslSettings;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Path resolvePath(String location) {
|
||||
if (env == null) {
|
||||
return PathUtils.get(Strings.cleanPath(location));
|
||||
}
|
||||
return super.resolvePath(location);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,7 +43,8 @@ public class ActiveDirectoryGroupsResolverTests extends ESTestCase {
|
|||
ClientSSLService clientSSLService = new ClientSSLService(Settings.builder()
|
||||
.put("shield.ssl.keystore.path", keystore)
|
||||
.put("shield.ssl.keystore.password", "changeit")
|
||||
.build(), env);
|
||||
.build());
|
||||
clientSSLService.setEnvironment(env);
|
||||
|
||||
LDAPURL ldapurl = new LDAPURL(ActiveDirectorySessionFactoryTests.AD_LDAP_URL);
|
||||
LDAPConnectionOptions options = new LDAPConnectionOptions();
|
||||
|
|
|
@ -51,7 +51,8 @@ public class ActiveDirectorySessionFactoryTests extends ESTestCase {
|
|||
clientSSLService = new ClientSSLService(Settings.builder()
|
||||
.put("shield.ssl.keystore.path", keystore)
|
||||
.put("shield.ssl.keystore.password", "changeit")
|
||||
.build(), env);
|
||||
.build());
|
||||
clientSSLService.setEnvironment(env);
|
||||
globalSettings = Settings.builder().put("path.home", createTempDir()).build();
|
||||
}
|
||||
|
||||
|
|
|
@ -63,7 +63,8 @@ public class LdapUserSearchSessionFactoryTests extends LdapTestCase {
|
|||
clientSSLService = new ClientSSLService(settingsBuilder()
|
||||
.put("shield.ssl.keystore.path", keystore)
|
||||
.put("shield.ssl.keystore.password", "changeit")
|
||||
.build(), env);
|
||||
.build());
|
||||
clientSSLService.setEnvironment(env);
|
||||
|
||||
globalSettings = settingsBuilder().put("path.home", createTempDir()).build();
|
||||
}
|
||||
|
|
|
@ -48,7 +48,8 @@ public class OpenLdapTests extends ESTestCase {
|
|||
clientSSLService = new ClientSSLService(Settings.builder()
|
||||
.put("shield.ssl.keystore.path", keystore)
|
||||
.put("shield.ssl.keystore.password", "changeit")
|
||||
.build(), env);
|
||||
.build());
|
||||
clientSSLService.setEnvironment(env);
|
||||
globalSettings = Settings.builder().put("path.home", createTempDir()).build();
|
||||
}
|
||||
|
||||
|
|
|
@ -43,7 +43,8 @@ public class SearchGroupsResolverTests extends ESTestCase {
|
|||
ClientSSLService clientSSLService = new ClientSSLService(Settings.builder()
|
||||
.put("shield.ssl.keystore.path", keystore)
|
||||
.put("shield.ssl.keystore.password", "changeit")
|
||||
.build(), env);
|
||||
.build());
|
||||
clientSSLService.setEnvironment(env);
|
||||
|
||||
LDAPURL ldapurl = new LDAPURL(OpenLdapTests.OPEN_LDAP_URL);
|
||||
LDAPConnectionOptions options = new LDAPConnectionOptions();
|
||||
|
|
|
@ -41,7 +41,8 @@ public class UserAttributeGroupsResolverTests extends ESTestCase {
|
|||
ClientSSLService clientSSLService = new ClientSSLService(Settings.builder()
|
||||
.put("shield.ssl.keystore.path", keystore)
|
||||
.put("shield.ssl.keystore.password", "changeit")
|
||||
.build(), env);
|
||||
.build());
|
||||
clientSSLService.setEnvironment(env);
|
||||
|
||||
LDAPURL ldapurl = new LDAPURL(ActiveDirectorySessionFactoryTests.AD_LDAP_URL);
|
||||
LDAPConnectionOptions options = new LDAPConnectionOptions();
|
||||
|
|
|
@ -34,7 +34,7 @@ public class ClientSSLServiceTests extends ESTestCase {
|
|||
@Before
|
||||
public void setup() throws Exception {
|
||||
testclientStore = getDataPath("/org/elasticsearch/shield/transport/ssl/certs/simple/testclient.jks");
|
||||
env = new Environment(settingsBuilder().put("path.home", createTempDir()).build());
|
||||
env = randomBoolean() ? new Environment(settingsBuilder().put("path.home", createTempDir()).build()) : null;
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -46,7 +46,7 @@ public class ClientSSLServiceTests extends ESTestCase {
|
|||
.put("shield.ssl.keystore.password", "testclient")
|
||||
.put("shield.ssl.truststore.path", testclientStore)
|
||||
.put("shield.ssl.truststore.password", "testclient")
|
||||
.build(), env).createSSLEngine();
|
||||
.build()).createSSLEngine();
|
||||
fail("expected an exception");
|
||||
} catch (ElasticsearchException e) {
|
||||
assertThat(e.getMessage(), containsString("failed to initialize the SSLContext"));
|
||||
|
@ -57,10 +57,10 @@ public class ClientSSLServiceTests extends ESTestCase {
|
|||
public void testThatCustomTruststoreCanBeSpecified() throws Exception {
|
||||
Path testnodeStore = getDataPath("/org/elasticsearch/shield/transport/ssl/certs/simple/testnode.jks");
|
||||
|
||||
ClientSSLService sslService = new ClientSSLService(settingsBuilder()
|
||||
ClientSSLService sslService = createClientSSLService(settingsBuilder()
|
||||
.put("shield.ssl.keystore.path", testclientStore)
|
||||
.put("shield.ssl.keystore.password", "testclient")
|
||||
.build(), env);
|
||||
.build());
|
||||
|
||||
Settings.Builder settingsBuilder = settingsBuilder()
|
||||
.put("truststore.path", testnodeStore)
|
||||
|
@ -75,10 +75,10 @@ public class ClientSSLServiceTests extends ESTestCase {
|
|||
|
||||
@Test
|
||||
public void testThatSslContextCachingWorks() throws Exception {
|
||||
ClientSSLService sslService = new ClientSSLService(settingsBuilder()
|
||||
ClientSSLService sslService = createClientSSLService(settingsBuilder()
|
||||
.put("shield.ssl.keystore.path", testclientStore)
|
||||
.put("shield.ssl.keystore.password", "testclient")
|
||||
.build(), env);
|
||||
.build());
|
||||
|
||||
SSLContext sslContext = sslService.sslContext();
|
||||
SSLContext cachedSslContext = sslService.sslContext();
|
||||
|
@ -89,21 +89,21 @@ public class ClientSSLServiceTests extends ESTestCase {
|
|||
@Test
|
||||
public void testThatKeyStoreAndKeyCanHaveDifferentPasswords() throws Exception {
|
||||
Path differentPasswordsStore = getDataPath("/org/elasticsearch/shield/transport/ssl/certs/simple/testnode-different-passwords.jks");
|
||||
new ClientSSLService(settingsBuilder()
|
||||
createClientSSLService(settingsBuilder()
|
||||
.put("shield.ssl.keystore.path", differentPasswordsStore)
|
||||
.put("shield.ssl.keystore.password", "testnode")
|
||||
.put("shield.ssl.keystore.key_password", "testnode1")
|
||||
.build(), env).createSSLEngine();
|
||||
.build()).createSSLEngine();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIncorrectKeyPasswordThrowsException() throws Exception {
|
||||
Path differentPasswordsStore = getDataPath("/org/elasticsearch/shield/transport/ssl/certs/simple/testnode-different-passwords.jks");
|
||||
try {
|
||||
new ClientSSLService(settingsBuilder()
|
||||
createClientSSLService(settingsBuilder()
|
||||
.put("shield.ssl.keystore.path", differentPasswordsStore)
|
||||
.put("shield.ssl.keystore.password", "testnode")
|
||||
.build(), env).createSSLEngine();
|
||||
.build()).createSSLEngine();
|
||||
fail("expected an exception");
|
||||
} catch (ElasticsearchException e) {
|
||||
assertThat(e.getMessage(), containsString("failed to initialize a KeyManagerFactory"));
|
||||
|
@ -112,20 +112,20 @@ public class ClientSSLServiceTests extends ESTestCase {
|
|||
|
||||
@Test
|
||||
public void testThatSSLv3IsNotEnabled() throws Exception {
|
||||
ClientSSLService sslService = new ClientSSLService(settingsBuilder()
|
||||
ClientSSLService sslService = createClientSSLService(settingsBuilder()
|
||||
.put("shield.ssl.keystore.path", testclientStore)
|
||||
.put("shield.ssl.keystore.password", "testclient")
|
||||
.build(), env);
|
||||
.build());
|
||||
SSLEngine engine = sslService.createSSLEngine();
|
||||
assertThat(Arrays.asList(engine.getEnabledProtocols()), not(hasItem("SSLv3")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThatSSLSessionCacheHasDefaultLimits() throws Exception {
|
||||
ClientSSLService sslService = new ClientSSLService(settingsBuilder()
|
||||
ClientSSLService sslService = createClientSSLService(settingsBuilder()
|
||||
.put("shield.ssl.keystore.path", testclientStore)
|
||||
.put("shield.ssl.keystore.password", "testclient")
|
||||
.build(), env);
|
||||
.build());
|
||||
SSLSessionContext context = sslService.sslContext().getServerSessionContext();
|
||||
assertThat(context.getSessionCacheSize(), equalTo(1000));
|
||||
assertThat(context.getSessionTimeout(), equalTo((int) TimeValue.timeValueHours(24).seconds()));
|
||||
|
@ -133,12 +133,12 @@ public class ClientSSLServiceTests extends ESTestCase {
|
|||
|
||||
@Test
|
||||
public void testThatSettingSSLSessionCacheLimitsWorks() throws Exception {
|
||||
ClientSSLService sslService = new ClientSSLService(settingsBuilder()
|
||||
ClientSSLService sslService = createClientSSLService(settingsBuilder()
|
||||
.put("shield.ssl.keystore.path", testclientStore)
|
||||
.put("shield.ssl.keystore.password", "testclient")
|
||||
.put("shield.ssl.session.cache_size", "300")
|
||||
.put("shield.ssl.session.cache_timeout", "600s")
|
||||
.build(), env);
|
||||
.build());
|
||||
SSLSessionContext context = sslService.sslContext().getServerSessionContext();
|
||||
assertThat(context.getSessionCacheSize(), equalTo(300));
|
||||
assertThat(context.getSessionTimeout(), equalTo(600));
|
||||
|
@ -146,27 +146,27 @@ public class ClientSSLServiceTests extends ESTestCase {
|
|||
|
||||
@Test
|
||||
public void testThatCreateClientSSLEngineWithoutAnySettingsWorks() throws Exception {
|
||||
ClientSSLService sslService = new ClientSSLService(Settings.EMPTY, env);
|
||||
ClientSSLService sslService = createClientSSLService(Settings.EMPTY);
|
||||
SSLEngine sslEngine = sslService.createSSLEngine();
|
||||
assertThat(sslEngine, notNullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThatCreateSSLEngineWithOnlyTruststoreWorks() throws Exception {
|
||||
ClientSSLService sslService = new ClientSSLService(settingsBuilder()
|
||||
ClientSSLService sslService = createClientSSLService(settingsBuilder()
|
||||
.put("shield.ssl.truststore.path", testclientStore)
|
||||
.put("shield.ssl.truststore.password", "testclient")
|
||||
.build(), env);
|
||||
.build());
|
||||
SSLEngine sslEngine = sslService.createSSLEngine();
|
||||
assertThat(sslEngine, notNullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThatCreateSSLEngineWithOnlyKeystoreWorks() throws Exception {
|
||||
ClientSSLService sslService = new ClientSSLService(settingsBuilder()
|
||||
ClientSSLService sslService = createClientSSLService(settingsBuilder()
|
||||
.put("shield.ssl.keystore.path", testclientStore)
|
||||
.put("shield.ssl.keystore.password", "testclient")
|
||||
.build(), env);
|
||||
.build());
|
||||
SSLEngine sslEngine = sslService.createSSLEngine();
|
||||
assertThat(sslEngine, notNullValue());
|
||||
}
|
||||
|
@ -174,7 +174,7 @@ public class ClientSSLServiceTests extends ESTestCase {
|
|||
@Test
|
||||
@Network
|
||||
public void testThatSSLContextWithoutSettingsWorks() throws Exception {
|
||||
ClientSSLService sslService = new ClientSSLService(Settings.EMPTY, env);
|
||||
ClientSSLService sslService = createClientSSLService(Settings.EMPTY);
|
||||
SSLContext sslContext = sslService.sslContext();
|
||||
try (CloseableHttpClient client = HttpClients.custom().setSslcontext(sslContext).build()) {
|
||||
// Execute a GET on a site known to have a valid certificate signed by a trusted public CA
|
||||
|
@ -187,10 +187,10 @@ public class ClientSSLServiceTests extends ESTestCase {
|
|||
@Test
|
||||
@Network
|
||||
public void testThatSSLContextWithKeystoreDoesNotTrustAllPublicCAs() throws Exception {
|
||||
ClientSSLService sslService = new ClientSSLService(settingsBuilder()
|
||||
ClientSSLService sslService = createClientSSLService(settingsBuilder()
|
||||
.put("shield.ssl.keystore.path", testclientStore)
|
||||
.put("shield.ssl.keystore.password", "testclient")
|
||||
.build(), env);
|
||||
.build());
|
||||
SSLContext sslContext = sslService.sslContext();
|
||||
try (CloseableHttpClient client = HttpClients.custom().setSslcontext(sslContext).build()) {
|
||||
// Execute a GET on a site known to have a valid certificate signed by a trusted public CA
|
||||
|
@ -205,17 +205,17 @@ public class ClientSSLServiceTests extends ESTestCase {
|
|||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testThatTruststorePasswordIsRequired() throws Exception {
|
||||
ClientSSLService sslService = new ClientSSLService(settingsBuilder()
|
||||
ClientSSLService sslService = createClientSSLService(settingsBuilder()
|
||||
.put("shield.ssl.truststore.path", testclientStore)
|
||||
.build(), env);
|
||||
.build());
|
||||
sslService.sslContext();
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testThatKeystorePasswordIsRequired() throws Exception {
|
||||
ClientSSLService sslService = new ClientSSLService(settingsBuilder()
|
||||
ClientSSLService sslService = createClientSSLService(settingsBuilder()
|
||||
.put("shield.ssl.keystore.path", testclientStore)
|
||||
.build(), env);
|
||||
.build());
|
||||
sslService.sslContext();
|
||||
}
|
||||
|
||||
|
@ -224,9 +224,9 @@ public class ClientSSLServiceTests extends ESTestCase {
|
|||
List<String> ciphers = new ArrayList<>(Arrays.asList(AbstractSSLService.DEFAULT_CIPHERS));
|
||||
ciphers.add("foo");
|
||||
ciphers.add("bar");
|
||||
ClientSSLService sslService = new ClientSSLService(settingsBuilder()
|
||||
ClientSSLService sslService = createClientSSLService(settingsBuilder()
|
||||
.putArray("shield.ssl.ciphers", ciphers.toArray(new String[ciphers.size()]))
|
||||
.build(), env);
|
||||
.build());
|
||||
SSLEngine engine = sslService.createSSLEngine();
|
||||
assertThat(engine, is(notNullValue()));
|
||||
String[] enabledCiphers = engine.getEnabledCipherSuites();
|
||||
|
@ -235,18 +235,18 @@ public class ClientSSLServiceTests extends ESTestCase {
|
|||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void invalidCiphersOnlyThrowsException() throws Exception {
|
||||
ClientSSLService sslService = new ClientSSLService(settingsBuilder()
|
||||
ClientSSLService sslService = createClientSSLService(settingsBuilder()
|
||||
.putArray("shield.ssl.ciphers", new String[] { "foo", "bar" })
|
||||
.build(), env);
|
||||
.build());
|
||||
sslService.createSSLEngine();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThatSSLSocketFactoryHasProperCiphersAndProtocols() throws Exception {
|
||||
ClientSSLService sslService = new ClientSSLService(settingsBuilder()
|
||||
ClientSSLService sslService = createClientSSLService(settingsBuilder()
|
||||
.put("shield.ssl.keystore.path", testclientStore)
|
||||
.put("shield.ssl.keystore.password", "testclient")
|
||||
.build(), env);
|
||||
.build());
|
||||
SSLSocketFactory factory = sslService.sslSocketFactory();
|
||||
assertThat(factory.getDefaultCipherSuites(), is(sslService.ciphers()));
|
||||
|
||||
|
@ -255,4 +255,10 @@ public class ClientSSLServiceTests extends ESTestCase {
|
|||
assertThat(socket.getEnabledProtocols(), is(sslService.supportedProtocols()));
|
||||
}
|
||||
}
|
||||
|
||||
ClientSSLService createClientSSLService(Settings settings) {
|
||||
ClientSSLService clientSSLService = new ClientSSLService(settings);
|
||||
clientSSLService.setEnvironment(env);
|
||||
return clientSSLService;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,7 +49,8 @@ public class ShieldNettyTransportTests extends ESTestCase {
|
|||
Environment env = new Environment(settingsBuilder().put("path.home", createTempDir()).build());
|
||||
settingsFilter = new ShieldSettingsFilter(settings, new SettingsFilter(settings));
|
||||
serverSSLService = new ServerSSLService(settings, settingsFilter, env);
|
||||
clientSSLService = new ClientSSLService(settings, env);
|
||||
clientSSLService = new ClientSSLService(settings);
|
||||
clientSSLService.setEnvironment(env);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -13,7 +13,6 @@ import org.elasticsearch.ElasticsearchException;
|
|||
import org.elasticsearch.client.transport.TransportClient;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.transport.TransportAddress;
|
||||
import org.elasticsearch.env.Environment;
|
||||
import org.elasticsearch.http.HttpServerTransport;
|
||||
import org.elasticsearch.node.Node;
|
||||
import org.elasticsearch.shield.ShieldPlugin;
|
||||
|
@ -73,8 +72,7 @@ public class SslClientAuthTests extends ShieldIntegTestCase {
|
|||
@Test
|
||||
public void testThatHttpWorksWithSslClientAuth() throws IOException {
|
||||
Settings settings = settingsBuilder().put(ShieldSettingsSource.getSSLSettingsForStore("/org/elasticsearch/shield/transport/ssl/certs/simple/testclient.jks", "testclient")).build();
|
||||
Environment env = new Environment(settingsBuilder().put("path.home", createTempDir()).build());
|
||||
ClientSSLService sslService = new ClientSSLService(settings, env);
|
||||
ClientSSLService sslService = new ClientSSLService(settings);
|
||||
|
||||
SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(
|
||||
sslService.sslContext(),
|
||||
|
|
|
@ -21,7 +21,6 @@ import org.elasticsearch.common.io.Streams;
|
|||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.transport.InetSocketTransportAddress;
|
||||
import org.elasticsearch.common.transport.TransportAddress;
|
||||
import org.elasticsearch.env.Environment;
|
||||
import org.elasticsearch.http.HttpServerTransport;
|
||||
import org.elasticsearch.node.Node;
|
||||
import org.elasticsearch.shield.ssl.ClientSSLService;
|
||||
|
@ -95,8 +94,7 @@ public class SslIntegrationTests extends ShieldIntegTestCase {
|
|||
@Test
|
||||
public void testThatConnectionToHTTPWorks() throws Exception {
|
||||
Settings settings = ShieldSettingsSource.getSSLSettingsForStore("/org/elasticsearch/shield/transport/ssl/certs/simple/testclient.jks", "testclient");
|
||||
Environment env = new Environment(settingsBuilder().put("path.home", createTempDir()).build());
|
||||
ClientSSLService service = new ClientSSLService(settings, env);
|
||||
ClientSSLService service = new ClientSSLService(settings);
|
||||
|
||||
CredentialsProvider provider = new BasicCredentialsProvider();
|
||||
provider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(nodeClientUsername(), new String(nodeClientPassword().internalChars())));
|
||||
|
|
Loading…
Reference in New Issue