diff --git a/feature-pack/src/main/resources/feature_groups/elytron-with-tls.xml b/feature-pack/src/main/resources/feature_groups/elytron-with-tls.xml deleted file mode 100644 index dfc7e07..0000000 --- a/feature-pack/src/main/resources/feature_groups/elytron-with-tls.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/feature-pack/src/main/resources/modules/system/layers/base/org/wildfly/extension/elytron-tls/main/module.xml b/feature-pack/src/main/resources/modules/system/layers/base/org/wildfly/extension/elytron-tls/main/module.xml index 5115aed..2751183 100644 --- a/feature-pack/src/main/resources/modules/system/layers/base/org/wildfly/extension/elytron-tls/main/module.xml +++ b/feature-pack/src/main/resources/modules/system/layers/base/org/wildfly/extension/elytron-tls/main/module.xml @@ -38,6 +38,7 @@ + \ No newline at end of file diff --git a/pom.xml b/pom.xml index 2cac87a..d66403f 100644 --- a/pom.xml +++ b/pom.xml @@ -46,6 +46,8 @@ + 4.1.71.Final + 2.3 1.1.6 1.69 1.1.6 @@ -325,6 +327,68 @@ + + + io.netty + netty-buffer + ${version.io.netty} + test + + + io.netty + netty-codec + ${version.io.netty} + test + + + io.netty + netty-codec-http + ${version.io.netty} + test + + + io.netty + netty-codec-socks + ${version.io.netty} + test + + + io.netty + netty-common + ${version.io.netty} + test + + + io.netty + netty-handler + ${version.io.netty} + test + + + io.netty + netty-handler-proxy + ${version.io.netty} + test + + + io.netty + netty-transport + ${version.io.netty} + test + + + + org.apache.velocity + velocity-engine-core + ${version.org.apache.velocity} + test + + + org.apache.velocity + velocity-engine-scripting + ${version.org.apache.velocity} + test + org.bouncycastle bcpkix-jdk15on @@ -353,6 +417,36 @@ mockserver-netty ${version.org.mockserver} test + + + com.google.code.findbugs + jsr305 + + + org.apache.commons + commons-lang3 + + + com.google.guava + guava + + + org.bouncycastle + bcmail-jdk15on + + + org.bouncycastle + bcprov-jdk15on + + + org.bouncycastle + bcpkix-jdk15on + + + org.bouncycastle + bcutil-jdk15on + + org.wildfly.core diff --git a/subsystem/src/main/java/org/wildfly/extension/elytron/tls/subsystem/Capabilities.java b/subsystem/src/main/java/org/wildfly/extension/elytron/tls/subsystem/Capabilities.java index 8ff9bbf..fd27f48 100644 --- a/subsystem/src/main/java/org/wildfly/extension/elytron/tls/subsystem/Capabilities.java +++ b/subsystem/src/main/java/org/wildfly/extension/elytron/tls/subsystem/Capabilities.java @@ -57,12 +57,22 @@ */ class Capabilities { + // This has to be at this position, and must not be a lambda, to avoid an init circularity problem on IBM + @SuppressWarnings("Convert2Lambda") + static final Consumer COMMON_REQUIREMENTS = new Consumer() { + // unchecked because ServiceBuilder is a raw type + @SuppressWarnings("unchecked") + public void accept(final ServiceBuilder serviceBuilder) { + ElytronTlsSubsystemDefinition.commonRequirements(serviceBuilder); + } + }; + private static final String WILDFLY_SECURITY_CAPABILITY_BASE = "org.wildfly.security."; static final String ELYTRON_TLS_SUBSYSTEM_CAPABILITY_NAME = "org.wildfly.extras.elytron-tls"; - static final RuntimeCapability ELYTRON_TLS_RUNTIME_CAPABILITY = RuntimeCapability.Builder - .of(ELYTRON_TLS_SUBSYSTEM_CAPABILITY_NAME) + static final RuntimeCapability> ELYTRON_TLS_RUNTIME_CAPABILITY = RuntimeCapability.Builder + .of(ELYTRON_TLS_SUBSYSTEM_CAPABILITY_NAME, COMMON_REQUIREMENTS) .addRequirements(ElytronTlsExtension.WELD_CAPABILITY_NAME) .build(); @@ -104,24 +114,6 @@ class Capabilities { static final String DIR_CONTEXT_CAPABILITY = WILDFLY_SECURITY_CAPABILITY_BASE + "dir-context"; - // This has to be at this position, and must not be a lambda, to avoid an init circularity problem on IBM - @SuppressWarnings("Convert2Lambda") - static final Consumer COMMON_DEPENDENCIES = new Consumer() { - // unchecked because ServiceBuilder is a raw type - @SuppressWarnings("unchecked") - public void accept(final ServiceBuilder serviceBuilder) { - //ElytronDefinition.commonDependencies(serviceBuilder); - //TODO: - } - }; - - static final String ELYTRON_CAPABILITY = WILDFLY_SECURITY_CAPABILITY_BASE + "elytron"; - - - static final RuntimeCapability> ELYTRON_RUNTIME_CAPABILITY = RuntimeCapability - .Builder.of(ELYTRON_CAPABILITY, COMMON_DEPENDENCIES) - .build(); - static final String EVIDENCE_DECODER_CAPABILITY = WILDFLY_SECURITY_CAPABILITY_BASE + "evidence-decoder"; static final RuntimeCapability EVIDENCE_DECODER_RUNTIME_CAPABILITY = RuntimeCapability diff --git a/subsystem/src/main/java/org/wildfly/extension/elytron/tls/subsystem/SSLContextDefinitions.java b/subsystem/src/main/java/org/wildfly/extension/elytron/tls/subsystem/SSLContextDefinitions.java index 0148492..1311bae 100644 --- a/subsystem/src/main/java/org/wildfly/extension/elytron/tls/subsystem/SSLContextDefinitions.java +++ b/subsystem/src/main/java/org/wildfly/extension/elytron/tls/subsystem/SSLContextDefinitions.java @@ -69,7 +69,6 @@ import org.jboss.as.controller.AbstractAddStepHandler; import org.jboss.as.controller.AttributeDefinition; -import org.jboss.as.controller.MapAttributeDefinition; import org.jboss.as.controller.ModelVersion; import org.jboss.as.controller.ObjectListAttributeDefinition; import org.jboss.as.controller.ObjectTypeAttributeDefinition; @@ -80,7 +79,6 @@ import org.jboss.as.controller.ResourceDefinition; import org.jboss.as.controller.SimpleAttributeDefinition; import org.jboss.as.controller.SimpleAttributeDefinitionBuilder; -import org.jboss.as.controller.SimpleMapAttributeDefinition; import org.jboss.as.controller.SimpleOperationDefinitionBuilder; import org.jboss.as.controller.StringListAttributeDefinition; import org.jboss.as.controller.capability.RuntimeCapability; @@ -445,7 +443,8 @@ public class SSLContextDefinitions { /* SSL Context definitions */ - static final SimpleAttributeDefinition DEFAULT_SSL_CONTEXT = new SimpleAttributeDefinitionBuilder(Constants.DEFAULT_SSL_CONTEXT, ModelType.STRING) + // TODO implement SNI + /* static final SimpleAttributeDefinition DEFAULT_SSL_CONTEXT = new SimpleAttributeDefinitionBuilder(Constants.DEFAULT_SSL_CONTEXT, ModelType.STRING) .setRequired(true) .setCapabilityReference(SSL_CONTEXT_CAPABILITY) .setRestartAllServices() @@ -457,7 +456,7 @@ public class SSLContextDefinitions { .setMapValidator(new Validators.HostContextMapValidator()) .setAllowExpression(false) .setRestartAllServices() - .build(); + .build(); */ /* Runtime Attributes */ @@ -1011,9 +1010,9 @@ protected ValueSupplier getValueSupplier(ServiceBuilder final String cipherSuiteFilter = CIPHER_SUITE_FILTER.resolveModelAttribute(context, model).asString(); final String cipherSuiteNames = CIPHER_SUITE_NAMES.resolveModelAttribute(context, model).asStringOrNull(); - final InjectedValue providersInjector = addSSLContextDependency(PROVIDERS_CAPABILITY, providersDefinition, Provider[].class, serviceBuilder, context, model); - final InjectedValue keyManagerInjector = addSSLContextDependency(KEY_MANAGER_CAPABILITY, KEY_MANAGER, KeyManager.class, serviceBuilder, context, model); - final InjectedValue trustManagerInjector = addSSLContextDependency(TRUST_MANAGER_CAPABILITY, TRUST_MANAGER, TrustManager.class, serviceBuilder, context, model);; + final InjectedValue providersInjector = addDependency(PROVIDERS_CAPABILITY, providersDefinition, Provider[].class, serviceBuilder, context, model); + final InjectedValue keyManagerInjector = addDependency(KEY_MANAGER_CAPABILITY, KEY_MANAGER, KeyManager.class, serviceBuilder, context, model); + final InjectedValue trustManagerInjector = addDependency(TRUST_MANAGER_CAPABILITY, TRUST_MANAGER, TrustManager.class, serviceBuilder, context, model); // final ModelNode keyManagerObject = KEY_MANAGER_OBJECT.resolveModelAttribute(context, model); // final ModelNode trustManagerObject = TRUST_MANAGER_OBJECT.resolveModelAttribute(context, model); @@ -1136,9 +1135,9 @@ protected ValueSupplier getValueSupplier(ServiceBuilder final int sessionTimeout = SESSION_TIMEOUT.resolveModelAttribute(context, model).asInt(); final boolean wrap = WRAP.resolveModelAttribute(context, model).asBoolean(); - final InjectedValue providersInjector = addSSLContextDependency(PROVIDERS_CAPABILITY, providersDefinition, Provider[].class, serviceBuilder, context, model); - InjectedValue keyManagerInjector = addSSLContextDependency(KEY_MANAGER_CAPABILITY, KEY_MANAGER, KeyManager.class, serviceBuilder, context, model); - InjectedValue trustManagerInjector = addSSLContextDependency(TRUST_MANAGER_CAPABILITY, TRUST_MANAGER, TrustManager.class, serviceBuilder, context, model);; + final InjectedValue providersInjector = addDependency(PROVIDERS_CAPABILITY, providersDefinition, Provider[].class, serviceBuilder, context, model); + InjectedValue keyManagerInjector = addDependency(KEY_MANAGER_CAPABILITY, KEY_MANAGER, KeyManager.class, serviceBuilder, context, model); + InjectedValue trustManagerInjector = addDependency(TRUST_MANAGER_CAPABILITY, TRUST_MANAGER, TrustManager.class, serviceBuilder, context, model);; // final ModelNode keyManagerObject = KEY_MANAGER_OBJECT.resolveModelAttribute(context, model); // final ModelNode trustManagerObject = TRUST_MANAGER_OBJECT.resolveModelAttribute(context, model); @@ -1812,7 +1811,7 @@ private static X509ExtendedTrustManager getX509TrustManager(TrustManager trustMa if (trustManager == null) { return null; } - if (trustManager instanceof X509ExtendedKeyManager) { + if (trustManager instanceof X509ExtendedTrustManager) { X509ExtendedTrustManager x509TrustManager = (X509ExtendedTrustManager) trustManager; // TODO: add FIPS /* if (x509TrustManager instanceof DelegatingTrustManager && IS_FIPS.getAsBoolean()) { @@ -1926,7 +1925,7 @@ private static ExceptionSupplier createKeyStore(ServiceBuil ModelNode model, InjectedValue providersInjector) throws OperationFailedException { return null; } // Derives dynamic name from provided attribute - private static InjectedValue addSSLContextDependency(String baseName, SimpleAttributeDefinition attribute, + private static InjectedValue addDependency(String baseName, SimpleAttributeDefinition attribute, Class type, ServiceBuilder serviceBuilder, OperationContext context, ModelNode model) throws OperationFailedException { String dynamicNameElement = attribute.resolveModelAttribute(context, model).asStringOrNull(); InjectedValue injectedValue = new InjectedValue<>(); @@ -1938,32 +1937,6 @@ private static InjectedValue addSSLContextDependency(String baseName, Sim return injectedValue; } - private static InjectedValue addKeyManagerDependency(String baseName, SimpleAttributeDefinition attribute, - Class type, ServiceBuilder serviceBuilder, OperationContext context, ModelNode model) throws OperationFailedException { - String dynamicNameElement = attribute.resolveModelAttribute(context, model).asStringOrNull(); - InjectedValue injectedValue = new InjectedValue<>(); - - if (dynamicNameElement != null) { - serviceBuilder.addDependency(context.getCapabilityServiceName( - buildDynamicCapabilityName(baseName, dynamicNameElement), type), - type, injectedValue); - } - return injectedValue; - } - - private static InjectedValue addTrustManagerDependency(String baseName, SimpleAttributeDefinition attribute, - Class type, ServiceBuilder serviceBuilder, OperationContext context, ModelNode model) throws OperationFailedException { - String dynamicNameElement = attribute.resolveModelAttribute(context, model).asStringOrNull(); - InjectedValue injectedValue = new InjectedValue<>(); - - if (dynamicNameElement != null) { - serviceBuilder.addDependency(context.getCapabilityServiceName( - buildDynamicCapabilityName(baseName, dynamicNameElement), type), - type, injectedValue); - } - return injectedValue; - } - private static Provider[] filterProviders(Provider[] all, String provider) { if (provider == null || all == null) return all; List list = new ArrayList<>(); diff --git a/subsystem/src/main/java/org/wildfly/extension/elytron/tls/subsystem/TrivialService.java b/subsystem/src/main/java/org/wildfly/extension/elytron/tls/subsystem/TrivialService.java index 43a4f68..80de780 100644 --- a/subsystem/src/main/java/org/wildfly/extension/elytron/tls/subsystem/TrivialService.java +++ b/subsystem/src/main/java/org/wildfly/extension/elytron/tls/subsystem/TrivialService.java @@ -32,7 +32,7 @@ * * @author Darran Lofthouse */ -class TrivialService implements Service, org.jboss.msc.Service { +class TrivialService implements Service { private volatile ValueSupplier valueSupplier; private final Consumer valueConsumer; diff --git a/subsystem/src/test/java/org/wildfly/extension/elytron/tls/subsystem/CertificateAuthoritiesTestCase.java b/subsystem/src/test/java/org/wildfly/extension/elytron/tls/subsystem/CertificateAuthoritiesTestCase.java index a5059af..e92c321 100644 --- a/subsystem/src/test/java/org/wildfly/extension/elytron/tls/subsystem/CertificateAuthoritiesTestCase.java +++ b/subsystem/src/test/java/org/wildfly/extension/elytron/tls/subsystem/CertificateAuthoritiesTestCase.java @@ -131,7 +131,7 @@ public void init() throws Exception { } else { subsystemXml = JdkUtils.getJavaSpecVersion() <= 12 ? "tls-sun.xml" : "tls-oracle13plus.xml"; } - services = super.createKernelServicesBuilder(TestEnvironment.asNormal()).setSubsystemXmlResource(subsystemXml).build(); + services = super.createKernelServicesBuilder(new TestEnvironment()).setSubsystemXmlResource(subsystemXml).build(); if (!services.isSuccessfulBoot()) { Assert.fail(services.getBootError().toString()); } diff --git a/subsystem/src/test/java/org/wildfly/extension/elytron/tls/subsystem/CredentialStoreTestCase.java b/subsystem/src/test/java/org/wildfly/extension/elytron/tls/subsystem/CredentialStoreTestCase.java new file mode 100644 index 0000000..20bca63 --- /dev/null +++ b/subsystem/src/test/java/org/wildfly/extension/elytron/tls/subsystem/CredentialStoreTestCase.java @@ -0,0 +1,527 @@ +/* + * Copyright 2019 Red Hat, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.wildfly.extension.elytron.tls.subsystem; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.FAILED; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OUTCOME; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SUCCESS; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.wildfly.security.encryption.SecretKeyUtil.importSecretKey; + +import java.security.AccessController; +import java.security.GeneralSecurityException; +import java.security.PrivilegedAction; +import java.security.Provider; +import java.security.Security; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import javax.crypto.SecretKey; + +import org.jboss.as.controller.client.helpers.ClientConstants; +import org.jboss.as.subsystem.test.AbstractSubsystemTest; +import org.jboss.as.subsystem.test.KernelServices; +import org.jboss.dmr.ModelNode; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.wildfly.security.WildFlyElytronProvider; +import org.wildfly.security.credential.SecretKeyCredential; +import org.wildfly.security.encryption.CipherUtil; + +/** + * Test case testing operations against a credential store using the management operations. + * + * @author Darran Lofthouse + */ +public class CredentialStoreTestCase extends AbstractSubsystemTest { + + private static final Provider PROVIDER = new WildFlyElytronProvider(); + + private static final String CLEAR_TEXT = "Lorem ipsum dolor sit amet"; + private static final String CONFIGURATION = "credential-store.xml"; + + private KernelServices services = null; + + public CredentialStoreTestCase() { + super(ElytronTlsExtension.SUBSYSTEM_NAME, new ElytronTlsExtension()); + } + + @Before + public void init() throws Exception { + services = super.createKernelServicesBuilder(new TestEnvironment()).setSubsystemXmlResource(CONFIGURATION).build(); + if (!services.isSuccessfulBoot()) { + Assert.fail(services.getBootError().toString()); + } + + } + + @BeforeClass + public static void initTests() throws Exception { + AccessController.doPrivileged(new PrivilegedAction() { + public Integer run() { + return Security.insertProviderAt(PROVIDER, 1); + } + }); + } + + @AfterClass + public static void cleanUpTests() { + AccessController.doPrivileged(new PrivilegedAction() { + public Void run() { + Security.removeProvider(PROVIDER.getName()); + + return null; + } + }); + } + + // Test Contents of dynamically initialised credential stores + + @Test + public void testDefaultContentTest() throws GeneralSecurityException { + testDefaultContent("credential-store", "test", null, -1); + } + + @Test + public void testDefaultContentTestEmpty() throws GeneralSecurityException { + testDefaultContent("secret-key-credential-store", "testEmpty", null, -1); + } + + @Test + public void testDefaultContentTest128() throws GeneralSecurityException { + testDefaultContent("secret-key-credential-store", "test128", "key", 128); + } + + @Test + public void testDefaultContentTest192() throws GeneralSecurityException { + testDefaultContent("secret-key-credential-store", "test192", "192", 192); + } + + @Test + public void testDefaultContentTest256() throws GeneralSecurityException { + testDefaultContent("secret-key-credential-store", "test256", "key", 256); + } + + private void testDefaultContent(final String storeType, final String storeName, String expectedAlias, int expectedKeySize) throws GeneralSecurityException { + // First Check The Default Aliases + ModelNode readAliases = new ModelNode(); + readAliases.get(ClientConstants.OP_ADDR).add("subsystem", "elytron-tls").add(storeType, storeName); + readAliases.get(ClientConstants.OP).set("read-aliases"); + + ModelNode aliases = assertSuccess(services.executeOperation(readAliases)).get("result"); + System.out.println(aliases.toString()); + List aliasValues = aliases.asList(); + assertEquals("Expected alias count", expectedAlias != null ? 1 : 0, aliasValues.size()); + if (expectedAlias != null) { + assertEquals("Expected alias name", expectedAlias, aliasValues.get(0).asString()); + + // Export the generated SecretKey + ModelNode export = new ModelNode(); + export.get(ClientConstants.OP_ADDR).add("subsystem", "elytron-tls").add(storeType, storeName); + export.get(ClientConstants.OP).set("export-secret-key"); + export.get(Constants.ALIAS).set(expectedAlias); + ModelNode exportResult = assertSuccess(services.executeOperation(export)); + + final String key = exportResult.get(ClientConstants.RESULT).get(Constants.KEY).asString(); + SecretKey secretKey = importSecretKey(key); + // Key Sizes are in bits. + assertEquals("Expected Key Size", expectedKeySize, secretKey.getEncoded().length * 8); + } + } + + // Add SecretKey Flow for secret-key-credential-store but take into account the resource also has a default key size. + + @Test + public void testSecretKeyFlowDefault() throws GeneralSecurityException { + testSecretKeyGenerateExportImport("credential-store", "test", 256, true, true); + testSecretKeyGenerateExportImport("secret-key-credential-store", "test128", 128, true, false); + testSecretKeyGenerateExportImport("secret-key-credential-store", "test192", 192, true, false); + testSecretKeyGenerateExportImport("secret-key-credential-store", "test256", 256, true, false); + } + + @Test + public void testSecretKeyFlow128() throws GeneralSecurityException { + testSecretKeyGenerateExportImport("credential-store", "test", 128, false, true); + // Use a store which does not default to 128 bits. + testSecretKeyGenerateExportImport("secret-key-credential-store", "test256", 128, false, false); + } + + @Test + public void testSecretKeyFlow192() throws GeneralSecurityException { + testSecretKeyGenerateExportImport("credential-store", "test", 192, false, true); + // Use a store which does not default to 192 bits. + testSecretKeyGenerateExportImport("secret-key-credential-store", "test256", 192, false, false); + } + + @Test + public void testSecretKeyFlow256() throws GeneralSecurityException { + testSecretKeyGenerateExportImport("credential-store", "test", 256, false, true); + // Use a store which does not default to 256 bits. + testSecretKeyGenerateExportImport("secret-key-credential-store", "test128", 256, false, false); + } + + private void testSecretKeyGenerateExportImport(final String storeType, final String storeName, final int keySize, + boolean omitKeySize, boolean entryTypeRequired) throws GeneralSecurityException { + final String alias = "test"; + + // Generate and store a new SecretKey using the specified keySize. + ModelNode generate = new ModelNode(); + generate.get(ClientConstants.OP_ADDR).add("subsystem", "elytron-tls").add(storeType, storeName); + generate.get(ClientConstants.OP).set("generate-secret-key"); + generate.get(Constants.ALIAS).set(alias); + if (!omitKeySize) { + if (keySize > 0) { + generate.get(Constants.KEY_SIZE).set(keySize); + } + } + assertSuccess(services.executeOperation(generate)); + + // Export the generated SecretKey + ModelNode export = new ModelNode(); + export.get(ClientConstants.OP_ADDR).add("subsystem", "elytron-tls").add(storeType, storeName); + export.get(ClientConstants.OP).set("export-secret-key"); + export.get(Constants.ALIAS).set(alias); + ModelNode exportResult = assertSuccess(services.executeOperation(export)); + + final String key = exportResult.get(ClientConstants.RESULT).get(Constants.KEY).asString(); + + SecretKey secretKey = importSecretKey(key); + // Key Sizes are in bits. + assertEquals("Expected Key Size", keySize, secretKey.getEncoded().length * 8); + + + String importAlias = alias + "Import"; + // Import the previously exported SecretKey + ModelNode importKey = new ModelNode(); + importKey.get(ClientConstants.OP_ADDR).add("subsystem", "elytron-tls").add(storeType, storeName); + importKey.get(ClientConstants.OP).set("import-secret-key"); + importKey.get(Constants.ALIAS).set(importAlias); + importKey.get(Constants.KEY).set(key); + assertSuccess(services.executeOperation(importKey)); + + // Re-export so keys can be compared + ModelNode export2 = new ModelNode(); + export2.get(ClientConstants.OP_ADDR).add("subsystem", "elytron-tls").add(storeType, storeName); + export2.get(ClientConstants.OP).set("export-secret-key"); + export2.get(Constants.ALIAS).set(importAlias); + ModelNode exportResult2 = assertSuccess(services.executeOperation(export2)); + + final String key2 = exportResult2.get(ClientConstants.RESULT).get(Constants.KEY).asString(); + assertNotNull("Exported SecretKey", key); + assertEquals("Matching keys", key, key2); + + // Remove aliases + ModelNode remove = new ModelNode(); + remove.get(ClientConstants.OP_ADDR).add("subsystem", "elytron-tls").add(storeType, storeName); + remove.get(ClientConstants.OP).set("remove-alias"); + if (entryTypeRequired) { + remove.get(Constants.ENTRY_TYPE).set(SecretKeyCredential.class.getSimpleName()); + } + remove.get(Constants.ALIAS).set(alias); + assertSuccess(services.executeOperation(remove)); + + ModelNode remove2 = new ModelNode(); + remove2.get(ClientConstants.OP_ADDR).add("subsystem", "elytron-tls").add(storeType, storeName); + remove2.get(ClientConstants.OP).set("remove-alias"); + if (entryTypeRequired) { + remove2.get(Constants.ENTRY_TYPE).set(SecretKeyCredential.class.getSimpleName()); + } + remove2.get(Constants.ALIAS).set(importAlias); + assertSuccess(services.executeOperation(remove2)); + } + + @Test + public void testCreateExpression() throws Exception { + // First obtain the generated SecretKey as we will want this to test any expressions can be decrypted. + ModelNode export = new ModelNode(); + export.get(ClientConstants.OP_ADDR).add("subsystem", "elytron-tls").add("secret-key-credential-store", "test256"); + export.get(ClientConstants.OP).set("export-secret-key"); + export.get(Constants.ALIAS).set("key"); + ModelNode exportResult = assertSuccess(services.executeOperation(export)); + + final String key = exportResult.get(ClientConstants.RESULT).get(Constants.KEY).asString(); + + SecretKey secretKey = importSecretKey(key); + + ModelNode createExpression = new ModelNode(); + createExpression.get(ClientConstants.OP_ADDR).add("subsystem", "elytron-tls").add("expression", "encryption"); + createExpression.get(ClientConstants.OP).set("create-expression"); + createExpression.get(Constants.RESOLVER).set("A"); + createExpression.get(Constants.CLEAR_TEXT).set(CLEAR_TEXT); + ModelNode createExpressionResult = assertSuccess(services.executeOperation(createExpression)); + + String expression = createExpressionResult.get(ClientConstants.RESULT).get(Constants.EXPRESSION).asString(); + assertEquals("Expected Expression Prefix", "${CIPHER::A:", expression.substring(0, 12)); + String cipherTextToken = expression.substring(12, expression.length() - 1); + assertEquals("Decrypted value", CLEAR_TEXT, CipherUtil.decrypt(cipherTextToken, secretKey)); + + createExpression = new ModelNode(); + createExpression.get(ClientConstants.OP_ADDR).add("subsystem", "elytron-tls").add("expression", "encryption"); + createExpression.get(ClientConstants.OP).set("create-expression"); + createExpression.get(Constants.CLEAR_TEXT).set(CLEAR_TEXT); + createExpressionResult = assertSuccess(services.executeOperation(createExpression)); + + expression = createExpressionResult.get(ClientConstants.RESULT).get(Constants.EXPRESSION).asString(); + assertEquals("Expected Expression Prefix", "${CIPHER::", expression.substring(0, 10)); + cipherTextToken = expression.substring(10, expression.length() - 1); + assertEquals("Decrypted value", CLEAR_TEXT, CipherUtil.decrypt(cipherTextToken, secretKey)); + } + + @Test + public void testErrorsOfDoubleOperationsInFlow() throws GeneralSecurityException { + testErrorsOfDoubleOperationsInFlow("credential-store", "test", true); + testErrorsOfDoubleOperationsInFlow("secret-key-credential-store", "test128", false); + } + + private void testErrorsOfDoubleOperationsInFlow(final String storeType, final String storeName, + boolean entryTypeRequired) throws GeneralSecurityException { + + final String alias = "test"; + + // Generate and store a new SecretKey. + ModelNode generate = new ModelNode(); + generate.get(ClientConstants.OP_ADDR).add("subsystem", "elytron-tls").add(storeType, storeName); + generate.get(ClientConstants.OP).set("generate-secret-key"); + generate.get(Constants.ALIAS).set(alias); + assertSuccess(services.executeOperation(generate)); + + ModelNode doubleGenerateResult = assertFailed(services.executeOperation(generate)); + assertThat(doubleGenerateResult.get(ClientConstants.FAILURE_DESCRIPTION).asString(), containsString("ELYTLS0913:")); + + // Export the generated SecretKey + ModelNode export = new ModelNode(); + export.get(ClientConstants.OP_ADDR).add("subsystem", "elytron-tls").add(storeType, storeName); + export.get(ClientConstants.OP).set("export-secret-key"); + export.get(Constants.ALIAS).set(alias); + ModelNode exportResult = assertSuccess(services.executeOperation(export)); + + final String key = exportResult.get(ClientConstants.RESULT).get(Constants.KEY).asString(); + + String importAlias = alias + "Import"; + // Import the previously exported SecretKey + ModelNode importKey = new ModelNode(); + importKey.get(ClientConstants.OP_ADDR).add("subsystem", "elytron-tls").add(storeType, storeName); + importKey.get(ClientConstants.OP).set("import-secret-key"); + importKey.get(Constants.ALIAS).set(importAlias); + importKey.get(Constants.KEY).set(key); + assertSuccess(services.executeOperation(importKey)); + + ModelNode doubleImportResult = assertFailed(services.executeOperation(importKey)); + assertThat(doubleImportResult.get(ClientConstants.FAILURE_DESCRIPTION).asString(), containsString("ELYTLS0913:")); + + // Remove aliases + ModelNode remove = new ModelNode(); + remove.get(ClientConstants.OP_ADDR).add("subsystem", "elytron-tls").add(storeType, storeName); + remove.get(ClientConstants.OP).set("remove-alias"); + if (entryTypeRequired) { + remove.get(Constants.ENTRY_TYPE).set(SecretKeyCredential.class.getSimpleName()); + } + remove.get(Constants.ALIAS).set(alias); + assertSuccess(services.executeOperation(remove)); + + ModelNode remove2 = new ModelNode(); + remove2.get(ClientConstants.OP_ADDR).add("subsystem", "elytron-tls").add(storeType, storeName); + remove2.get(ClientConstants.OP).set("remove-alias"); + if (entryTypeRequired) { + remove2.get(Constants.ENTRY_TYPE).set(SecretKeyCredential.class.getSimpleName()); + } + remove2.get(Constants.ALIAS).set(importAlias); + assertSuccess(services.executeOperation(remove2)); + + ModelNode doubleRemoveResult = assertFailed(services.executeOperation(remove2)); + assertThat(doubleRemoveResult.get(ClientConstants.FAILURE_DESCRIPTION).asString(), containsString("ELYTLS0920:")); + } + + @Test + public void testExportNonExistingAlias() throws GeneralSecurityException { + testExportNonExistingAlias("credential-store", "test"); + testExportNonExistingAlias("secret-key-credential-store", "test192"); + } + + private void testExportNonExistingAlias(final String storeType, final String storeName) throws GeneralSecurityException { + final String alias = "test"; + + // Export the generated SecretKey + ModelNode export = new ModelNode(); + export.get(ClientConstants.OP_ADDR).add("subsystem", "elytron-tls").add(storeType, storeName); + export.get(ClientConstants.OP).set("export-secret-key"); + export.get(Constants.ALIAS).set(alias); + ModelNode exportResult = assertFailed(services.executeOperation(export)); + assertThat(exportResult.get(ClientConstants.FAILURE_DESCRIPTION).asString(), containsString("ELYTLS0920:")); + } + + @Test + public void testImportInvalidSecretKey() throws GeneralSecurityException { + testImportInvalidSecretKey("credential-store", "test"); + testImportInvalidSecretKey("secret-key-credential-store", "test256"); + } + + private void testImportInvalidSecretKey(final String storeType, final String storeName) throws GeneralSecurityException { + final String alias = "test"; + + // Generate and store a new SecretKey. + ModelNode generate = new ModelNode(); + generate.get(ClientConstants.OP_ADDR).add("subsystem", "elytron-tls").add(storeType, storeName); + generate.get(ClientConstants.OP).set("generate-secret-key"); + generate.get(Constants.ALIAS).set(alias); + assertSuccess(services.executeOperation(generate)); + + // Export the generated SecretKey + ModelNode export = new ModelNode(); + export.get(ClientConstants.OP_ADDR).add("subsystem", "elytron-tls").add(storeType, storeName); + export.get(ClientConstants.OP).set("export-secret-key"); + export.get(Constants.ALIAS).set(alias); + ModelNode exportResult = assertSuccess(services.executeOperation(export)); + + final String key = exportResult.get(ClientConstants.RESULT).get(Constants.KEY).asString(); + final String truncatedKey = key.substring(0, key.length() - 2); + + String importAlias = alias + "Import"; + // Import the previously exported and truncated SecretKey + ModelNode importKey = new ModelNode(); + importKey.get(ClientConstants.OP_ADDR).add("subsystem", "elytron-tls").add(storeType, storeName); + importKey.get(ClientConstants.OP).set("import-secret-key"); + importKey.get(Constants.ALIAS).set(importAlias); + importKey.get(Constants.KEY).set(truncatedKey); + ModelNode importResult = assertFailed(services.executeOperation(importKey)); + assertThat(importResult.get(ClientConstants.FAILURE_DESCRIPTION).asString(), containsString("ELY19004:")); + + // Remove aliases + ModelNode remove = new ModelNode(); + remove.get(ClientConstants.OP_ADDR).add("subsystem", "elytron-tls").add(storeType, storeName); + remove.get(ClientConstants.OP).set("remove-alias"); + remove.get(Constants.ENTRY_TYPE).set(SecretKeyCredential.class.getSimpleName()); + remove.get(Constants.ALIAS).set(alias); + assertSuccess(services.executeOperation(remove)); + } + + @Test + public void testRemoveAliasOfEntryType() throws GeneralSecurityException { + final String alias = "test"; + + testExpectedAliases("credential-store", "test"); + + // Generate and store a new SecretKey. + ModelNode generateSecretKey = new ModelNode(); + generateSecretKey.get(ClientConstants.OP_ADDR).add("subsystem", "elytron-tls").add("credential-store", "test"); + generateSecretKey.get(ClientConstants.OP).set("generate-secret-key"); + generateSecretKey.get(Constants.ALIAS).set(alias); + assertSuccess(services.executeOperation(generateSecretKey)); + + testExpectedAliases("credential-store", "test", alias); + + // Store a new Password. + ModelNode addPassword = new ModelNode(); + addPassword.get(ClientConstants.OP_ADDR).add("subsystem", "elytron-tls").add("credential-store", "test"); + addPassword.get(ClientConstants.OP).set("add-alias"); + addPassword.get(Constants.ALIAS).set(alias); + addPassword.get(Constants.SECRET_VALUE).set("password"); + assertSuccess(services.executeOperation(addPassword)); + + testExpectedAliases("credential-store", "test", alias); + + // Remove SecretKeyCredential alias + ModelNode removeSecretKey = new ModelNode(); + removeSecretKey.get(ClientConstants.OP_ADDR).add("subsystem", "elytron-tls").add("credential-store", "test"); + removeSecretKey.get(ClientConstants.OP).set("remove-alias"); + removeSecretKey.get(Constants.ENTRY_TYPE).set(SecretKeyCredential.class.getSimpleName()); + removeSecretKey.get(Constants.ALIAS).set(alias); + assertSuccess(services.executeOperation(removeSecretKey)); + + testExpectedAliases("credential-store", "test", alias); + + // Generate and store a new SecretKey again. + ModelNode generateSecretKeyAgain = new ModelNode(); + generateSecretKeyAgain.get(ClientConstants.OP_ADDR).add("subsystem", "elytron-tls").add("credential-store", "test"); + generateSecretKeyAgain.get(ClientConstants.OP).set("generate-secret-key"); + generateSecretKeyAgain.get(Constants.ALIAS).set(alias); + assertSuccess(services.executeOperation(generateSecretKeyAgain)); + + testExpectedAliases("credential-store", "test", alias); + + // Remove PasswordCredential alias (default type) + ModelNode removePassword = new ModelNode(); + removePassword.get(ClientConstants.OP_ADDR).add("subsystem", "elytron-tls").add("credential-store", "test"); + removePassword.get(ClientConstants.OP).set("remove-alias"); + removePassword.get(Constants.ALIAS).set(alias); + assertSuccess(services.executeOperation(removePassword)); + + testExpectedAliases("credential-store", "test", alias); + + // Remove SecretKeyCredential alias again + ModelNode removeSecretKeyAgain = new ModelNode(); + removeSecretKeyAgain.get(ClientConstants.OP_ADDR).add("subsystem", "elytron-tls").add("credential-store", "test"); + removeSecretKeyAgain.get(ClientConstants.OP).set("remove-alias"); + removeSecretKeyAgain.get(Constants.ENTRY_TYPE).set(SecretKeyCredential.class.getSimpleName()); + removeSecretKeyAgain.get(Constants.ALIAS).set(alias); + assertSuccess(services.executeOperation(removeSecretKeyAgain)); + + testExpectedAliases("credential-store", "test"); + } + + private void testExpectedAliases(String resourceType, String resourceName, + String... expectedAliases) throws GeneralSecurityException { + + ModelNode readAliases = new ModelNode(); + readAliases.get(ClientConstants.OP_ADDR).add("subsystem", "elytron-tls").add(resourceType, resourceName); + readAliases.get(ClientConstants.OP).set(Constants.READ_ALIASES); + + ModelNode readAliasesResult = assertSuccess(services.executeOperation(readAliases)); + + ModelNode result = readAliasesResult.get(ClientConstants.RESULT); + if (expectedAliases.length > 0) { + Set expectedSet = new HashSet<>(Arrays.asList(expectedAliases)); + + List aliasList = result.asList(); + for (ModelNode aliasNode : aliasList) { + String alias = aliasNode.asString(); + if (!expectedSet.remove(alias)) { + fail("Alias '" + alias + "' not expected."); + } + } + + assertEquals("All expected aliases found", 0, expectedSet.size()); + } else { + assertTrue("No aliases expected", result.asList().isEmpty()); + } + } + + private static ModelNode assertSuccess(ModelNode response) { + if (!response.get(OUTCOME).asString().equals(SUCCESS)) { + Assert.fail(response.toJSONString(false)); + } + return response; + } + + private static ModelNode assertFailed(ModelNode response) { + if (!response.get(OUTCOME).asString().equals(FAILED)) { + Assert.fail(response.toJSONString(false)); + } + return response; + } + +} diff --git a/subsystem/src/test/java/org/wildfly/extension/elytron/tls/subsystem/CredentialStoreUpdatesTestCase.java b/subsystem/src/test/java/org/wildfly/extension/elytron/tls/subsystem/CredentialStoreUpdatesTestCase.java new file mode 100644 index 0000000..8c306bf --- /dev/null +++ b/subsystem/src/test/java/org/wildfly/extension/elytron/tls/subsystem/CredentialStoreUpdatesTestCase.java @@ -0,0 +1,782 @@ +/* + * JBoss, Home of Professional Open Source + * + * Copyright 2019 Red Hat, Inc. and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.wildfly.extension.elytron.tls.subsystem; + +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.FAILED; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.FAILURE_DESCRIPTION; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.NAME; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OUTCOME; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.RESULT; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SUCCESS; +import static org.jboss.as.controller.security.CredentialReference.ALIAS; +import static org.jboss.as.controller.security.CredentialReference.CLEAR_TEXT; +import static org.jboss.as.controller.security.CredentialReference.CREDENTIAL_REFERENCE; +import static org.jboss.as.controller.security.CredentialReference.NEW_ALIAS; +import static org.jboss.as.controller.security.CredentialReference.STORE; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.security.Provider; +import java.security.Security; +import java.util.Arrays; +import java.util.Objects; + +import org.jboss.as.controller.client.helpers.ClientConstants; +import org.jboss.as.controller.security.CredentialReference; +import org.jboss.as.subsystem.test.AbstractSubsystemTest; +import org.jboss.as.subsystem.test.KernelServices; +import org.jboss.dmr.ModelNode; +import org.jboss.msc.service.ServiceName; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.wildfly.security.WildFlyElytronProvider; +import org.wildfly.security.credential.PasswordCredential; +import org.wildfly.security.credential.store.CredentialStore; +import org.wildfly.security.credential.store.CredentialStoreException; +import org.wildfly.security.password.interfaces.ClearPassword; + +/** + * Tests for automatic updates of credential stores. + * + * @author Farah Juma + */ +public class CredentialStoreUpdatesTestCase extends AbstractSubsystemTest { + + private static final String EMPTY_CS_PASSWORD = "super_secret1"; + private static final String NON_EMPTY_CS_PASSWORD = "super_secret2"; + private static final String EMPTY_CS_NAME = "store1"; + private static final String EMPTY_CS_PATH = "target/test.credential.store1"; + private static final String NON_EMPTY_CS_NAME = "store2"; + private static final String NON_EMPTY_CS_PATH = "target/test.credential.store2"; + private static final String KS_NAME = "test-keystore"; + private static final String CLEAR_TEXT_ATTRIBUTE_NAME = CredentialReference.CREDENTIAL_REFERENCE + "." + CLEAR_TEXT; + private static final String ALIAS_ATTRIBUTE_NAME = CredentialReference.CREDENTIAL_REFERENCE + "." + ALIAS; + private static final String STORE_ATTRIBUTE_NAME = CredentialReference.CREDENTIAL_REFERENCE + "." + STORE; + private static final String EXISTING_ALIAS = "existingAlias"; + private static final String EXISTING_PASSWORD = "existingPassword"; + private static final Provider wildFlyElytronProvider = new WildFlyElytronProvider(); + private static CredentialStoreUtility emptyCSUtil = null; + private static CredentialStoreUtility nonEmptyCSUtil = null; + private KernelServices services = null; + + public CredentialStoreUpdatesTestCase() { + super(ElytronTlsExtension.SUBSYSTEM_NAME, new ElytronTlsExtension()); + } + + @BeforeClass + public static void initTests() { + AccessController.doPrivileged(new PrivilegedAction() { + public Integer run() { + return Security.insertProviderAt(wildFlyElytronProvider, 1); + } + }); + } + + @Before + public void init() throws Exception { + services = super.createKernelServicesBuilder(new TestEnvironment()).setSubsystemXmlResource("credential-store-updates.xml").build(); + if (!services.isSuccessfulBoot()) { + Assert.fail(services.getBootError().toString()); + } + emptyCSUtil = new CredentialStoreUtility(EMPTY_CS_PATH, EMPTY_CS_PASSWORD); + nonEmptyCSUtil = new CredentialStoreUtility(NON_EMPTY_CS_PATH, NON_EMPTY_CS_PASSWORD); + } + + @After + public void cleanUpCredentialStores() { + emptyCSUtil.cleanUp(); + nonEmptyCSUtil.cleanUp(); + } + + @AfterClass + public static void cleanUpTests() { + AccessController.doPrivileged(new PrivilegedAction() { + public Void run() { + Security.removeProvider(wildFlyElytronProvider.getName()); + return null; + } + }); + } + + @Test + public void testCredentialReferenceAddNewEntryToEmptyCredentialStore() throws Exception { + String alias = "newAlias"; + String password = "newPassword"; + try { + CredentialStore credentialStore = getCredentialStore(EMPTY_CS_NAME); + assertEquals(0, credentialStore.getAliases().size()); + + addKeyStoreWithCredentialReference(KS_NAME, EMPTY_CS_NAME, alias, password, false); + assertEquals(1, credentialStore.getAliases().size()); + assertTrue(credentialStore.exists(alias, PasswordCredential.class)); + PasswordCredential passwordCredential = credentialStore.retrieve(alias, PasswordCredential.class); + ClearPassword clearPassword = passwordCredential.getPassword(ClearPassword.class); + assertArrayEquals(password.toCharArray(), clearPassword.getPassword()); + + assertNull(readAttribute(KS_NAME, CLEAR_TEXT_ATTRIBUTE_NAME)); + assertEquals(alias, readAttribute(KS_NAME, ALIAS_ATTRIBUTE_NAME)); + } finally { + removeKeyStore(KS_NAME); + } + } + + @Test + public void testCredentialReferenceAddNewEntryWithGeneratedAliasToEmptyCredentialStore() throws Exception { + String password = "newPassword"; + try { + CredentialStore credentialStore = getCredentialStore(EMPTY_CS_NAME); + assertEquals(0, credentialStore.getAliases().size()); + + String generatedAlias = addKeyStoreWithCredentialReference(KS_NAME, EMPTY_CS_NAME,null, password, false); + assertEquals(1, credentialStore.getAliases().size()); + assertTrue(credentialStore.exists(generatedAlias, PasswordCredential.class)); + PasswordCredential passwordCredential = credentialStore.retrieve(generatedAlias, PasswordCredential.class); + ClearPassword clearPassword = passwordCredential.getPassword(ClearPassword.class); + assertArrayEquals(password.toCharArray(), clearPassword.getPassword()); + + assertNull(readAttribute(KS_NAME, CLEAR_TEXT_ATTRIBUTE_NAME)); + assertEquals(generatedAlias, readAttribute(KS_NAME, ALIAS_ATTRIBUTE_NAME)); + } finally { + removeKeyStore(KS_NAME); + } + } + + @Test + public void testCredentialReferenceAddNewEntry() throws Exception { + String alias = "newAlias"; + String password = "newPassword"; + try { + CredentialStore credentialStore = getNonEmptyCredentialStore(); + + assertFalse(credentialStore.exists(alias, PasswordCredential.class)); + assertEquals(1, credentialStore.getAliases().size()); + + addKeyStoreWithCredentialReference(KS_NAME, NON_EMPTY_CS_NAME, alias, password, false); + assertEquals(2, credentialStore.getAliases().size()); + assertTrue(credentialStore.exists(alias, PasswordCredential.class)); + PasswordCredential passwordCredential = credentialStore.retrieve(alias, PasswordCredential.class); + ClearPassword clearPassword = passwordCredential.getPassword(ClearPassword.class); + assertArrayEquals(password.toCharArray(), clearPassword.getPassword()); + + assertNull(readAttribute(KS_NAME, CLEAR_TEXT_ATTRIBUTE_NAME)); + assertEquals(alias, readAttribute(KS_NAME, ALIAS_ATTRIBUTE_NAME)); + } finally { + removeKeyStore(KS_NAME); + } + } + + @Test + public void testCredentialReferenceAddNewEntryWithGeneratedAlias() throws Exception { + String password = "newPassword"; + try { + CredentialStore credentialStore = getNonEmptyCredentialStore(); + + assertEquals(1, credentialStore.getAliases().size()); + + String generatedAlias = addKeyStoreWithCredentialReference(KS_NAME, NON_EMPTY_CS_NAME, null, password, false); + assertEquals(2, credentialStore.getAliases().size()); + assertTrue(credentialStore.exists(generatedAlias, PasswordCredential.class)); + PasswordCredential passwordCredential = credentialStore.retrieve(generatedAlias, PasswordCredential.class); + ClearPassword clearPassword = passwordCredential.getPassword(ClearPassword.class); + assertArrayEquals(password.toCharArray(), clearPassword.getPassword()); + + assertNull(readAttribute(KS_NAME, CLEAR_TEXT_ATTRIBUTE_NAME)); + assertEquals(generatedAlias, readAttribute(KS_NAME, ALIAS_ATTRIBUTE_NAME)); + } finally { + removeKeyStore(KS_NAME); + } + } + + @Test + public void testCredentialReferenceUpdateExistingEntry() throws Exception { + String newPassword = "newPassword"; + try { + CredentialStore credentialStore = getNonEmptyCredentialStore(); + + assertTrue(credentialStore.exists(EXISTING_ALIAS, PasswordCredential.class)); + PasswordCredential passwordCredential = credentialStore.retrieve(EXISTING_ALIAS, PasswordCredential.class); + ClearPassword clearPassword = passwordCredential.getPassword(ClearPassword.class); + assertArrayEquals(EXISTING_PASSWORD.toCharArray(), clearPassword.getPassword()); + assertEquals(1, credentialStore.getAliases().size()); + + addKeyStoreWithCredentialReference(KS_NAME, NON_EMPTY_CS_NAME, EXISTING_ALIAS, newPassword, true); + assertEquals(1, credentialStore.getAliases().size()); + assertTrue(credentialStore.exists(EXISTING_ALIAS, PasswordCredential.class)); + passwordCredential = credentialStore.retrieve(EXISTING_ALIAS, PasswordCredential.class); + clearPassword = passwordCredential.getPassword(ClearPassword.class); + assertArrayEquals(newPassword.toCharArray(), clearPassword.getPassword()); + + assertNull(readAttribute(KS_NAME, CLEAR_TEXT_ATTRIBUTE_NAME)); + assertEquals(EXISTING_ALIAS, readAttribute(KS_NAME, ALIAS_ATTRIBUTE_NAME)); + } finally { + removeKeyStore(KS_NAME); + } + } + + @Test + public void testCredentialReferenceAddNewEntryFromOperation() throws Exception { + try { + CredentialStore credentialStore = getNonEmptyCredentialStore(); + addKeyStoreWithCredentialReference(KS_NAME, NON_EMPTY_CS_NAME, EXISTING_ALIAS, null, true, false); + + String alias = "newAlias"; + String password = "newPassword"; + assertFalse(credentialStore.exists(alias, PasswordCredential.class)); + assertEquals(1, credentialStore.getAliases().size()); + + // specify a credential-reference when executing a key-store operation + generateKeyPairWithCredentialStoreUpdate(KS_NAME, NON_EMPTY_CS_NAME, alias, password, false); + assertEquals(2, credentialStore.getAliases().size()); + assertTrue(credentialStore.exists(alias, PasswordCredential.class)); + PasswordCredential passwordCredential = credentialStore.retrieve(alias, PasswordCredential.class); + ClearPassword clearPassword = passwordCredential.getPassword(ClearPassword.class); + assertArrayEquals(password.toCharArray(), clearPassword.getPassword()); + + assertNull(readAttribute(KS_NAME, CLEAR_TEXT_ATTRIBUTE_NAME)); + assertEquals(EXISTING_ALIAS, readAttribute(KS_NAME, ALIAS_ATTRIBUTE_NAME)); + } finally { + removeKeyStore(KS_NAME); + } + } + + @Test + public void testCredentialReferenceAddNewEntryWithGeneratedAliasFromOperation() throws Exception { + try { + CredentialStore credentialStore = getNonEmptyCredentialStore(); + addKeyStoreWithCredentialReference(KS_NAME, NON_EMPTY_CS_NAME, EXISTING_ALIAS, null, true, false); + + String password = "newPassword"; + assertEquals(1, credentialStore.getAliases().size()); + + // specify a credential-reference when executing a key-store operation + String generatedAlias = generateKeyPairWithCredentialStoreUpdate(KS_NAME, NON_EMPTY_CS_NAME, null, password, false); + assertEquals(2, credentialStore.getAliases().size()); + assertTrue(credentialStore.exists(generatedAlias, PasswordCredential.class)); + PasswordCredential passwordCredential = credentialStore.retrieve(generatedAlias, PasswordCredential.class); + ClearPassword clearPassword = passwordCredential.getPassword(ClearPassword.class); + assertArrayEquals(password.toCharArray(), clearPassword.getPassword()); + + assertNull(readAttribute(KS_NAME, CLEAR_TEXT_ATTRIBUTE_NAME)); + assertEquals(EXISTING_ALIAS, readAttribute(KS_NAME, ALIAS_ATTRIBUTE_NAME)); + } finally { + removeKeyStore(KS_NAME); + } + } + + @Test + public void testCredentialReferenceUpdateExistingEntryFromOperation() throws Exception { + try { + addKeyStoreWithCredentialReference(KS_NAME, EMPTY_CS_NAME, "alias1", "secret", false, false); + + CredentialStore credentialStore = getNonEmptyCredentialStore(); + assertEquals(1, credentialStore.getAliases().size()); + assertTrue(credentialStore.exists(EXISTING_ALIAS, PasswordCredential.class)); + PasswordCredential passwordCredential = credentialStore.retrieve(EXISTING_ALIAS, PasswordCredential.class); + ClearPassword clearPassword = passwordCredential.getPassword(ClearPassword.class); + assertArrayEquals(EXISTING_PASSWORD.toCharArray(), clearPassword.getPassword()); + + // specify a credential-reference when executing a key-store operation + String password = "newPassword"; + generateKeyPairWithCredentialStoreUpdate(KS_NAME, NON_EMPTY_CS_NAME, EXISTING_ALIAS, password, true); + + assertEquals(1, credentialStore.getAliases().size()); + assertTrue(credentialStore.exists(EXISTING_ALIAS, PasswordCredential.class)); + passwordCredential = credentialStore.retrieve(EXISTING_ALIAS, PasswordCredential.class); + clearPassword = passwordCredential.getPassword(ClearPassword.class); + assertArrayEquals(password.toCharArray(), clearPassword.getPassword()); + + assertNull(readAttribute(KS_NAME, CLEAR_TEXT_ATTRIBUTE_NAME)); + assertEquals("alias1", readAttribute(KS_NAME, ALIAS_ATTRIBUTE_NAME)); + } finally { + removeKeyStore(KS_NAME); + } + } + + @Test + public void testCredentialReferenceUpdateExistingEntryFromWriteAttributeOperation() throws Exception { + try { + CredentialStore credentialStore = getNonEmptyCredentialStore(); + + assertTrue(credentialStore.exists(EXISTING_ALIAS, PasswordCredential.class)); + PasswordCredential passwordCredential = credentialStore.retrieve(EXISTING_ALIAS, PasswordCredential.class); + ClearPassword clearPassword = passwordCredential.getPassword(ClearPassword.class); + assertArrayEquals(EXISTING_PASSWORD.toCharArray(), clearPassword.getPassword()); + assertEquals(1, credentialStore.getAliases().size()); + addKeyStoreWithCredentialReference(KS_NAME, NON_EMPTY_CS_NAME, EXISTING_ALIAS, null, true); + + String password = "newPassword"; + writeClearTextAttribute("key-store", KS_NAME, password); + + assertEquals(1, credentialStore.getAliases().size()); + assertTrue(credentialStore.exists(EXISTING_ALIAS, PasswordCredential.class)); + passwordCredential = credentialStore.retrieve(EXISTING_ALIAS, PasswordCredential.class); + clearPassword = passwordCredential.getPassword(ClearPassword.class); + assertArrayEquals(password.toCharArray(), clearPassword.getPassword()); + + assertNull(readAttribute(KS_NAME, CLEAR_TEXT_ATTRIBUTE_NAME)); + assertEquals(EXISTING_ALIAS, readAttribute(KS_NAME, ALIAS_ATTRIBUTE_NAME)); + } finally { + removeKeyStore(KS_NAME); + } + } + + @Test + public void testCredentialReferenceNoUpdate() throws Exception { + try { + CredentialStore credentialStore = getNonEmptyCredentialStore(); + + assertTrue(credentialStore.exists(EXISTING_ALIAS, PasswordCredential.class)); + PasswordCredential passwordCredential = credentialStore.retrieve(EXISTING_ALIAS, PasswordCredential.class); + ClearPassword clearPassword = passwordCredential.getPassword(ClearPassword.class); + assertArrayEquals(EXISTING_PASSWORD.toCharArray(), clearPassword.getPassword()); + assertEquals(1, credentialStore.getAliases().size()); + + addKeyStoreWithCredentialReference(KS_NAME, NON_EMPTY_CS_NAME, EXISTING_ALIAS, null, true); + assertEquals(1, credentialStore.getAliases().size()); + assertTrue(credentialStore.exists(EXISTING_ALIAS, PasswordCredential.class)); + passwordCredential = credentialStore.retrieve(EXISTING_ALIAS, PasswordCredential.class); + clearPassword = passwordCredential.getPassword(ClearPassword.class); + assertArrayEquals(EXISTING_PASSWORD.toCharArray(), clearPassword.getPassword()); + + assertNull(readAttribute(KS_NAME, CLEAR_TEXT_ATTRIBUTE_NAME)); + assertEquals(EXISTING_ALIAS, readAttribute(KS_NAME, ALIAS_ATTRIBUTE_NAME)); + } finally { + removeKeyStore(KS_NAME); + } + } + + @Test + public void testCredentialReferenceRollbackOfNewlyAddedCredentialDuringAddOperation() throws Exception { + String alias = "newAlias"; + String password = "newPassword"; + + CredentialStore credentialStore = getNonEmptyCredentialStore(); + + assertFalse(credentialStore.exists(alias, PasswordCredential.class)); + assertEquals(1, credentialStore.getAliases().size()); + + + // Add an invalid key-store, specify a credential-reference attribute that will result in a new entry being added + // to the credential store. The new entry will be rolled back when the key-store add operation fails + addKeyStoreWithCredentialReference(KS_NAME, NON_EMPTY_CS_NAME, alias, password, "InvalidType", false, true, true); + assertEquals(1, credentialStore.getAliases().size()); + assertFalse(credentialStore.exists(alias, PasswordCredential.class)); + + } + + @Test + public void testCredentialReferenceRollbackOfUpdatedExistingCredentialDuringAddOperation() throws Exception { + String password = "newPassword"; + + CredentialStore credentialStore = getNonEmptyCredentialStore(); + + assertTrue(credentialStore.exists(EXISTING_ALIAS, PasswordCredential.class)); + PasswordCredential passwordCredential = credentialStore.retrieve(EXISTING_ALIAS, PasswordCredential.class); + ClearPassword clearPassword = passwordCredential.getPassword(ClearPassword.class); + assertArrayEquals(EXISTING_PASSWORD.toCharArray(), clearPassword.getPassword()); + assertEquals(1, credentialStore.getAliases().size()); + + // Add an invalid key-store, specify a credential-reference attribute that will result in an existing entry being updated + // in the credential store. The updated entry will be rolled back to its previous value when the key-store add operation fails + addKeyStoreWithCredentialReference(KS_NAME, NON_EMPTY_CS_NAME, EXISTING_ALIAS, password, "InvalidType", false, true, true); + assertEquals(1, credentialStore.getAliases().size()); + assertTrue(credentialStore.exists(EXISTING_ALIAS, PasswordCredential.class)); + passwordCredential = credentialStore.retrieve(EXISTING_ALIAS, PasswordCredential.class); + clearPassword = passwordCredential.getPassword(ClearPassword.class); + assertArrayEquals(EXISTING_PASSWORD.toCharArray(), clearPassword.getPassword()); // password should remain unchanged + } + + @Test + public void testFailedAddOperationWithClearTextAttributeOnly() throws Exception { + String password = "secret"; + addKeyStoreWithCredentialReference(KS_NAME, null, null, password, "InvalidType", false, false, true); + } + + @Test + public void testCredentialReferenceRollbackOfNewlyAddedCredentialDuringRuntimeOperation() throws Exception { + String alias = "newAlias"; + String password = "newPassword"; + addKeyStore(); + CredentialStore credentialStore = getNonEmptyCredentialStore(); + + assertFalse(credentialStore.exists(alias, PasswordCredential.class)); + assertEquals(1, credentialStore.getAliases().size()); + try { + ModelNode operation = new ModelNode(); + operation.get(ClientConstants.OP_ADDR).add("subsystem", "elytron-tls").add("key-store", KS_NAME); + operation.get(ClientConstants.OP).set(Constants.GENERATE_KEY_PAIR); + operation.get(Constants.ALIAS).set("bsmith"); + operation.get(Constants.ALGORITHM).set("Invalid"); + operation.get(Constants.DISTINGUISHED_NAME).set("CN=bob smith, OU=jboss, O=red hat, L=raleigh, ST=north carolina, C=us"); + operation.get(CredentialReference.CREDENTIAL_REFERENCE).get(STORE).set(NON_EMPTY_CS_NAME); + operation.get(CredentialReference.CREDENTIAL_REFERENCE).get(ALIAS).set(alias); + operation.get(CredentialReference.CREDENTIAL_REFERENCE).get(CLEAR_TEXT).set(password); + ModelNode response = assertFailed(services.executeOperation(operation)).get(RESULT); + validateFailedResponse(response); + + assertEquals(1, credentialStore.getAliases().size()); + assertFalse(credentialStore.exists(alias, PasswordCredential.class)); + } finally { + removeKeyStore(KS_NAME); + } + } + + @Test + public void testCredentialReferenceRollbackOfUpdatedExistingCredentialDuringRuntimeOperation() throws Exception { + String password = "newPassword"; + addKeyStore(); + + CredentialStore credentialStore = getNonEmptyCredentialStore(); + + assertTrue(credentialStore.exists(EXISTING_ALIAS, PasswordCredential.class)); + PasswordCredential passwordCredential = credentialStore.retrieve(EXISTING_ALIAS, PasswordCredential.class); + ClearPassword clearPassword = passwordCredential.getPassword(ClearPassword.class); + assertArrayEquals(EXISTING_PASSWORD.toCharArray(), clearPassword.getPassword()); + assertEquals(1, credentialStore.getAliases().size()); + try { + ModelNode operation = new ModelNode(); + operation.get(ClientConstants.OP_ADDR).add("subsystem", "elytron-tls").add("key-store", KS_NAME); + operation.get(ClientConstants.OP).set(Constants.GENERATE_KEY_PAIR); + operation.get(Constants.ALIAS).set("bsmith"); + operation.get(Constants.ALGORITHM).set("Invalid"); + operation.get(Constants.DISTINGUISHED_NAME).set("CN=bob smith, OU=jboss, O=red hat, L=raleigh, ST=north carolina, C=us"); + operation.get(CredentialReference.CREDENTIAL_REFERENCE).get(STORE).set(NON_EMPTY_CS_NAME); + operation.get(CredentialReference.CREDENTIAL_REFERENCE).get(ALIAS).set(EXISTING_ALIAS); + operation.get(CredentialReference.CREDENTIAL_REFERENCE).get(CLEAR_TEXT).set(password); + ModelNode response = assertFailed(services.executeOperation(operation)).get(RESULT); + validateFailedResponse(response); + + assertEquals(1, credentialStore.getAliases().size()); + assertTrue(credentialStore.exists(EXISTING_ALIAS, PasswordCredential.class)); + passwordCredential = credentialStore.retrieve(EXISTING_ALIAS, PasswordCredential.class); + clearPassword = passwordCredential.getPassword(ClearPassword.class); + assertArrayEquals(EXISTING_PASSWORD.toCharArray(), clearPassword.getPassword()); // password should remain unchanged + } finally { + removeKeyStore(KS_NAME); + } + } + + @Test + public void testFailedRuntimeOperationWithClearTextAttributeOnly() throws Exception { + addKeyStore(); + try { + ModelNode operation = new ModelNode(); + operation.get(ClientConstants.OP_ADDR).add("subsystem", "elytron-tls").add("key-store", KS_NAME); + operation.get(ClientConstants.OP).set(Constants.GENERATE_KEY_PAIR); + operation.get(Constants.ALIAS).set("bsmith"); + operation.get(Constants.ALGORITHM).set("Invalid"); + operation.get(Constants.DISTINGUISHED_NAME).set("CN=bob smith, OU=jboss, O=red hat, L=raleigh, ST=north carolina, C=us"); + operation.get(CredentialReference.CREDENTIAL_REFERENCE).get(CLEAR_TEXT).set("secret"); + assertFailed(services.executeOperation(operation)).get(RESULT); + } finally { + removeKeyStore(KS_NAME); + } + } + + @Test + public void testAddOperationWithCredentialReferenceWithStoreOnly() throws Exception { + testAddKeyStoreWithInvalidCredentialReference(KS_NAME, NON_EMPTY_CS_NAME, null, null); + } + + @Test + public void testAddOperationWithCredentialReferenceWithAliasOnly() throws Exception { + testAddKeyStoreWithInvalidCredentialReference(KS_NAME, null, EXISTING_ALIAS, null); + } + + @Test + public void testAddOperationWithCredentialReferenceWithClearTextOnly() throws Exception { + try { + String password = "secret"; + addKeyStoreWithCredentialReference(KS_NAME, null, null, password, false, false); + assertEquals(password, readAttribute(KS_NAME, CLEAR_TEXT_ATTRIBUTE_NAME)); + assertNull(readAttribute(KS_NAME, ALIAS_ATTRIBUTE_NAME)); + } finally { + removeKeyStore(KS_NAME); + } + } + + @Test + public void testWriteAttributeWithCredentialReferenceWithStoreOnly() throws Exception { + try { + getNonEmptyCredentialStore(); + addKeyStoreWithCredentialReference(KS_NAME, NON_EMPTY_CS_NAME, EXISTING_ALIAS, null, true); + testWriteCredentialReferenceAttribute("key-store", KS_NAME, NON_EMPTY_CS_NAME, null, null, true); + + // no changes to credential-reference attribute + assertNull(readAttribute(KS_NAME, CLEAR_TEXT_ATTRIBUTE_NAME)); + assertEquals(EXISTING_ALIAS, readAttribute(KS_NAME, ALIAS_ATTRIBUTE_NAME)); + assertEquals(NON_EMPTY_CS_NAME, readAttribute(KS_NAME, STORE_ATTRIBUTE_NAME)); + } finally { + removeKeyStore(KS_NAME); + } + } + + @Test + public void testWriteAttributeWithCredentialReferenceWithAliasOnly() throws Exception { + try { + getNonEmptyCredentialStore(); + addKeyStoreWithCredentialReference(KS_NAME, NON_EMPTY_CS_NAME, EXISTING_ALIAS, null, true); + testWriteCredentialReferenceAttribute("key-store", KS_NAME, null, NEW_ALIAS, null, true); + + // no changes to credential-reference attribute + assertNull(readAttribute(KS_NAME, CLEAR_TEXT_ATTRIBUTE_NAME)); + assertEquals(EXISTING_ALIAS, readAttribute(KS_NAME, ALIAS_ATTRIBUTE_NAME)); + assertEquals(NON_EMPTY_CS_NAME, readAttribute(KS_NAME, STORE_ATTRIBUTE_NAME)); + } finally { + removeKeyStore(KS_NAME); + } + } + + @Test + public void testWriteAttributeWithCredentialReferenceWithClearTextOnly() throws Exception { + try { + String newPassword = "newPassword"; + getNonEmptyCredentialStore(); + addKeyStoreWithCredentialReference(KS_NAME, NON_EMPTY_CS_NAME, EXISTING_ALIAS, null, true); + testWriteCredentialReferenceAttribute("key-store", KS_NAME, null, null, newPassword, false); + + // changes to credential-reference attribute + assertEquals(newPassword, readAttribute(KS_NAME, CLEAR_TEXT_ATTRIBUTE_NAME)); + assertNull(readAttribute(KS_NAME, ALIAS_ATTRIBUTE_NAME)); + assertNull(readAttribute(KS_NAME, STORE_ATTRIBUTE_NAME)); + } finally { + removeKeyStore(KS_NAME); + } + } + + private String addKeyStoreWithCredentialReference(String keyStoreName, String store, String alias, String secret, boolean exists) throws Exception { + return addKeyStoreWithCredentialReference(keyStoreName, store, alias, secret, "JKS", exists, true, false); + } + + private String addKeyStoreWithCredentialReference(String keyStoreName, String store, String alias, String secret, boolean exists, boolean validateResponse) throws Exception { + return addKeyStoreWithCredentialReference(keyStoreName, store, alias, secret, "JKS", exists, validateResponse, false); + } + + private String addKeyStoreWithCredentialReference(String keyStoreName, String store, String alias, String secret, String type, boolean exists, boolean validateResponse) throws Exception { + return addKeyStoreWithCredentialReference(keyStoreName, store, alias, secret, type, exists, validateResponse, false); + } + + private String addKeyStoreWithCredentialReference(String keyStoreName, String store, String alias, String secret, String type, boolean exists, boolean validateResponse, boolean assertFailed) throws Exception { + Path resources = Paths.get(Objects.requireNonNull(KeyStoresTestCase.class.getResource(".")).toURI()); + ModelNode operation = new ModelNode(); + operation.get(ClientConstants.OPERATION_HEADERS).get("allow-resource-service-restart").set(Boolean.TRUE); + operation.get(ClientConstants.OP_ADDR).add("subsystem","elytron-tls").add("key-store", keyStoreName); + operation.get(ClientConstants.OP).set(ClientConstants.ADD); + operation.get(Constants.PATH).set(resources + "/test.keystore"); + operation.get(Constants.TYPE).set(type); + if (store != null) { + operation.get(CredentialReference.CREDENTIAL_REFERENCE).get(STORE).set(store); + } + boolean autoGeneratedAlias = false; + if (alias != null) { + operation.get(CredentialReference.CREDENTIAL_REFERENCE).get(ALIAS).set(alias); + } else { + autoGeneratedAlias = true; + } + if (secret != null) { + operation.get(CredentialReference.CREDENTIAL_REFERENCE).get(CLEAR_TEXT).set(secret); + } + if (assertFailed) { + ModelNode response = assertFailed(services.executeOperation(operation)).get(RESULT); + if (validateResponse) { + return validateFailedResponse(response); + } else { + return null; + } + } else { + ModelNode response = assertSuccess(services.executeOperation(operation)).get(RESULT); + if (validateResponse) { + return validateResponse(response, secret, autoGeneratedAlias, exists); + } else { + return null; + } + } + } + + private void testAddKeyStoreWithInvalidCredentialReference(String keyStoreName, String store, String alias, String secret) throws Exception { + Path resources = Paths.get(Objects.requireNonNull(KeyStoresTestCase.class.getResource(".")).toURI()); + ModelNode operation = new ModelNode(); + operation.get(ClientConstants.OPERATION_HEADERS).get("allow-resource-service-restart").set(Boolean.TRUE); + operation.get(ClientConstants.OP_ADDR).add("subsystem","elytron-tls").add("key-store", keyStoreName); + operation.get(ClientConstants.OP).set(ClientConstants.ADD); + operation.get(Constants.PATH).set(resources + "/test.keystore"); + operation.get(Constants.TYPE).set("JKS"); + if (store != null) { + operation.get(CredentialReference.CREDENTIAL_REFERENCE).get(STORE).set(store); + } + if (alias != null) { + operation.get(CredentialReference.CREDENTIAL_REFERENCE).get(ALIAS).set(alias); + } + if (secret != null) { + operation.get(CredentialReference.CREDENTIAL_REFERENCE).get(CLEAR_TEXT).set(secret); + } + + ModelNode response = services.executeOperation(operation); + assertEquals(FAILED, response.get(OUTCOME).asString()); + assertTrue(response.get(FAILURE_DESCRIPTION).asString().contains(CREDENTIAL_REFERENCE)); + } + + private void testWriteCredentialReferenceAttribute(String resource, String resourceName, String store, String alias, String secret, boolean assertFailed) { + ModelNode operation = new ModelNode(); + operation.get(ClientConstants.OP_ADDR).add("subsystem", "elytron-tls").add(resource, resourceName); + operation.get(ClientConstants.OP).set(ClientConstants.WRITE_ATTRIBUTE_OPERATION); + operation.get(ClientConstants.NAME).set(CREDENTIAL_REFERENCE); + ModelNode credentialReference = new ModelNode(); + if (store != null) { + credentialReference.get(STORE).set(store); + } + if (alias != null) { + credentialReference.get(ALIAS).set(alias); + } + if (secret != null) { + credentialReference.get(CLEAR_TEXT).set(secret); + } + operation.get(ClientConstants.VALUE).set(credentialReference); + ModelNode response = services.executeOperation(operation); + if (assertFailed) { + assertEquals(FAILED, response.get(OUTCOME).asString()); + assertTrue(response.get(FAILURE_DESCRIPTION).asString().contains(CREDENTIAL_REFERENCE)); + } else { + assertSuccess(response); + } + } + + private void writeClearTextAttribute(String resource, String resourceName, String secret) { + ModelNode operation = new ModelNode(); + operation.get(ClientConstants.OP_ADDR).add("subsystem", "elytron-tls").add(resource, resourceName); + operation.get(ClientConstants.OP).set(ClientConstants.WRITE_ATTRIBUTE_OPERATION); + operation.get(ClientConstants.NAME).set(CLEAR_TEXT_ATTRIBUTE_NAME); + operation.get(ClientConstants.VALUE).set(secret); + ModelNode response = assertSuccess(services.executeOperation(operation)).get(RESULT); + validateResponse(response, secret, false, true); + } + + private String generateKeyPairWithCredentialStoreUpdate(String keyStoreName, String store, String alias, String secret, boolean exists) { + ModelNode operation = new ModelNode(); + operation.get(ClientConstants.OP_ADDR).add("subsystem", "elytron-tls").add("key-store", keyStoreName); + operation.get(ClientConstants.OP).set(Constants.GENERATE_KEY_PAIR); + operation.get(Constants.ALIAS).set("bsmith"); + operation.get(Constants.DISTINGUISHED_NAME).set("CN=bob smith"); + operation.get(CredentialReference.CREDENTIAL_REFERENCE).get(STORE).set(store); + boolean autoGeneratedAlias = false; + if (alias != null) { + operation.get(CredentialReference.CREDENTIAL_REFERENCE).get(CredentialReference.ALIAS).set(alias); + } else { + autoGeneratedAlias = true; + } + if (secret != null) { + operation.get(CredentialReference.CREDENTIAL_REFERENCE).get(CredentialReference.CLEAR_TEXT).set(secret); + } + ModelNode response = assertSuccess(services.executeOperation(operation)).get(RESULT); + return validateResponse(response, secret, autoGeneratedAlias, exists); + } + + private String validateResponse(ModelNode response, String secret, boolean autoGeneratedAlias, boolean exists) { + if (secret == null) { + assertFalse(response.isDefined()); + return null; + } + ModelNode credentialStoreUpdate = response.get(CredentialReference.CREDENTIAL_STORE_UPDATE); + if (! exists) { + assertEquals(CredentialReference.NEW_ENTRY_ADDED, credentialStoreUpdate.get(CredentialReference.STATUS).asString()); + } else { + assertEquals(CredentialReference.EXISTING_ENTRY_UPDATED, credentialStoreUpdate.get(CredentialReference.STATUS).asString()); + } + if (autoGeneratedAlias) { + String generatedAlias = credentialStoreUpdate.get(CredentialReference.NEW_ALIAS).asString(); + assertTrue(generatedAlias != null && ! generatedAlias.isEmpty()); + return generatedAlias; + } + return null; + } + + private String validateFailedResponse(ModelNode response) { + ModelNode credentialStoreUpdate = response.get(CredentialReference.CREDENTIAL_STORE_UPDATE); + assertEquals(CredentialReference.UPDATE_ROLLED_BACK, credentialStoreUpdate.get(CredentialReference.STATUS).asString()); + return null; + } + + private String readAttribute(String keyStoreName, String attributeName) { + ModelNode operation = new ModelNode(); + operation.get(ClientConstants.OP_ADDR).add("subsystem","elytron-tls").add("key-store", keyStoreName); + operation.get(ClientConstants.OP).set(ClientConstants.READ_ATTRIBUTE_OPERATION); + operation.get(NAME).set(attributeName); + return assertSuccess(services.executeOperation(operation)).get(RESULT).asStringOrNull(); + } + + private void removeKeyStore(String keyStoreName) { + ModelNode operation = new ModelNode(); + operation.get(ClientConstants.OPERATION_HEADERS).get("allow-resource-service-restart").set(Boolean.TRUE); + operation.get(ClientConstants.OP_ADDR).add("subsystem","elytron-tls").add("key-store", keyStoreName); + operation.get(ClientConstants.OP).set(ClientConstants.REMOVE_OPERATION); + assertSuccess(services.executeOperation(operation)); + } + + private CredentialStore getNonEmptyCredentialStore() throws CredentialStoreException { + CredentialStore credentialStore = getCredentialStore(NON_EMPTY_CS_NAME); + credentialStore.store(EXISTING_ALIAS, new PasswordCredential(ClearPassword.createRaw(ClearPassword.ALGORITHM_CLEAR, EXISTING_PASSWORD.toCharArray()))); + credentialStore.flush(); + return credentialStore; + } + + private CredentialStore getCredentialStore(String store) { + ServiceName serviceName = Capabilities.CREDENTIAL_STORE_RUNTIME_CAPABILITY.getCapabilityServiceName(store); + return (CredentialStore) services.getContainer().getService(serviceName).getValue(); + } + + private ModelNode assertSuccess(ModelNode response) { + if (!response.get(OUTCOME).asString().equals(SUCCESS)) { + Assert.fail(response.toJSONString(false)); + } + return response; + } + + private ModelNode assertFailed(ModelNode response) { + if (! response.get(OUTCOME).asString().equals(FAILED)) { + Assert.fail(response.toJSONString(false)); + } + return response; + } + + private void addKeyStore() throws Exception { + addKeyStore("test.keystore", KS_NAME, "Elytron"); + } + + private void addKeyStore(String keyStoreFile, String keyStoreName, String keyStorePassword) throws Exception { + Path resources = Paths.get(Objects.requireNonNull(KeyStoresTestCase.class.getResource(".")).toURI()); + ModelNode operation = new ModelNode(); + operation.get(ClientConstants.OPERATION_HEADERS).get("allow-resource-service-restart").set(Boolean.TRUE); + operation.get(ClientConstants.OP_ADDR).add("subsystem","elytron-tls").add("key-store", keyStoreName); + operation.get(ClientConstants.OP).set(ClientConstants.ADD); + operation.get(Constants.PATH).set(resources + "/test.keystore"); + operation.get(Constants.TYPE).set("JKS"); + operation.get(CredentialReference.CREDENTIAL_REFERENCE).get(CredentialReference.CLEAR_TEXT).set(keyStorePassword); + assertSuccess(services.executeOperation(operation)); + } +} diff --git a/subsystem/src/test/java/org/wildfly/extension/elytron/tls/subsystem/ElytronTlsSubsystemTestCase.java b/subsystem/src/test/java/org/wildfly/extension/elytron/tls/subsystem/ElytronTlsSubsystem1_0TestCase.java similarity index 50% rename from subsystem/src/test/java/org/wildfly/extension/elytron/tls/subsystem/ElytronTlsSubsystemTestCase.java rename to subsystem/src/test/java/org/wildfly/extension/elytron/tls/subsystem/ElytronTlsSubsystem1_0TestCase.java index 5abd401..6e21b7c 100644 --- a/subsystem/src/test/java/org/wildfly/extension/elytron/tls/subsystem/ElytronTlsSubsystemTestCase.java +++ b/subsystem/src/test/java/org/wildfly/extension/elytron/tls/subsystem/ElytronTlsSubsystem1_0TestCase.java @@ -1,11 +1,13 @@ /* - * Copyright 2019 Red Hat, Inc. + * JBoss, Home of Professional Open Source. + * Copyright 2021 Red Hat, Inc., and individual contributors + * as indicated by the @author tags. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -17,51 +19,31 @@ package org.wildfly.extension.elytron.tls.subsystem; import java.io.IOException; -import java.util.List; +import org.jboss.as.controller.RunningMode; import org.jboss.as.subsystem.test.AbstractSubsystemBaseTest; import org.jboss.as.subsystem.test.AdditionalInitialization; -import org.jboss.as.subsystem.test.KernelServices; -import org.jboss.dmr.ModelNode; -import org.junit.Assert; -import org.junit.Test; /** - * @author Kabir Khan + * + * @author Farah Juma * @author Cameron Rodriguez */ -public class ElytronTlsSubsystemTestCase extends AbstractSubsystemBaseTest { +public class ElytronTlsSubsystem1_0TestCase extends AbstractSubsystemBaseTest { - public ElytronTlsSubsystemTestCase() { + public ElytronTlsSubsystem1_0TestCase() { super(ElytronTlsExtension.SUBSYSTEM_NAME, new ElytronTlsExtension()); } @Override protected String getSubsystemXml() throws IOException { - return readResource("elytron-tls-subsystem-test.xml"); + return readResource("elytron-tls-subsystem-1.0.xml"); } @Override - protected String getSubsystemXsdPath() { - return ElytronTlsExtension.getCurrentXsdPath(); - } - - @Test - public void testParseAndMarshalModel_TLS() throws Exception { - standardSubsystemTest("tls.xml"); - } - - @Test - public void testDisallowedProviders() throws Exception { - KernelServices services = standardSubsystemTest("providers.xml", true); - List disallowedProviders = services.readWholeModel().get("subsystem", "elytron-tls", "disallowed-providers").asList(); - Assert.assertNotNull(disallowedProviders); - Assert.assertEquals(3, disallowedProviders.size()); - } - protected AdditionalInitialization createAdditionalInitialization() { // Our use of the expression=encryption resource requires kernel capability setup that TestEnvironment provides - return TestEnvironment.asAdmin(); + return new TestEnvironment(RunningMode.ADMIN_ONLY); } } diff --git a/subsystem/src/test/java/org/wildfly/extension/elytron/tls/subsystem/ExpressionResolutionTestCase.java b/subsystem/src/test/java/org/wildfly/extension/elytron/tls/subsystem/ExpressionResolutionTestCase.java index b069b04..beedcc3 100644 --- a/subsystem/src/test/java/org/wildfly/extension/elytron/tls/subsystem/ExpressionResolutionTestCase.java +++ b/subsystem/src/test/java/org/wildfly/extension/elytron/tls/subsystem/ExpressionResolutionTestCase.java @@ -107,7 +107,7 @@ public void testPreConfiguredHierarchy() throws Exception { csTwo = createCredentialStoreTwo(); csThree = createCredentialStoreThree(); - KernelServices services = super.createKernelServicesBuilder(TestEnvironment.asNormal()) + KernelServices services = super.createKernelServicesBuilder(new TestEnvironment()) .setSubsystemXmlResource("expression-encryption.xml").build(); if (!services.isSuccessfulBoot()) { Assert.fail(services.getBootError().toString()); @@ -128,7 +128,7 @@ public void testPreConfiguredHierarchy() throws Exception { protected AdditionalInitialization createAdditionalInitialization() { // Our use of the expression=encryption resource requires kernel capability setup that TestEnvironment provides - return TestEnvironment.asAdmin(); + return new TestEnvironment(RunningMode.ADMIN_ONLY); } private static void cleanUp(CredentialStoreUtility csUtil) { @@ -185,7 +185,7 @@ private void testExpectedAliases(KernelServices services, String resourceType, S @Test public void testExpressionEncryptionOperations() throws Exception { - KernelServices services = super.createKernelServicesBuilder(TestEnvironment.asNormal()).setSubsystemXml(emptySubsystemXml()).build(); + KernelServices services = super.createKernelServicesBuilder(new TestEnvironment()).setSubsystemXml(emptySubsystemXml()).build(); if (!services.isSuccessfulBoot()) { Assert.fail(services.getBootError().toString()); } @@ -203,9 +203,6 @@ public void testExpressionEncryptionOperations() throws Exception { add.get(Constants.PATH).set(testStorePath); add.get(Constants.POPULATE).set(false); - System.out.println("string is"); - System.out.println(add.get(ClientConstants.OP_ADDR)); - assertSuccess(services.executeOperation(add)); // Generate one and export it. @@ -357,7 +354,7 @@ public void testExpressionEncryptionCycle() throws Exception { addEE.get(Constants.RESOLVERS).set(resolvers); steps.add(addEE); - KernelServices services = super.createKernelServicesBuilder(TestEnvironment.asNormal()).setSubsystemXml(emptySubsystemXml()).build(); + KernelServices services = super.createKernelServicesBuilder(new TestEnvironment()).setSubsystemXml(emptySubsystemXml()).build(); if (!services.isSuccessfulBoot()) { Assert.fail(services.getBootError().toString()); } diff --git a/subsystem/src/test/java/org/wildfly/extension/elytron/tls/subsystem/KeyStoresTestCase.java b/subsystem/src/test/java/org/wildfly/extension/elytron/tls/subsystem/KeyStoresTestCase.java index 579a4af..5fb12a0 100644 --- a/subsystem/src/test/java/org/wildfly/extension/elytron/tls/subsystem/KeyStoresTestCase.java +++ b/subsystem/src/test/java/org/wildfly/extension/elytron/tls/subsystem/KeyStoresTestCase.java @@ -70,6 +70,7 @@ import org.junit.Assert; import org.junit.Before; import org.junit.BeforeClass; +import org.junit.Ignore; import org.junit.Test; import org.mockserver.integration.ClientAndServer; import org.wildfly.security.WildFlyElytronProvider; @@ -363,7 +364,7 @@ public void init() throws Exception { } else { subsystemXml = JdkUtils.getJavaSpecVersion() <= 12 ? "tls-sun.xml" : "tls-oracle13plus.xml"; } - services = super.createKernelServicesBuilder(TestEnvironment.asNormal()).setSubsystemXmlResource(subsystemXml).build(); + services = super.createKernelServicesBuilder(new TestEnvironment()).setSubsystemXmlResource(subsystemXml).build(); if (!services.isSuccessfulBoot()) { Assert.fail(services.getBootError().toString()); } @@ -522,6 +523,7 @@ private void checkCertificate(final ModelNode certificate, final boolean verbose } } + @Ignore("Filtering keystores not implemented yet") @Test public void testFilteringKeystoreService() throws Exception { ServiceName serviceName = Capabilities.KEY_STORE_RUNTIME_CAPABILITY.getCapabilityServiceName("FilteringKeyStore"); @@ -539,6 +541,26 @@ public void testFilteringKeystoreService() throws Exception { assertFalse(keyStore.isCertificateEntry("ca")); } + @Ignore("Filtering keystores not implemented yet") + @Test + public void testFilteringKeystoreCli() throws Exception { + ModelNode operation = new ModelNode(); + operation.get(ClientConstants.OP_ADDR).add("subsystem","elytron").add(Constants.FILTERING_KEY_STORE,"FilteringKeyStore"); + operation.get(ClientConstants.OP).set(Constants.READ_ALIASES); + List nodes = assertSuccess(services.executeOperation(operation)).get(ClientConstants.RESULT).asList(); + assertEquals(1, nodes.size()); + assertEquals("firefly", nodes.get(0).asString()); + + operation = new ModelNode(); + operation.get(ClientConstants.OP_ADDR).add("subsystem","elytron").add(Constants.FILTERING_KEY_STORE,"FilteringKeyStore"); + operation.get(ClientConstants.OP).set(Constants.READ_ALIAS); + operation.get(Constants.ALIAS).set("firefly"); + ModelNode firefly = assertSuccess(services.executeOperation(operation)).get(ClientConstants.RESULT); + assertEquals("firefly", firefly.get(Constants.ALIAS).asString()); + assertEquals(KeyStore.PrivateKeyEntry.class.getSimpleName(), firefly.get(Constants.ENTRY_TYPE).asString()); + assertTrue(firefly.get(Constants.CERTIFICATE_CHAIN).isDefined()); + } + @Test public void testAutomaticKeystoreService() throws Exception { ServiceName serviceName = Capabilities.KEY_STORE_RUNTIME_CAPABILITY.getCapabilityServiceName("AutomaticKeystore"); @@ -1328,7 +1350,7 @@ private void addOriginalKeyStore() throws Exception { private List readAliases() { ModelNode operation = new ModelNode(); - operation.get(ClientConstants.OP_ADDR).add("subsystem","`elytron-tls").add("key-store", KEYSTORE_NAME); + operation.get(ClientConstants.OP_ADDR).add("subsystem","elytron-tls").add("key-store", KEYSTORE_NAME); operation.get(ClientConstants.OP).set(Constants.READ_ALIASES); return assertSuccess(services.executeOperation(operation)).get(ClientConstants.RESULT).asList(); } @@ -1414,7 +1436,7 @@ private void removeCertificateAuthority() { private void removeCertificateAuthorityAccount() { ModelNode operation = new ModelNode(); operation.get(ClientConstants.OPERATION_HEADERS).get("allow-resource-service-restart").set(Boolean.TRUE); - operation.get(ClientConstants.OP_ADDR).add("subsystem","elytron-tls`").add("certificate-authority-account", CERTIFICATE_AUTHORITY_ACCOUNT_NAME); + operation.get(ClientConstants.OP_ADDR).add("subsystem","elytron-tls").add("certificate-authority-account", CERTIFICATE_AUTHORITY_ACCOUNT_NAME); operation.get(ClientConstants.OP).set(ClientConstants.REMOVE_OPERATION); assertSuccess(services.executeOperation(operation)); } diff --git a/subsystem/src/test/java/org/wildfly/extension/elytron/tls/subsystem/ResolveExpressionAttributesTestCase.java b/subsystem/src/test/java/org/wildfly/extension/elytron/tls/subsystem/ResolveExpressionAttributesTestCase.java index 94a6734..4db6864 100644 --- a/subsystem/src/test/java/org/wildfly/extension/elytron/tls/subsystem/ResolveExpressionAttributesTestCase.java +++ b/subsystem/src/test/java/org/wildfly/extension/elytron/tls/subsystem/ResolveExpressionAttributesTestCase.java @@ -68,7 +68,7 @@ protected String getSubsystemXml() throws IOException { @Override protected AdditionalInitialization createAdditionalInitialization() { // Our use of the expression=encryption resource requires kernel capability setup that TestEnvironment provides - return TestEnvironment.asAdmin(); + return new TestEnvironment(RunningMode.ADMIN_ONLY); } @Test @@ -82,6 +82,7 @@ public void testExpressionAttributesResolved() { testKeyStore(); // testLdapKeyStore(); testProvider(); + // testSaslServer(); testTLSComponents(); } @@ -126,6 +127,7 @@ private void testCustomComponent() { private void testElytronTlsDefinition() { assertEquals(Arrays.asList("test"), getValue(serverModel, Constants.DISALLOWED_PROVIDERS, true)); + // assertEquals("false", getValue(serverModel, Constants.REGISTER_JASPI_FACTORY)); } private void testFilteringKeyStoreDefinition() { @@ -133,6 +135,19 @@ private void testFilteringKeyStoreDefinition() { assertEquals("NONE:+firefly", getValue(keystore, Constants.ALIAS_FILTER)); } + private void testJaspiConfiguration() { + ModelNode jaspi = serverModel.get(Constants.JASPI_CONFIGURATION).get("test"); + assertEquals("HttpServlet", getValue(jaspi, Constants.LAYER)); + assertEquals("default /test", getValue(jaspi, Constants.APPLICATION_CONTEXT)); + assertEquals("Test Definition", getValue(jaspi, Constants.DESCRIPTION)); + + ModelNode testModule = jaspi.get(Constants.SERVER_AUTH_MODULES).get(0); + assertEquals("REQUISITE", getValue(testModule, Constants.FLAG)); + + ModelNode options = testModule.get(Constants.OPTIONS); + assertEquals("b", getValue(options, "a")); + } + private void testKeyStore() { ModelNode keystore = serverModel.get(Constants.KEY_STORE).get("jks_store"); assertEquals("jks", getValue(keystore, Constants.TYPE)); @@ -173,10 +188,17 @@ private void testLdapKeyStore() { private void testProvider() { ModelNode provider = serverModel.get(Constants.PROVIDER_LOADER).get("openssl"); assertEquals("val", getValue(provider.get(Constants.CONFIGURATION), "prop")); - provider = serverModel.get(Constants.PROVIDER_LOADER).get("elytron-tls"); + provider = serverModel.get(Constants.PROVIDER_LOADER).get("elytron"); assertEquals("arg", getValue(provider, Constants.ARGUMENT)); } + private void testSaslServer() { + ModelNode factory = serverModel.get(Constants.SASL_AUTHENTICATION_FACTORY).get("SaslAuthenticationDefinition").get(Constants.MECHANISM_CONFIGURATIONS).get(0); + assertEquals("PLAIN", getValue(factory, Constants.MECHANISM_NAME)); + assertEquals("host", getValue(factory, Constants.HOST_NAME)); + assertEquals("protocol", getValue(factory, Constants.PROTOCOL)); + } + private void testTLSComponents() { // SSL Context ModelNode context = serverModel.get(Constants.SERVER_SSL_CONTEXT).get("server"); diff --git a/subsystem/src/test/java/org/wildfly/extension/elytron/tls/subsystem/SecurityPropertiesTestCase.java b/subsystem/src/test/java/org/wildfly/extension/elytron/tls/subsystem/SecurityPropertiesTestCase.java new file mode 100644 index 0000000..3a6d49c --- /dev/null +++ b/subsystem/src/test/java/org/wildfly/extension/elytron/tls/subsystem/SecurityPropertiesTestCase.java @@ -0,0 +1,96 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2017, Red Hat, Inc., and individual contributors as indicated + * by the @authors tag. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.wildfly.extension.elytron.tls.subsystem; + +import java.util.HashMap; +import java.util.Map; + +import org.junit.Assert; +import org.junit.Test; + +/** + * @author Tomaz Cerar (c) 2017 Red Hat Inc. + */ +public class SecurityPropertiesTestCase { + + @Test + public void testPropertyDiff(){ + Map newMap = new HashMap<>(); + Map oldMap = new HashMap<>(); + + Map updated = new HashMap<>(); + Map added = new HashMap<>(); + Map removed = new HashMap<>(); + + newMap.put("key1","value"); + newMap.put("key2","value"); + newMap.put("key3","value"); + + SecurityPropertiesWriteHandler.doDifference(newMap, oldMap, added, removed, updated ); + Assert.assertEquals(0, updated.size()); + Assert.assertEquals(3, added.size()); + Assert.assertEquals(0, removed.size()); + + oldMap.put("key1","value2"); + + updated.clear(); + removed.clear(); + added.clear(); + + SecurityPropertiesWriteHandler.doDifference(newMap, oldMap, added, removed, updated); + Assert.assertEquals(1, updated.size()); + Assert.assertEquals(2, added.size()); + Assert.assertEquals(0, removed.size()); + + + oldMap.put("key1", "value1"); + oldMap.put("key2", "value2"); + oldMap.put("key3", "value3"); + + updated.clear(); + removed.clear(); + added.clear(); + + SecurityPropertiesWriteHandler.doDifference(newMap, oldMap, added, removed, updated); + Assert.assertEquals(3, updated.size()); + Assert.assertEquals(0, added.size()); + Assert.assertEquals(0, removed.size()); + + + newMap.clear(); + newMap.put("key1", "value"); + newMap.put("key2", "value"); + newMap.put("key4", "value"); + + oldMap.clear(); + oldMap.put("key1", "value1"); + oldMap.put("key2", "value2"); + oldMap.put("key3", "value3"); + + updated.clear(); + removed.clear(); + added.clear(); + + SecurityPropertiesWriteHandler.doDifference(newMap, oldMap, added, removed, updated); + Assert.assertEquals(2, updated.size()); + Assert.assertEquals(1, added.size()); + Assert.assertEquals(1, removed.size()); + + } +} diff --git a/subsystem/src/test/java/org/wildfly/extension/elytron/tls/subsystem/SubsystemParsingTestCase.java b/subsystem/src/test/java/org/wildfly/extension/elytron/tls/subsystem/SubsystemParsingTestCase.java new file mode 100644 index 0000000..3311957 --- /dev/null +++ b/subsystem/src/test/java/org/wildfly/extension/elytron/tls/subsystem/SubsystemParsingTestCase.java @@ -0,0 +1,98 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2014 Red Hat, Inc., and individual contributors + * as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.wildfly.extension.elytron.tls.subsystem; + +import java.io.IOException; +import java.util.List; + +import org.jboss.as.subsystem.test.AbstractSubsystemBaseTest; +import org.jboss.as.subsystem.test.AdditionalInitialization; +import org.jboss.as.subsystem.test.KernelServices; +import org.jboss.dmr.ModelNode; +import org.junit.Assert; +import org.junit.Ignore; +import org.junit.Test; + +/** + * Tests all management expects for subsystem, parsing, marshaling, model definition and other + * Here is an example that allows you a fine grained controller over what is tested and how. So it can give you ideas what can be done and tested. + * If you have no need for advanced testing of subsystem you look at {@link ElytronTlsSubsystem1_0TestCase} that tests same stuff but most of the code + * is hidden inside of test harness + * + * @author Kabir Khan + * @author Cameron Rodriguez + */ +public class SubsystemParsingTestCase extends AbstractSubsystemBaseTest { + + public SubsystemParsingTestCase() { + super(ElytronTlsExtension.SUBSYSTEM_NAME, new ElytronTlsExtension()); + } + + @Override + protected String getSubsystemXml() throws IOException { + return readResource("elytron-tls-subsystem-1.0.xml"); + } + + @Test + public void testParseAndMarshalModel_TLS() throws Exception { + standardSubsystemTest("tls.xml"); + } + + @Test + public void testParseAndMarshalModel_ProviderLoader() throws Exception { + standardSubsystemTest("providers.xml"); + } + + @Test + public void testDisallowedProviders() throws Exception { + KernelServices services = standardSubsystemTest("providers.xml", true); + List disallowedProviders = services.readWholeModel().get("subsystem", "elytron-tls", "disallowed-providers").asList(); + Assert.assertNotNull(disallowedProviders); + Assert.assertEquals(3, disallowedProviders.size()); + } + + @Ignore("to be determined if needed") + @Test + public void testParseAndMarshalModel_Sasl() throws Exception { + standardSubsystemTest("sasl.xml"); + } + + @Test + public void testParseAndMarshalModel_SecurityProperties() throws Exception { + standardSubsystemTest("security-properties.xml"); + } + + @Test + public void testParseAndMarshalModel_CredentialStores() throws Exception { + standardSubsystemTest("credential-stores.xml"); + } + + @Ignore("to be determined if needed") + @Test + public void testParseAndMarshalModel_JASPI() throws Exception { + standardSubsystemTest("jaspi.xml"); + } + + @Override + protected AdditionalInitialization createAdditionalInitialization() { + return AdditionalInitialization.withCapabilities(ElytronTlsExtension.WELD_CAPABILITY_NAME); + } + + +} diff --git a/subsystem/src/test/java/org/wildfly/extension/elytron/tls/subsystem/TestEnvironment.java b/subsystem/src/test/java/org/wildfly/extension/elytron/tls/subsystem/TestEnvironment.java index 7cea212..079da33 100644 --- a/subsystem/src/test/java/org/wildfly/extension/elytron/tls/subsystem/TestEnvironment.java +++ b/subsystem/src/test/java/org/wildfly/extension/elytron/tls/subsystem/TestEnvironment.java @@ -17,39 +17,222 @@ */ package org.wildfly.extension.elytron.tls.subsystem; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.math.BigInteger; +import java.net.URL; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.KeyStore; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.cert.X509Certificate; + +import javax.security.auth.x500.X500Principal; + +import org.jboss.as.controller.RunningMode; +import org.jboss.as.controller.capability.RuntimeCapability; import org.jboss.as.controller.capability.registry.RuntimeCapabilityRegistry; import org.jboss.as.controller.extension.ExtensionRegistry; import org.jboss.as.controller.registry.ManagementResourceRegistration; import org.jboss.as.controller.registry.Resource; import org.jboss.as.subsystem.test.AdditionalInitialization; +import org.jboss.as.subsystem.test.ControllerInitializer; +import org.jboss.as.subsystem.test.KernelServices; +import org.jboss.msc.service.ServiceController; +import org.jboss.msc.service.ServiceName; +import org.wildfly.security.x500.cert.BasicConstraintsExtension; +import org.wildfly.security.x500.cert.SelfSignedX509CertificateAndSigningKey; +import org.wildfly.security.x500.cert.X509CertificateBuilder; + +class TestEnvironment extends AdditionalInitialization { + // TODO reenable tests after LDAP is added + + static final int LDAPS1_PORT = 11391; + static final int LDAPS2_PORT = 11392; + + private static final String WORKING_DIRECTORY_LOCATION = "./target/test-classes/org/wildfly/extension/elytron/tls/subsystem"; + private static final char[] GENERATED_KEYSTORE_PASSWORD = "Elytron".toCharArray(); + private static final X500Principal ISSUER_DN = new X500Principal("O=Root Certificate Authority, EMAILADDRESS=elytron@wildfly.org, C=UK, ST=Elytron, CN=Elytron CA"); + private static final X500Principal LOCALHOST_DN = new X500Principal("OU=Elytron, O=Elytron, C=CZ, ST=Elytron, CN=localhost"); + + private static KeyStore loadKeyStore() throws Exception{ + KeyStore ks = KeyStore.getInstance("JKS"); + ks.load(null, null); + return ks; + } + + private static SelfSignedX509CertificateAndSigningKey createIssuer() { + return SelfSignedX509CertificateAndSigningKey.builder() + .setDn(ISSUER_DN) + .setKeyAlgorithmName("RSA") + .setSignatureAlgorithmName("SHA256withRSA") + .addExtension(false, "BasicConstraints", "CA:true,pathlen:2147483647") + .build(); + } + + private static KeyStore createTrustStore(SelfSignedX509CertificateAndSigningKey issuerSelfSignedX509CertificateAndSigningKey) throws Exception { + KeyStore trustStore = loadKeyStore(); + + X509Certificate issuerCertificate = issuerSelfSignedX509CertificateAndSigningKey.getSelfSignedCertificate(); + trustStore.setCertificateEntry("mykey", issuerCertificate); + + return trustStore; + } + + private static KeyStore createLocalhostKeyStore(SelfSignedX509CertificateAndSigningKey issuerSelfSignedX509CertificateAndSigningKey) throws Exception { + KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); + KeyPair localhostKeys = keyPairGenerator.generateKeyPair(); + PrivateKey localhostSigningKey = localhostKeys.getPrivate(); + PublicKey localhostPublicKey = localhostKeys.getPublic(); + + KeyStore localhostKeyStore = loadKeyStore(); + + X509Certificate issuerCertificate = issuerSelfSignedX509CertificateAndSigningKey.getSelfSignedCertificate(); + localhostKeyStore.setCertificateEntry("ca", issuerCertificate); + + X509Certificate localhostCertificate = new X509CertificateBuilder() + .setIssuerDn(ISSUER_DN) + .setSubjectDn(LOCALHOST_DN) + .setSignatureAlgorithmName("SHA256withRSA") + .setSigningKey(issuerSelfSignedX509CertificateAndSigningKey.getSigningKey()) + .setPublicKey(localhostPublicKey) + .setSerialNumber(new BigInteger("3")) + .addExtension(new BasicConstraintsExtension(false, false, -1)) + .build(); + localhostKeyStore.setKeyEntry("localhost", localhostSigningKey, GENERATED_KEYSTORE_PASSWORD, new X509Certificate[]{localhostCertificate,issuerCertificate}); + + return localhostKeyStore; + } + + private static void createTemporaryKeyStoreFile(KeyStore keyStore, File outputFile) throws Exception { + try (FileOutputStream fos = new FileOutputStream(outputFile)){ + keyStore.store(fos, GENERATED_KEYSTORE_PASSWORD); + } + } + + public static void setUpKeyStores() throws Exception { + File workingDir = new File(WORKING_DIRECTORY_LOCATION); + if (workingDir.exists() == false) { + workingDir.mkdirs(); + } + + SelfSignedX509CertificateAndSigningKey issuerSelfSignedX509CertificateAndSigningKey = createIssuer(); + File trustFile = new File(workingDir, "ca.truststore"); + KeyStore trustStore = createTrustStore(issuerSelfSignedX509CertificateAndSigningKey); + File localhostFile = new File(workingDir, "localhost.keystore"); + KeyStore localhostKeyStore = createLocalhostKeyStore(issuerSelfSignedX509CertificateAndSigningKey); + + createTemporaryKeyStoreFile(trustStore, trustFile); + createTemporaryKeyStoreFile(localhostKeyStore, localhostFile); + } + + private final RunningMode runningMode; + + TestEnvironment() { + this(RunningMode.NORMAL); + } + + TestEnvironment(RunningMode runningMode) { + this.runningMode = runningMode; + + } + + @Override + protected RunningMode getRunningMode() { + return runningMode; + } + + @Override + protected ControllerInitializer createControllerInitializer() { + ControllerInitializer initializer = new ControllerInitializer(); + + // TODO perform check without realms + try { + URL fsr = getClass().getResource("keystore.jceks"); + if (fsr != null) emptyDirectory(new File(fsr.getFile()).toPath()); + } catch (Exception e) { + throw new RuntimeException("Could ensure empty testing filesystem directory", e); + } + + try { + initializer.addPath("jboss.server.config.dir", getClass().getResource(".").getFile(), null); + initializer.addPath("jboss.server.data.dir", "target", null); + } catch (Exception e) { + throw new RuntimeException("Could not create test config directory", e); + } + + return initializer; + } + + @Override + protected void initializeExtraSubystemsAndModel(ExtensionRegistry extensionRegistry, Resource rootResource, ManagementResourceRegistration rootRegistration, RuntimeCapabilityRegistry capabilityRegistry) { + super.initializeExtraSubystemsAndModel(extensionRegistry, rootResource, rootRegistration, capabilityRegistry); + registerCapabilities(capabilityRegistry, ElytronTlsExtension.WELD_CAPABILITY_NAME); + } + + /* public static void startLdapService() { + try { + setUpKeyStores(); + LdapService.builder() + .setWorkingDir(new File("./target/apache-ds/working1")) + .createDirectoryService("TestService1") + .addPartition("Elytron", "dc=elytron,dc=wildfly,dc=org", 5, "uid") + .importLdif(TestEnvironment.class.getResourceAsStream("ldap-schemas.ldif")) + .importLdif(TestEnvironment.class.getResourceAsStream("ldap-data.ldif")) + .addTcpServer("Default TCP", "localhost", LDAPS1_PORT, "localhost.keystore", "Elytron") + .start(); + LdapService.builder() + .setWorkingDir(new File("./target/apache-ds/working2")) + .createDirectoryService("TestService2") + .addPartition("Elytron", "dc=elytron,dc=wildfly,dc=org", 5, "uid") + .importLdif(TestEnvironment.class.getResourceAsStream("ldap-schemas.ldif")) + .importLdif(TestEnvironment.class.getResourceAsStream("ldap-referred.ldif")) + .addTcpServer("Default TCP", "localhost", LDAPS2_PORT, "localhost.keystore", "Elytron") + .start(); + } catch (Exception e) { + throw new RuntimeException("Could not start LDAP embedded server.", e); + } + } */ + + private void emptyDirectory(Path directory) throws IOException { + Files.walkFileTree(directory, new SimpleFileVisitor() { + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + Files.delete(file); + return FileVisitResult.CONTINUE; + } -class TestEnvironment { - // TODO: Re-add LDAP configuration - - /** - * Creates an {@link AdditionalInitialization} with the Weld {@link org.jboss.as.controller.capability.RuntimeCapability capability}, - * operating in {@link org.jboss.as.controller.RunningMode#ADMIN_ONLY RunningMode.ADMIN_ONLY}. - * - * @author Cameron Rodriguez - */ - static AdditionalInitialization asAdmin() { - return AdditionalInitialization.withCapabilities(ElytronTlsExtension.WELD_CAPABILITY_NAME); - } - - /** - * Creates an {@link AdditionalInitialization} with the Weld {@link org.jboss.as.controller.capability.RuntimeCapability capability}, - * operating in {@link org.jboss.as.controller.RunningMode#NORMAL RunningMode.NORMAL}. - * - * @author Cameron Rodriguez - */ - static AdditionalInitialization asNormal() { - return new AdditionalInitialization() { @Override - protected void initializeExtraSubystemsAndModel(ExtensionRegistry extensionRegistry, Resource rootResource, - ManagementResourceRegistration rootRegistration, RuntimeCapabilityRegistry capabilityRegistry) { - super.initializeExtraSubystemsAndModel(extensionRegistry, rootResource, rootRegistration, capabilityRegistry); - registerCapabilities(capabilityRegistry, ElytronTlsExtension.WELD_CAPABILITY_NAME); + public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { + Files.delete(dir); + return FileVisitResult.CONTINUE; } - }; + }); + } + + // classloader obtaining mock to load classes from testsuite + /* private static class ClassLoadingAttributeDefinitionsMock extends MockUp { + @Mock + static ClassLoader resolveClassLoader(String module) { + return SaslTestCase.class.getClassLoader(); + } + } + + static void mockCallerModuleClassloader() { + new ClassLoadingAttributeDefinitionsMock(); + } */ + + static void activateService(KernelServices services, RuntimeCapability capability, String... dynamicNameElements) throws InterruptedException { + ServiceName serviceName = capability.getCapabilityServiceName(dynamicNameElements); + ServiceController serviceController = services.getContainer().getService(serviceName); + serviceController.setMode(ServiceController.Mode.ACTIVE); + serviceController.awaitValue(); } } diff --git a/subsystem/src/test/java/org/wildfly/extension/elytron/tls/subsystem/TlsTestCase.java b/subsystem/src/test/java/org/wildfly/extension/elytron/tls/subsystem/TlsTestCase.java index 88e7c48..d8c0b3c 100644 --- a/subsystem/src/test/java/org/wildfly/extension/elytron/tls/subsystem/TlsTestCase.java +++ b/subsystem/src/test/java/org/wildfly/extension/elytron/tls/subsystem/TlsTestCase.java @@ -1,3 +1,21 @@ +/* + * JBoss, Home of Professional Open Source + * + * Copyright 2016 Red Hat, Inc. and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.wildfly.extension.elytron.tls.subsystem; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.FAILED; @@ -78,6 +96,7 @@ import org.wildfly.security.x500.cert.X509CertificateBuilder; /** + * @author Jan Kalina * @author Cameron Rodriguez */ public class TlsTestCase extends AbstractSubsystemTest { @@ -99,7 +118,7 @@ public class TlsTestCase extends AbstractSubsystemTest { private static final String INIT_TEST_TRUSTSTORE = "myTS"; private static final String INIT_TEST_TRUSTMANAGER = "myTM"; - private static final char[] GENERATED_KEYSTORE_PASSWORD = "ElytronTLS".toCharArray(); + private static final char[] GENERATED_KEYSTORE_PASSWORD = "Elytron".toCharArray(); private static final String PROTOCOLS_SERVER = "enabledProtocolsServer"; private static final String PROTOCOLS_CLIENT = "enabledProtocolsClient"; private static final String NEGOTIATED_PROTOCOL = "negotiatedProtocol"; @@ -249,7 +268,7 @@ private static void createTemporaryKeyStoreFile(KeyStore keyStore, File outputFi private static void setUpKeyStores() throws Exception { File workingDir = new File(WORKING_DIRECTORY_LOCATION); if (!workingDir.exists()) { - Assert.assertTrue(workingDir.mkdirs()); + workingDir.mkdirs(); } SelfSignedX509CertificateAndSigningKey issuerSelfSignedX509CertificateAndSigningKey = createIssuer(ISSUER_DN); @@ -284,7 +303,7 @@ private static void deleteKeyStoreFiles() { }; for (File file : testFiles) { if (file.exists()) { - Assert.assertTrue(file.delete()); + file.delete(); } } } @@ -332,7 +351,7 @@ public void prepare() throws Throwable { } else { subsystemXml = JdkUtils.getJavaSpecVersion() <= 12 ? "tls-sun.xml" : "tls-oracle13plus.xml"; } - services = super.createKernelServicesBuilder(TestEnvironment.asNormal()).setSubsystemXmlResource(subsystemXml).build(); + services = super.createKernelServicesBuilder(new TestEnvironment()).setSubsystemXmlResource(subsystemXml).build(); if (!services.isSuccessfulBoot()) { Assert.fail(services.getBootError().toString()); } @@ -398,9 +417,13 @@ public void testSslServiceAuthSSLv2HelloOneWay() throws Throwable { public void testSslServiceAuthProtocolMismatchSSLv2Hello() throws Throwable { Assume.assumeFalse("Skipping testSslServiceAuthSSLv2Hello as IBM JDK does not support enabling SSLv2Hello " + "in the client", JdkUtils.isIbmJdk()); - testCommunication("ServerSslContextTLS12Only", "ClientSslContextSSLv2Hello", false, "OU=Elytron,O=Elytron,C=CZ,ST=Elytron,CN=localhost", - "OU=Elytron,O=Elytron,C=UK,ST=Elytron,CN=Firefly"); - fail("Excepted SSLHandshakeException not thrown"); + try { + testCommunication("ServerSslContextTLS12Only", "ClientSslContextSSLv2Hello", false, "OU=Elytron,O=Elytron,C=CZ,ST=Elytron,CN=localhost", + "OU=Elytron,O=Elytron,C=UK,ST=Elytron,CN=Firefly"); + fail("Excepted SSLHandshakeException not thrown"); + } catch (SSLHandshakeException ignored) { + + } } @Test @@ -765,6 +788,7 @@ private void checkProtocolConfiguration(Map protocolChecker, S // Check negotiated protocol is the one we expected assertEquals(protocolChecker.get(NEGOTIATED_PROTOCOL)[0], serverSocket.getSession().getProtocol()); + assertEquals(protocolChecker.get(NEGOTIATED_PROTOCOL)[0], clientSocket.getSession().getProtocol()); } private void testSessionsReading(String serverContextName, String clientContextName, String expectedServerPrincipal, String expectedClientPrincipal) { diff --git a/subsystem/src/test/resources/org/wildfly/extension/elytron/tls/subsystem/credential-store-updates.xml b/subsystem/src/test/resources/org/wildfly/extension/elytron/tls/subsystem/credential-store-updates.xml new file mode 100644 index 0000000..360409b --- /dev/null +++ b/subsystem/src/test/resources/org/wildfly/extension/elytron/tls/subsystem/credential-store-updates.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/subsystem/src/test/resources/org/wildfly/extension/elytron/tls/subsystem/credential-store.xml b/subsystem/src/test/resources/org/wildfly/extension/elytron/tls/subsystem/credential-store.xml new file mode 100644 index 0000000..6d36283 --- /dev/null +++ b/subsystem/src/test/resources/org/wildfly/extension/elytron/tls/subsystem/credential-store.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + diff --git a/subsystem/src/test/resources/org/wildfly/extension/elytron/tls/subsystem/credential-stores.xml b/subsystem/src/test/resources/org/wildfly/extension/elytron/tls/subsystem/credential-stores.xml new file mode 100644 index 0000000..59e6f5c --- /dev/null +++ b/subsystem/src/test/resources/org/wildfly/extension/elytron/tls/subsystem/credential-stores.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/subsystem/src/test/resources/org/wildfly/extension/elytron/tls/subsystem/elytron-tls-expressions.xml b/subsystem/src/test/resources/org/wildfly/extension/elytron/tls/subsystem/elytron-tls-expressions.xml index 6c70784..eafc85a 100644 --- a/subsystem/src/test/resources/org/wildfly/extension/elytron/tls/subsystem/elytron-tls-expressions.xml +++ b/subsystem/src/test/resources/org/wildfly/extension/elytron/tls/subsystem/elytron-tls-expressions.xml @@ -24,7 +24,7 @@ - + diff --git a/subsystem/src/test/resources/org/wildfly/extension/elytron/tls/subsystem/elytron-tls-subsystem-1.0.xml b/subsystem/src/test/resources/org/wildfly/extension/elytron/tls/subsystem/elytron-tls-subsystem-1.0.xml new file mode 100644 index 0000000..2a0caa2 --- /dev/null +++ b/subsystem/src/test/resources/org/wildfly/extension/elytron/tls/subsystem/elytron-tls-subsystem-1.0.xml @@ -0,0 +1,206 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/subsystem/src/test/resources/org/wildfly/extension/elytron/tls/subsystem/elytron-tls-subsystem-test.xml b/subsystem/src/test/resources/org/wildfly/extension/elytron/tls/subsystem/elytron-tls-subsystem-test.xml index 9b93892..8442a81 100644 --- a/subsystem/src/test/resources/org/wildfly/extension/elytron/tls/subsystem/elytron-tls-subsystem-test.xml +++ b/subsystem/src/test/resources/org/wildfly/extension/elytron/tls/subsystem/elytron-tls-subsystem-test.xml @@ -15,29 +15,36 @@ --> - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + @@ -84,6 +91,22 @@ + + + + + + + + + + + + + + + + diff --git a/subsystem/src/test/resources/org/wildfly/extension/elytron/tls/subsystem/jaspi.xml b/subsystem/src/test/resources/org/wildfly/extension/elytron/tls/subsystem/jaspi.xml new file mode 100644 index 0000000..cfe77eb --- /dev/null +++ b/subsystem/src/test/resources/org/wildfly/extension/elytron/tls/subsystem/jaspi.xml @@ -0,0 +1,26 @@ + + diff --git a/subsystem/src/test/resources/org/wildfly/extension/elytron/tls/subsystem/security-properties.xml b/subsystem/src/test/resources/org/wildfly/extension/elytron/tls/subsystem/security-properties.xml new file mode 100644 index 0000000..1e13f51 --- /dev/null +++ b/subsystem/src/test/resources/org/wildfly/extension/elytron/tls/subsystem/security-properties.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/subsystem/src/test/resources/org/wildfly/extension/elytron/tls/subsystem/tls-ibm.xml b/subsystem/src/test/resources/org/wildfly/extension/elytron/tls/subsystem/tls-ibm.xml index 448656a..cff1ea0 100644 --- a/subsystem/src/test/resources/org/wildfly/extension/elytron/tls/subsystem/tls-ibm.xml +++ b/subsystem/src/test/resources/org/wildfly/extension/elytron/tls/subsystem/tls-ibm.xml @@ -16,7 +16,7 @@ - + @@ -46,32 +46,32 @@ - - - - + + + + - + - + - + - + - + - + diff --git a/subsystem/src/test/resources/org/wildfly/extension/elytron/tls/subsystem/tls-oracle13plus.xml b/subsystem/src/test/resources/org/wildfly/extension/elytron/tls/subsystem/tls-oracle13plus.xml index df5b0b1..1de525d 100644 --- a/subsystem/src/test/resources/org/wildfly/extension/elytron/tls/subsystem/tls-oracle13plus.xml +++ b/subsystem/src/test/resources/org/wildfly/extension/elytron/tls/subsystem/tls-oracle13plus.xml @@ -16,7 +16,7 @@ - + @@ -45,32 +45,32 @@ - - - - + + + + - + - + - + - + - + - + diff --git a/subsystem/src/test/resources/org/wildfly/extension/elytron/tls/subsystem/tls-sun.xml b/subsystem/src/test/resources/org/wildfly/extension/elytron/tls/subsystem/tls-sun.xml index 928a3c4..10e5943 100644 --- a/subsystem/src/test/resources/org/wildfly/extension/elytron/tls/subsystem/tls-sun.xml +++ b/subsystem/src/test/resources/org/wildfly/extension/elytron/tls/subsystem/tls-sun.xml @@ -16,7 +16,7 @@ - + @@ -45,32 +45,32 @@ - - - - + + + + - + - + - + - + - + - + diff --git a/subsystem/src/test/resources/org/wildfly/extension/elytron/tls/subsystem/tls.xml b/subsystem/src/test/resources/org/wildfly/extension/elytron/tls/subsystem/tls.xml index cacb7cc..746fd57 100644 --- a/subsystem/src/test/resources/org/wildfly/extension/elytron/tls/subsystem/tls.xml +++ b/subsystem/src/test/resources/org/wildfly/extension/elytron/tls/subsystem/tls.xml @@ -3,7 +3,6 @@ - @@ -62,7 +61,6 @@ - @@ -85,24 +83,5 @@ - - - - - - - - - - - - - - - - - diff --git a/testsuite/integration/pom.xml b/testsuite/integration/pom.xml index c797446..a4ec780 100644 --- a/testsuite/integration/pom.xml +++ b/testsuite/integration/pom.xml @@ -88,6 +88,10 @@ wildfly-arquillian-protocol-jmx test + + org.wildfly.core + wildfly-core-test-runner + \ No newline at end of file diff --git a/testsuite/integration/subsystem/src/test/java/org/wildfly/test/feature/pack/elytron/tls/subsystem/common/ModelNodeConvertable.java b/testsuite/integration/subsystem/src/test/java/org/jboss/as/test/shared/CliUtils.java similarity index 54% rename from testsuite/integration/subsystem/src/test/java/org/wildfly/test/feature/pack/elytron/tls/subsystem/common/ModelNodeConvertable.java rename to testsuite/integration/subsystem/src/test/java/org/jboss/as/test/shared/CliUtils.java index 663bce3..3690729 100644 --- a/testsuite/integration/subsystem/src/test/java/org/wildfly/test/feature/pack/elytron/tls/subsystem/common/ModelNodeConvertable.java +++ b/testsuite/integration/subsystem/src/test/java/org/jboss/as/test/shared/CliUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 Red Hat, Inc. + * Copyright 2016 Red Hat, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,17 +14,24 @@ * limitations under the License. */ -package org.wildfly.test.feature.pack.elytron.tls.subsystem.common; +package org.jboss.as.test.shared; -import org.jboss.dmr.ModelNode; +import static org.wildfly.common.Assert.checkNotNullParamWithNullPointerException; /** - * Represents objects which are convertable to ModelNode instances. + * CLI helper methods. * * @author Josef Cacek */ -public interface ModelNodeConvertable { - - ModelNode toModelNode(); +public class CliUtils { + /** + * Escapes given path String for CLI. + * + * @param path path string to escape (must be not-null) + * @return escaped path + */ + public static String escapePath(String path) { + return checkNotNullParamWithNullPointerException("path", path).replace("\\", "\\\\"); + } } diff --git a/testsuite/integration/subsystem/src/test/java/org/wildfly/test/feature/pack/elytron/tls/subsystem/common/AbstractConfigurableElement.java b/testsuite/integration/subsystem/src/test/java/org/wildfly/test/feature/pack/elytron/tls/subsystem/common/AbstractConfigurableElement.java deleted file mode 100644 index fe51f83..0000000 --- a/testsuite/integration/subsystem/src/test/java/org/wildfly/test/feature/pack/elytron/tls/subsystem/common/AbstractConfigurableElement.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2017 Red Hat, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.wildfly.test.feature.pack.elytron.tls.subsystem.common; - -import static org.wildfly.common.Assert.checkNotNullParamWithNullPointerException; - -/** - * Abstract parent for {@link ConfigurableElement} implementations. It just holds common fields and provides parent for - * builders. - * - * @author Josef Cacek - */ -public abstract class AbstractConfigurableElement implements ConfigurableElement { - - protected final String name; - - protected AbstractConfigurableElement(Builder builder) { - checkNotNullParamWithNullPointerException("builder", builder); - this.name = checkNotNullParamWithNullPointerException("builder.name", builder.name); - } - - @Override - public final String getName() { - return name; - } - - /** - * Builder to build {@link AbstractConfigurableElement}. - */ - public abstract static class Builder> { - private String name; - - protected Builder() { - } - - protected abstract T self(); - - public final T withName(String name) { - this.name = name; - return self(); - } - - } - -} diff --git a/testsuite/integration/subsystem/src/test/java/org/wildfly/test/feature/pack/elytron/tls/subsystem/common/ConfigurableElement.java b/testsuite/integration/subsystem/src/test/java/org/wildfly/test/feature/pack/elytron/tls/subsystem/common/ConfigurableElement.java deleted file mode 100644 index 326c606..0000000 --- a/testsuite/integration/subsystem/src/test/java/org/wildfly/test/feature/pack/elytron/tls/subsystem/common/ConfigurableElement.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2017 Red Hat, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.wildfly.test.feature.pack.elytron.tls.subsystem.common; - -import org.jboss.as.controller.client.ModelControllerClient; -import org.jboss.as.test.integration.management.util.CLIWrapper; - -/** - * Interface representing a configurable object in domain model. The implementation has to override at least one of the - * {@code create(...)} methods and one of the {@code remove(...)} methods. - * - * @author Josef Cacek - */ -public interface ConfigurableElement { - - /** - * Returns name of this element. - */ - String getName(); - - /** - * Creates this element in domain model and it also may create other resources if needed (e.g. external files). - * Implementation can choose if controller client is used or provided CLI wrapper. - */ - void create(ModelControllerClient client, CLIWrapper cli) throws Exception; - - /** - * Reverts the changes introdued by {@code create(...)} method(s). - */ - void remove(ModelControllerClient client, CLIWrapper cli) throws Exception; -} diff --git a/testsuite/integration/subsystem/src/test/java/org/wildfly/test/feature/pack/elytron/tls/subsystem/common/CredentialReference.java b/testsuite/integration/subsystem/src/test/java/org/wildfly/test/feature/pack/elytron/tls/subsystem/common/CredentialReference.java deleted file mode 100644 index b68ad67..0000000 --- a/testsuite/integration/subsystem/src/test/java/org/wildfly/test/feature/pack/elytron/tls/subsystem/common/CredentialReference.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright 2017 Red Hat, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.wildfly.test.feature.pack.elytron.tls.subsystem.common; - -import static org.apache.commons.lang3.StringUtils.isNotBlank; -import static org.wildfly.test.security.common.ModelNodeUtil.setIfNotNull; - -import org.jboss.dmr.ModelNode; -import org.wildfly.test.security.common.elytron.CliFragment; - -/** - * Helper class for adding "credential-reference" attributes into CLI commands. - * - * @author Josef Cacek - */ -public class CredentialReference implements CliFragment, ModelNodeConvertable { - - public static final CredentialReference EMPTY = CredentialReference.builder().build(); - - private final String store; - private final String alias; - private final String type; - private final String clearText; - - private CredentialReference(Builder builder) { - this.store = builder.store; - this.alias = builder.alias; - this.type = builder.type; - this.clearText = builder.clearText; - } - - @Override - public String asString() { - StringBuilder sb = new StringBuilder(); - if (isNotBlank(alias) || isNotBlank(clearText) || isNotBlank(store) || isNotBlank(type)) { - sb.append("credential-reference={ "); - if (isNotBlank(alias)) { - sb.append(String.format("alias=\"%s\", ", alias)); - } - if (isNotBlank(store)) { - sb.append(String.format("store=\"%s\", ", store)); - } - if (isNotBlank(type)) { - sb.append(String.format("type=\"%s\", ", type)); - } - if (isNotBlank(clearText)) { - sb.append(String.format("clear-text=\"%s\"", clearText)); - } - sb.append("}, "); - } - return sb.toString(); - } - - @Override - public ModelNode toModelNode() { - if (this == EMPTY) { - return null; - } - final ModelNode node= new ModelNode(); - setIfNotNull(node, "store", store); - setIfNotNull(node, "alias", alias); - setIfNotNull(node, "type", type); - setIfNotNull(node, "clear-text", clearText); - return node; - } - - /** - * Creates builder to build {@link CredentialReference}. - * - * @return created builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Builder to build {@link CredentialReference}. - */ - public static final class Builder { - private String store; - private String alias; - private String type; - private String clearText; - - private Builder() { - } - - public Builder withStore(String store) { - this.store = store; - return this; - } - - public Builder withAlias(String alias) { - this.alias = alias; - return this; - } - - public Builder withType(String type) { - this.type = type; - return this; - } - - public Builder withClearText(String clearText) { - this.clearText = clearText; - return this; - } - - public CredentialReference build() { - return new CredentialReference(this); - } - } -} diff --git a/testsuite/integration/subsystem/src/test/java/org/wildfly/test/integration/elytron/tls/subsystem/expression/SystemPropertyExpressionTestCase.java b/testsuite/integration/subsystem/src/test/java/org/wildfly/test/integration/elytron/tls/subsystem/expression/SystemPropertyExpressionTestCase.java new file mode 100644 index 0000000..4945cc6 --- /dev/null +++ b/testsuite/integration/subsystem/src/test/java/org/wildfly/test/integration/elytron/tls/subsystem/expression/SystemPropertyExpressionTestCase.java @@ -0,0 +1,248 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2021 Red Hat, Inc., and individual contributors + * as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.wildfly.test.integration.elytron.tls.subsystem.expression; + +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ALLOW_RESOURCE_SERVICE_RESTART; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.COMPOSITE; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.CORE_SERVICE; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OPERATION_HEADERS; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OUTCOME; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.PLATFORM_MBEAN; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.RESULT; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.STEPS; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SUBSYSTEM; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SUCCESS; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SYSTEM_PROPERTY; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.TYPE; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.VALUE; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.security.GeneralSecurityException; +import java.security.KeyStore; +import java.util.HashMap; +import java.util.Map; + +import org.jboss.as.controller.PathAddress; +import org.jboss.as.controller.client.ModelControllerClient; +import org.jboss.as.controller.client.helpers.ClientConstants; +import org.jboss.as.controller.operations.common.Util; +import org.jboss.as.test.integration.management.util.ServerReload; +import org.jboss.as.test.shared.TestSuiteEnvironment; +import org.jboss.dmr.ModelNode; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.wildfly.core.testrunner.ManagementClient; +import org.wildfly.core.testrunner.ServerSetup; +import org.wildfly.core.testrunner.ServerSetupTask; +import org.wildfly.core.testrunner.UnsuccessfulOperationException; +import org.wildfly.core.testrunner.WildflyTestRunner; +import org.wildfly.security.auth.server.IdentityCredentials; +import org.wildfly.security.credential.PasswordCredential; +import org.wildfly.security.credential.SecretKeyCredential; +import org.wildfly.security.credential.store.CredentialStore; +import org.wildfly.security.credential.store.impl.KeyStoreCredentialStore; +import org.wildfly.security.encryption.SecretKeyUtil; +import org.wildfly.security.password.interfaces.ClearPassword; + +@RunWith(WildflyTestRunner.class) +@ServerSetup(SystemPropertyExpressionTestCase.ServerSetup.class) +public class SystemPropertyExpressionTestCase { + + private static final String CNAME = SystemPropertyExpressionTestCase.class.getSimpleName(); + private static final String CS_PATH = "target/" + CNAME + ".cs"; + private static final PathAddress SUBSYSTEM_ADDRESS = PathAddress.pathAddress(SUBSYSTEM, "elytron"); + private static final PathAddress ENCRYPTION_ADDRESS = SUBSYSTEM_ADDRESS.append("expression", "encryption"); + private static final String CREDENTIAL_STORE = "credential-store"; + private static final PathAddress CREDENTIAL_STORE_ADDRESS = SUBSYSTEM_ADDRESS.append(CREDENTIAL_STORE, CNAME); + private static final String SECURE_KEY = "RUxZAUsXHVcDh99zAdxGEzTBK1h2qjW+sZg2+37w7ijhDEiJEw=="; + private static final PathAddress RUNTIME_ADDRESS = PathAddress.pathAddress(CORE_SERVICE, PLATFORM_MBEAN).append(TYPE, "runtime"); + private static final String PROP = CNAME; + private static final String MISSING_PROP = PROP + "-missing"; + + private static final String CLEAR_TEXT = "Lorem ipsum dolor sit amet"; + + public static final class ServerSetup implements ServerSetupTask { + @Override + public void setup(ManagementClient managementClient) throws Exception { + addCredentialStore(managementClient); + managementClient.executeForResult(getAddExpressionEncyryptionOp()); + } + + @Override + public void tearDown(ManagementClient managementClient) throws Exception { + try { + safeRemoveSystemProperty(managementClient, PROP); + safeRemoveSystemProperty(managementClient, MISSING_PROP); + removeExpressionEncryption(managementClient.getControllerClient()); + } finally { + removeCredentialStore(managementClient); + } + } + + private static void addCredentialStore(ManagementClient managementClient) throws GeneralSecurityException, IOException, UnsuccessfulOperationException { + cleanCredentialStoreFile(); + KeyStore ks = KeyStore.getInstance("JCEKS"); + ks.load(null, null); + ks.store(Files.newOutputStream(Paths.get(CS_PATH)), CNAME.toCharArray()); + + Map attributes = new HashMap<>(); + attributes.put("location", CS_PATH); + attributes.put("keyStoreType", "JCEKS"); + attributes.put("modifiable", "true"); + + PasswordCredential credential = new PasswordCredential(ClearPassword.createRaw(ClearPassword.ALGORITHM_CLEAR, CNAME.toCharArray())); + CredentialStore credentialStore = CredentialStore.getInstance(KeyStoreCredentialStore.KEY_STORE_CREDENTIAL_STORE); + + credentialStore.initialize(attributes, + new CredentialStore.CredentialSourceProtectionParameter(IdentityCredentials.NONE.withCredential(credential))); + credentialStore.store("securekey", new SecretKeyCredential(SecretKeyUtil.importSecretKey(SECURE_KEY))); + credentialStore.flush(); + + ModelNode addOp = Util.createAddOperation(CREDENTIAL_STORE_ADDRESS); + addOp.get("location").set(CS_PATH); + addOp.get("credential-reference", "clear-text").set(CNAME); + addOp.get("providers").set("combined-providers"); + managementClient.executeForResult(addOp); + } + + private static void safeRemoveSystemProperty(ManagementClient managementClient, String prop) { + try { + managementClient.getControllerClient().execute(Util.createRemoveOperation(PathAddress.pathAddress(SYSTEM_PROPERTY, prop))); + } catch (Exception e) { + e.printStackTrace(System.out); + } + } + + private static void removeCredentialStore(ManagementClient managementClient) throws UnsuccessfulOperationException { + try { + ModelNode removeOp = Util.createRemoveOperation(CREDENTIAL_STORE_ADDRESS); + managementClient.executeForResult(removeOp); + ServerReload.executeReloadAndWaitForCompletion(managementClient.getControllerClient()); + } finally { + cleanCredentialStoreFile(); + } + } + + private static void cleanCredentialStoreFile() { + File f = new File(CS_PATH); + assert !f.exists() || f.delete(); + } + } + + private static ModelNode getAddExpressionEncyryptionOp() { + ModelNode encAdd = Util.createAddOperation(ENCRYPTION_ADDRESS); + encAdd.get("default-resolver").set("Default"); + ModelNode resolvers = encAdd.get("resolvers"); + ModelNode resolver = new ModelNode(); + resolver.get("name").set("Default"); + resolver.get(CREDENTIAL_STORE).set(CNAME); + resolver.get("secret-key").set("securekey"); + resolvers.add(resolver); + return encAdd; + } + + private static void removeExpressionEncryption(ModelControllerClient modelControllerClient) throws IOException { + ModelNode removeOp = Util.createRemoveOperation(ENCRYPTION_ADDRESS); + removeOp.get(OPERATION_HEADERS, ALLOW_RESOURCE_SERVICE_RESTART).set(true); + assertSuccess(modelControllerClient.execute(removeOp)); + } + + @Test + public void testEncryptedSystemProperties() throws Exception { + + PathAddress propAddress = PathAddress.pathAddress(SYSTEM_PROPERTY, PROP); + try (ModelControllerClient client = TestSuiteEnvironment.getModelControllerClient()) { + + assertNull(getSystemProperty(client, PROP)); + assertNull(getSystemProperty(client, MISSING_PROP)); + + ModelNode response = createExpression(client); + assertSuccess(response); + String expression = response.get(ClientConstants.RESULT).get("expression").asString(); + + ModelNode addOp = Util.createAddOperation(propAddress); + addOp.get(VALUE).set(expression); + + response = client.execute(addOp); + assertSuccess(response); + + assertEquals(CLEAR_TEXT, getSystemProperty(client, PROP)); + + removeExpressionEncryption(client); + + // Removing the resolver doesn't affect the system property + assertEquals(CLEAR_TEXT, getSystemProperty(client, PROP)); + + ModelNode missingAddOp = Util.createAddOperation(PathAddress.pathAddress(SYSTEM_PROPERTY, MISSING_PROP)); + missingAddOp.get(VALUE).set(expression); + // The add should succeed, but the expression would be resolved as if it were a standard expression with a default + assertSuccess(client.execute(missingAddOp)); + assertEquals(expression.substring(expression.indexOf(":Default:"), expression.length() - 1), getSystemProperty(client, MISSING_PROP)); + + // Clean up + assertSuccess(client.execute(Util.createRemoveOperation(PathAddress.pathAddress(SYSTEM_PROPERTY, MISSING_PROP)))); + assertNull(getSystemProperty(client, MISSING_PROP)); + + // If the prop and the resolver are added in a composite, then proper resolution can occur during op execution + ModelNode composite = Util.createEmptyOperation(COMPOSITE, PathAddress.EMPTY_ADDRESS); + ModelNode steps = composite.get(STEPS); + steps.add(missingAddOp); + + steps.add(getAddExpressionEncyryptionOp()); + + assertSuccess(client.execute(composite)); + + assertEquals(CLEAR_TEXT, getSystemProperty(client, MISSING_PROP)); + + // Test boot behavior. Reload and confirm the property is set. + ServerReload.executeReloadAndWaitForCompletion(client); + + assertEquals(CLEAR_TEXT, getSystemProperty(client, PROP)); + + } + } + + private static ModelNode createExpression(ModelControllerClient client) throws IOException { + ModelNode createExpression = Util.createEmptyOperation("create-expression", ENCRYPTION_ADDRESS); + createExpression.get("resolver").set("Default"); + createExpression.get("clear-text").set(CLEAR_TEXT); + + return client.execute(createExpression); + } + + private static void assertSuccess(ModelNode response) { + if (!response.get(OUTCOME).asString().equals(SUCCESS)) { + Assert.fail(response.toJSONString(false)); + } + } + + private static String getSystemProperty(ModelControllerClient client, String property) throws IOException { + ModelNode response = client.execute(Util.getReadAttributeOperation(RUNTIME_ADDRESS, "system-properties")); + assertSuccess(response); + ModelNode val = response.get(RESULT, property); + return val.isDefined() ? val.asString() : null; + } +} diff --git a/testsuite/integration/subsystem/src/test/java/org/wildfly/test/feature/pack/elytron/tls/subsystem/sanity/SubsystemSanityTestCase.java b/testsuite/integration/subsystem/src/test/java/org/wildfly/test/integration/elytron/tls/subsystem/sanity/ElytronTlsSanityTestCase.java similarity index 90% rename from testsuite/integration/subsystem/src/test/java/org/wildfly/test/feature/pack/elytron/tls/subsystem/sanity/SubsystemSanityTestCase.java rename to testsuite/integration/subsystem/src/test/java/org/wildfly/test/integration/elytron/tls/subsystem/sanity/ElytronTlsSanityTestCase.java index 82ebc79..c12bb23 100644 --- a/testsuite/integration/subsystem/src/test/java/org/wildfly/test/feature/pack/elytron/tls/subsystem/sanity/SubsystemSanityTestCase.java +++ b/testsuite/integration/subsystem/src/test/java/org/wildfly/test/integration/elytron/tls/subsystem/sanity/ElytronTlsSanityTestCase.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.wildfly.test.feature.pack.elytron.tls.subsystem.sanity; +package org.wildfly.test.integration.elytron.tls.subsystem.sanity; import javax.inject.Inject; @@ -33,7 +33,7 @@ * @author Kabir Khan */ @RunWith(Arquillian.class) -public class SubsystemSanityTestCase { +public class ElytronTlsSanityTestCase { @Inject @ExampleQualifier @@ -43,7 +43,7 @@ public class SubsystemSanityTestCase { public static WebArchive getDeployment() { final WebArchive webArchive = ShrinkWrap.create(WebArchive.class, "sanity-test.war") .addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml") - .addPackage(SubsystemSanityTestCase.class.getPackage()); + .addPackage(ElytronTlsSanityTestCase.class.getPackage()); return webArchive; } diff --git a/testsuite/integration/subsystem/src/test/java/org/wildfly/test/feature/pack/elytron/tls/subsystem/tls/OpenSslTlsTestCase.java b/testsuite/integration/subsystem/src/test/java/org/wildfly/test/integration/elytron/tls/subsystem/tls/OpenSslTlsTestCase.java similarity index 94% rename from testsuite/integration/subsystem/src/test/java/org/wildfly/test/feature/pack/elytron/tls/subsystem/tls/OpenSslTlsTestCase.java rename to testsuite/integration/subsystem/src/test/java/org/wildfly/test/integration/elytron/tls/subsystem/tls/OpenSslTlsTestCase.java index 2e47ca1..7832219 100644 --- a/testsuite/integration/subsystem/src/test/java/org/wildfly/test/feature/pack/elytron/tls/subsystem/tls/OpenSslTlsTestCase.java +++ b/testsuite/integration/subsystem/src/test/java/org/wildfly/test/integration/elytron/tls/subsystem/tls/OpenSslTlsTestCase.java @@ -15,7 +15,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. */ -package org.wildfly.test.feature.pack.elytron.tls.subsystem.tls; +package org.wildfly.test.integration.elytron.tls.subsystem.tls; import static org.jboss.as.controller.client.helpers.ClientConstants.CONTENT; import static org.jboss.as.controller.client.helpers.ClientConstants.DEPLOYMENT; @@ -74,21 +74,21 @@ import org.wildfly.core.testrunner.ManagementClient; import org.wildfly.core.testrunner.ServerSetupTask; import org.wildfly.core.testrunner.WildflyTestRunner; -import org.wildfly.extension.elytron.ElytronExtension; +import org.wildfly.extension.elytron.tls.subsystem.ElytronTlsExtension; import org.wildfly.openssl.OpenSSLProvider; import org.wildfly.security.ssl.CipherSuiteSelector; import org.wildfly.security.ssl.ProtocolSelector; import org.wildfly.security.ssl.SSLContextBuilder; import org.wildfly.security.ssl.test.util.CAGenerationTool; import org.wildfly.security.ssl.test.util.CAGenerationTool.Identity; -import org.wildfly.test.feature.pack.elytron.tls.subsystem.common.TestRunnerConfigSetupTask; -import org.wildfly.test.feature.pack.elytron.tls.subsystem.common.CliPath; -import org.wildfly.test.feature.pack.elytron.tls.subsystem.common.ConfigurableElement; -import org.wildfly.test.feature.pack.elytron.tls.subsystem.common.SimpleServerSslContext; -import org.wildfly.test.feature.pack.elytron.tls.subsystem.common.CredentialReference; -import org.wildfly.test.feature.pack.elytron.tls.subsystem.common.SimpleKeyManager; -import org.wildfly.test.feature.pack.elytron.tls.subsystem.common.SimpleKeyStore; -import org.wildfly.test.feature.pack.elytron.tls.subsystem.common.SimpleTrustManager; +import org.wildfly.test.security.common.TestRunnerConfigSetupTask; +import org.wildfly.test.security.common.elytron.ConfigurableElement; +import org.wildfly.test.security.common.elytron.CredentialReference; +import org.wildfly.test.security.common.elytron.tls.subsystem.SimpleTlsKeyManager; +import org.wildfly.test.security.common.elytron.tls.subsystem.SimpleTlsKeyStore; +import org.wildfly.test.security.common.elytron.tls.subsystem.SimpleTlsServerSslContext; +import org.wildfly.test.security.common.elytron.tls.subsystem.SimpleTlsTrustManager; +import org.wildfly.test.security.common.elytron.tls.subsystem.CliPath; import org.wildfly.test.undertow.UndertowSSLService; import org.wildfly.test.undertow.UndertowSSLServiceActivator; import org.wildfly.test.undertow.UndertowServiceActivator; @@ -113,7 +113,7 @@ public class OpenSslTlsTestCase { private static final String SERVER_TRUST_MANAGER_NAME = "serverTM"; private static final String SERVER_SSL_CONTEXT_NAME = "test-context"; - private static final PathAddress ROOT_ADDRESS = PathAddress.pathAddress(SUBSYSTEM, ElytronExtension.SUBSYSTEM_NAME); + private static final PathAddress ROOT_ADDRESS = PathAddress.pathAddress(SUBSYSTEM, ElytronTlsExtension.SUBSYSTEM_NAME); private static final PathAddress SERVER_SSL_CONTEXT_ADDRESS = ROOT_ADDRESS.append("server-ssl-context", SERVER_SSL_CONTEXT_NAME); private static final Pattern OPENSSL_TLSv13_PATTERN = Pattern.compile("^(TLS_AES_128_GCM_SHA256|TLS_AES_256_GCM_SHA384|TLS_CHACHA20_POLY1305_SHA256|TLS_AES_128_CCM_SHA256|TLS_AES_128_CCM_8_SHA256)$"); @@ -199,7 +199,7 @@ protected ConfigurableElement[] getConfigurableElements() { .build(); // KeyStores - final SimpleKeyStore.Builder ksCommon = SimpleKeyStore.builder() + final SimpleTlsKeyStore.Builder ksCommon = SimpleTlsKeyStore.builder() .withType("JKS") .withCredentialReference(credentialReference); elements.add(ksCommon.withName(SERVER_KEY_STORE_NAME) @@ -214,19 +214,19 @@ protected ConfigurableElement[] getConfigurableElements() { .build()); // Key and Trust Managers - elements.add(SimpleKeyManager.builder() + elements.add(SimpleTlsKeyManager.builder() .withName(SERVER_KEY_MANAGER_NAME) .withCredentialReference(credentialReference) .withKeyStore(SERVER_KEY_STORE_NAME) .build()); elements.add( - SimpleTrustManager.builder() + SimpleTlsTrustManager.builder() .withName(SERVER_TRUST_MANAGER_NAME) .withKeyStore(SERVER_TRUST_STORE_NAME) .build()); // SSLContext with OpenSSL provider - elements.add(SimpleServerSslContext.builder() + elements.add(SimpleTlsServerSslContext.builder() .withName(SERVER_SSL_CONTEXT_NAME) .withKeyManagers(SERVER_KEY_MANAGER_NAME) .withTrustManagers(SERVER_TRUST_MANAGER_NAME) @@ -235,7 +235,7 @@ protected ConfigurableElement[] getConfigurableElements() { .withCipherSuiteNames("TLS_AES_256_GCM_SHA384:TLS_AES_128_CCM_8_SHA256") .build()); - return elements.toArray(new ConfigurableElement[elements.size()]); + return elements.toArray(new ConfigurableElement[0]); } @Override diff --git a/testsuite/integration/subsystem/src/test/java/org/wildfly/test/integration/elytron/tls/subsystem/tls/ServerSslSniContextTestCase.java b/testsuite/integration/subsystem/src/test/java/org/wildfly/test/integration/elytron/tls/subsystem/tls/ServerSslSniContextTestCase.java new file mode 100644 index 0000000..bf894a1 --- /dev/null +++ b/testsuite/integration/subsystem/src/test/java/org/wildfly/test/integration/elytron/tls/subsystem/tls/ServerSslSniContextTestCase.java @@ -0,0 +1,90 @@ +/* + * Copyright 2019 Red Hat, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.wildfly.test.integration.elytron.tls.subsystem.tls; + +import static org.hamcrest.CoreMatchers.containsString; + +import org.hamcrest.MatcherAssert; +import org.jboss.as.test.integration.management.util.CLIWrapper; +import org.jboss.as.test.integration.management.util.ServerReload; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.wildfly.core.testrunner.ServerSetup; +import org.wildfly.core.testrunner.WildflyTestRunner; + +@Ignore("SNI is not implemented yet") +@ServerSetup(ServerReload.SetupTask.class) +@RunWith(WildflyTestRunner.class) +public class ServerSslSniContextTestCase { + CLIWrapper cli; + + @Before + public void setup() throws Exception { + cli = new CLIWrapper(true); + // add server-ssl-sni-context + cli.sendLine("/subsystem=elytron-tls/key-store=exampleKeyStore:add(path=server.keystore,relative-to=jboss.server.config.dir,credential-reference={clear-text=\"keystore_password\"},type=JKS)"); + cli.sendLine("/subsystem=elytron-tls/key-manager=exampleKeyManager:add(key-store=exampleKeyStore,alias-filter=server,credential-reference={clear-text=\"key_password\"})"); + cli.sendLine("/subsystem=elytron-tls/server-ssl-context=exampleSslContext:add(key-manager=exampleKeyManager)"); + cli.sendLine("/subsystem=elytron-tls/server-ssl-sni-context=exampleSslSniContext:add(default-ssl-context=exampleSslContext"); + } + + @After + public void cleanup() throws Exception { + removeTestResources(); + cli.close(); + } + + @Test + public void testInvalidHostContextMapValue() { + boolean success = cli.sendLine("/subsystem=elytron-tls/server-ssl-sni-context=exampleSslSniContext:write-attribute(name=host-context-map,value={\"\\\\?.invalid.com\"=exampleSslContext})", true); + Assert.assertFalse(success); + MatcherAssert.assertThat("Wrong error message", cli.readOutput(), containsString("Invalid value of host context map")); + success = cli.sendLine("/subsystem=elytron-tls/server-ssl-sni-context=exampleSslSniContext:write-attribute(name=host-context-map,value={\"invalid\\\\.\\\\.example.com\"=exampleSslContext})", true); + Assert.assertFalse(success); + MatcherAssert.assertThat("Wrong error message", cli.readOutput(), containsString("Invalid value of host context map")); + success = cli.sendLine("/subsystem=elytron-tls/server-ssl-sni-context=exampleSslSniContext:write-attribute(name=host-context-map,value={\"*\\.invalid.com\"=exampleSslContext})", true); + Assert.assertFalse(success); + MatcherAssert.assertThat("Wrong error message", cli.readOutput(), containsString("Invalid value of host context map")); + success = cli.sendLine("/subsystem=elytron-tls/server-ssl-sni-context=exampleSslSniContext:write-attribute(name=host-context-map,value={\"invalid.com-\"=exampleSslContext})", true); + Assert.assertFalse(success); + MatcherAssert.assertThat("Wrong error message", cli.readOutput(), containsString("Invalid value of host context map")); + success = cli.sendLine("/subsystem=elytron-tls/server-ssl-sni-context=exampleSslSniContext:write-attribute(name=host-context-map,value={\"invalid.com\\\\.\"=exampleSslContext})", true); + Assert.assertFalse(success); + MatcherAssert.assertThat("Wrong error message", cli.readOutput(), containsString("Invalid value of host context map")); + } + + @Test + public void testValidHostContextMapValue() { + boolean success = cli.sendLine("/subsystem=elytron-tls/server-ssl-sni-context=exampleSslSniContext:write-attribute(name=host-context-map,value={\"..valid\\\\.example\\\\.com\"=exampleSslContext})", true); + Assert.assertTrue(success); + success = cli.sendLine("/subsystem=elytron-tls/server-ssl-sni-context=exampleSslSniContext:write-attribute(name=host-context-map,value={\"valid\\\\.example\\\\.com\"=exampleSslContext})", true); + Assert.assertTrue(success); + success = cli.sendLine("/subsystem=elytron-tls/server-ssl-sni-context=exampleSslSniContext:write-attribute(name=host-context-map,value={\"[^.]*\\\\.example\\\\.com\"=exampleSslContext})", true); + Assert.assertTrue(success); + } + + private void removeTestResources() { + cli.sendLine("/subsystem=elytron-tls/server-ssl-sni-context=exampleSslSniContext:remove"); + cli.sendLine("/subsystem=elytron-tls/server-ssl-context=exampleSslContext:remove"); + cli.sendLine("/subsystem=elytron-tls/key-manager=exampleKeyManager:remove"); + cli.sendLine("/subsystem=elytron-tls/key-store=exampleKeyStore:remove"); + } +} diff --git a/testsuite/integration/subsystem/src/test/java/org/wildfly/test/feature/pack/elytron/tls/subsystem/common/TestRunnerConfigSetupTask.java b/testsuite/integration/subsystem/src/test/java/org/wildfly/test/security/common/TestRunnerConfigSetupTask.java similarity index 95% rename from testsuite/integration/subsystem/src/test/java/org/wildfly/test/feature/pack/elytron/tls/subsystem/common/TestRunnerConfigSetupTask.java rename to testsuite/integration/subsystem/src/test/java/org/wildfly/test/security/common/TestRunnerConfigSetupTask.java index 6d629cd..92cb327 100644 --- a/testsuite/integration/subsystem/src/test/java/org/wildfly/test/feature/pack/elytron/tls/subsystem/common/TestRunnerConfigSetupTask.java +++ b/testsuite/integration/subsystem/src/test/java/org/wildfly/test/security/common/TestRunnerConfigSetupTask.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.wildfly.test.feature.pack.elytron.tls.subsystem.common; +package org.wildfly.test.security.common; import java.util.Arrays; import java.util.ListIterator; @@ -25,6 +25,7 @@ import org.jboss.logging.Logger; import org.wildfly.core.testrunner.ManagementClient; import org.wildfly.core.testrunner.ServerSetupTask; +import org.wildfly.test.security.common.elytron.ConfigurableElement; /** * WildFly TestRunner ServerSetupTask version of AbstractConfigSetupTask. @@ -49,7 +50,7 @@ public void tearDown(final ManagementClient managementClient) throws Exception { /** * Creates configuration elements (provided by implementation of {@link #getConfigurableElements()} method) and calls - * {@link ConfigurableElement#create(ModelControllerClient, CLIWrapper)} for them. + * {@link ConfigurableElement#create(CLIWrapper)} for them. */ protected void setup(final ModelControllerClient modelControllerClient) throws Exception { configurableElements = getConfigurableElements(); @@ -70,7 +71,7 @@ protected void setup(final ModelControllerClient modelControllerClient) throws E } /** - * Reverts configuration changes done by {@link #setup(ModelControllerClient)} method - i.e. calls {@link ConfigurableElement#remove(ModelControllerClient, CLIWrapper)} method + * Reverts configuration changes done by {@link #setup(ModelControllerClient)} method - i.e. calls {@link ConfigurableElement#remove(CLIWrapper)} method * on instances provided by {@link #getConfigurableElements()} (in reverse order). */ protected void tearDown(ModelControllerClient modelControllerClient) throws Exception { diff --git a/testsuite/integration/subsystem/src/test/java/org/wildfly/test/feature/pack/elytron/tls/subsystem/common/CliPath.java b/testsuite/integration/subsystem/src/test/java/org/wildfly/test/security/common/elytron/tls/subsystem/CliPath.java similarity index 97% rename from testsuite/integration/subsystem/src/test/java/org/wildfly/test/feature/pack/elytron/tls/subsystem/common/CliPath.java rename to testsuite/integration/subsystem/src/test/java/org/wildfly/test/security/common/elytron/tls/subsystem/CliPath.java index 0aeb2ce..e2af5ca 100644 --- a/testsuite/integration/subsystem/src/test/java/org/wildfly/test/feature/pack/elytron/tls/subsystem/common/CliPath.java +++ b/testsuite/integration/subsystem/src/test/java/org/wildfly/test/security/common/elytron/tls/subsystem/CliPath.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.wildfly.test.feature.pack.elytron.tls.subsystem.common; +package org.wildfly.test.security.common.elytron.tls.subsystem; import static org.apache.commons.lang3.StringUtils.isNotBlank; import static org.jboss.as.test.shared.CliUtils.escapePath; diff --git a/testsuite/integration/subsystem/src/test/java/org/wildfly/test/feature/pack/elytron/tls/subsystem/common/SimpleKeyManager.java b/testsuite/integration/subsystem/src/test/java/org/wildfly/test/security/common/elytron/tls/subsystem/SimpleTlsKeyManager.java similarity index 70% rename from testsuite/integration/subsystem/src/test/java/org/wildfly/test/feature/pack/elytron/tls/subsystem/common/SimpleKeyManager.java rename to testsuite/integration/subsystem/src/test/java/org/wildfly/test/security/common/elytron/tls/subsystem/SimpleTlsKeyManager.java index cd3a75b..7d29d52 100644 --- a/testsuite/integration/subsystem/src/test/java/org/wildfly/test/feature/pack/elytron/tls/subsystem/common/SimpleKeyManager.java +++ b/testsuite/integration/subsystem/src/test/java/org/wildfly/test/security/common/elytron/tls/subsystem/SimpleTlsKeyManager.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.wildfly.test.feature.pack.elytron.tls.subsystem.common; +package org.wildfly.test.security.common.elytron.tls.subsystem; import static org.apache.commons.lang3.ObjectUtils.defaultIfNull; import static org.wildfly.common.Assert.checkNotNullParamWithNullPointerException; @@ -22,18 +22,21 @@ import org.jboss.as.controller.client.ModelControllerClient; import org.jboss.as.test.integration.management.util.CLIWrapper; +import org.wildfly.test.security.common.elytron.AbstractConfigurableElement; +import org.wildfly.test.security.common.elytron.CredentialReference; /** - * Elytron key-manager configuration implementation. + * Elytron TLS key-manager configuration implementation. * * @author Josef Cacek + * @author Cameron Rodriguez */ -public class SimpleKeyManager extends AbstractConfigurableElement { +public class SimpleTlsKeyManager extends AbstractConfigurableElement { private final String keyStore; private final CredentialReference credentialReference; - private SimpleKeyManager(Builder builder) { + private SimpleTlsKeyManager(Builder builder) { super(builder); this.keyStore = checkNotNullParamWithNullPointerException("builder.keyStore", builder.keyStore); this.credentialReference = defaultIfNull(builder.credentialReference, CredentialReference.EMPTY); @@ -41,19 +44,19 @@ private SimpleKeyManager(Builder builder) { @Override public void create(ModelControllerClient client, CLIWrapper cli) throws Exception { - // /subsystem=elytron/key-manager=httpsKM:add(key-store=httpsKS,algorithm="SunX509",credential-reference={clear-text=secret}) + // /subsystem=elytron-tls/key-manager=httpsKM:add(key-store=httpsKS,algorithm="SunX509",credential-reference={clear-text=secret}) - cli.sendLine(String.format("/subsystem=elytron/key-manager=%s:add(key-store=\"%s\",algorithm=\"%s\", %s)", name, + cli.sendLine(String.format("/subsystem=elytron-tls/key-manager=%s:add(key-store=\"%s\",algorithm=\"%s\", %s)", name, keyStore, KeyManagerFactory.getDefaultAlgorithm(), credentialReference.asString())); } @Override public void remove(ModelControllerClient client, CLIWrapper cli) throws Exception { - cli.sendLine(String.format("/subsystem=elytron/key-manager=%s:remove()", name)); + cli.sendLine(String.format("/subsystem=elytron-tls/key-manager=%s:remove()", name)); } /** - * Creates builder to build {@link SimpleKeyManager}. + * Creates builder to build {@link SimpleTlsKeyManager}. * * @return created builder */ @@ -62,7 +65,7 @@ public static Builder builder() { } /** - * Builder to build {@link SimpleKeyManager}. + * Builder to build {@link SimpleTlsKeyManager}. */ public static final class Builder extends AbstractConfigurableElement.Builder { private String keyStore; @@ -81,8 +84,8 @@ public Builder withCredentialReference(CredentialReference credentialReference) return this; } - public SimpleKeyManager build() { - return new SimpleKeyManager(this); + public SimpleTlsKeyManager build() { + return new SimpleTlsKeyManager(this); } @Override diff --git a/testsuite/integration/subsystem/src/test/java/org/wildfly/test/feature/pack/elytron/tls/subsystem/common/SimpleKeyStore.java b/testsuite/integration/subsystem/src/test/java/org/wildfly/test/security/common/elytron/tls/subsystem/SimpleTlsKeyStore.java similarity index 71% rename from testsuite/integration/subsystem/src/test/java/org/wildfly/test/feature/pack/elytron/tls/subsystem/common/SimpleKeyStore.java rename to testsuite/integration/subsystem/src/test/java/org/wildfly/test/security/common/elytron/tls/subsystem/SimpleTlsKeyStore.java index 934ff58..a002974 100644 --- a/testsuite/integration/subsystem/src/test/java/org/wildfly/test/feature/pack/elytron/tls/subsystem/common/SimpleKeyStore.java +++ b/testsuite/integration/subsystem/src/test/java/org/wildfly/test/security/common/elytron/tls/subsystem/SimpleTlsKeyStore.java @@ -13,26 +13,29 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.wildfly.test.feature.pack.elytron.tls.subsystem.common; +package org.wildfly.test.security.common.elytron.tls.subsystem; import static org.apache.commons.lang3.ObjectUtils.defaultIfNull; import org.jboss.as.controller.client.ModelControllerClient; import org.jboss.as.test.integration.management.util.CLIWrapper; +import org.wildfly.test.security.common.elytron.AbstractConfigurableElement; +import org.wildfly.test.security.common.elytron.CredentialReference; /** - * Elytron key-store configuration implementation. + * Elytron TLS key-store configuration implementation. * * @author Josef Cacek + * @author Cameron Rodriguez */ -public class SimpleKeyStore extends AbstractConfigurableElement { +public class SimpleTlsKeyStore extends AbstractConfigurableElement { private final CliPath path; private final CredentialReference credentialReference; private final String type; private final boolean required; - private SimpleKeyStore(Builder builder) { + private SimpleTlsKeyStore(Builder builder) { super(builder); this.path = defaultIfNull(builder.path, CliPath.EMPTY); this.credentialReference = defaultIfNull(builder.credentialReference, CredentialReference.EMPTY); @@ -42,19 +45,19 @@ private SimpleKeyStore(Builder builder) { @Override public void create(ModelControllerClient client, CLIWrapper cli) throws Exception { - // /subsystem=elytron/key-store=httpsKS:add(path=keystore.jks,relative-to=jboss.server.config.dir, + // /subsystem=elytron-tls/key-store=httpsKS:add(path=keystore.jks,relative-to=jboss.server.config.dir, // credential-reference={clear-text=secret},type=JKS,required=false) - cli.sendLine(String.format("/subsystem=elytron/key-store=%s:add(%s%stype=\"%s\",required=%s)", name, path.asString(), - credentialReference.asString(), type, Boolean.toString(required))); + cli.sendLine(String.format("/subsystem=elytron-tls/key-store=%s:add(%s%stype=\"%s\",required=%s)", name, path.asString(), + credentialReference.asString(), type, required)); } @Override public void remove(ModelControllerClient client, CLIWrapper cli) throws Exception { - cli.sendLine(String.format("/subsystem=elytron/key-store=%s:remove()", name)); + cli.sendLine(String.format("/subsystem=elytron-tls/key-store=%s:remove()", name)); } /** - * Creates builder to build {@link SimpleKeyStore}. + * Creates builder to build {@link SimpleTlsKeyStore}. * * @return created builder */ @@ -63,7 +66,7 @@ public static Builder builder() { } /** - * Builder to build {@link SimpleKeyStore}. + * Builder to build {@link SimpleTlsKeyStore}. */ public static final class Builder extends AbstractConfigurableElement.Builder { private CliPath path; @@ -94,8 +97,8 @@ public Builder withRequired(boolean required) { return this; } - public SimpleKeyStore build() { - return new SimpleKeyStore(this); + public SimpleTlsKeyStore build() { + return new SimpleTlsKeyStore(this); } @Override diff --git a/testsuite/integration/subsystem/src/test/java/org/wildfly/test/feature/pack/elytron/tls/subsystem/common/SimpleServerSslContext.java b/testsuite/integration/subsystem/src/test/java/org/wildfly/test/security/common/elytron/tls/subsystem/SimpleTlsServerSslContext.java similarity index 79% rename from testsuite/integration/subsystem/src/test/java/org/wildfly/test/feature/pack/elytron/tls/subsystem/common/SimpleServerSslContext.java rename to testsuite/integration/subsystem/src/test/java/org/wildfly/test/security/common/elytron/tls/subsystem/SimpleTlsServerSslContext.java index 74c98b8..def7934 100644 --- a/testsuite/integration/subsystem/src/test/java/org/wildfly/test/feature/pack/elytron/tls/subsystem/common/SimpleServerSslContext.java +++ b/testsuite/integration/subsystem/src/test/java/org/wildfly/test/security/common/elytron/tls/subsystem/SimpleTlsServerSslContext.java @@ -13,35 +13,35 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.wildfly.test.feature.pack.elytron.tls.subsystem.common; +package org.wildfly.test.security.common.elytron.tls.subsystem; import java.util.StringJoiner; import org.apache.commons.lang3.StringUtils; import org.jboss.as.controller.client.ModelControllerClient; import org.jboss.as.test.integration.management.util.CLIWrapper; +import org.wildfly.test.security.common.elytron.AbstractConfigurableElement; /** - * Elytron server-ssl-context configuration implementation. + * Elytron TLS server-ssl-context configuration implementation. * * @author Josef Cacek + * @author Cameron Rodriguez */ -public class SimpleServerSslContext extends AbstractConfigurableElement { +public class SimpleTlsServerSslContext extends AbstractConfigurableElement { private final String keyManager; private final String trustManager; - private final String securityDomain; private final String[] protocols; private final boolean needClientAuth; private final Boolean authenticationOptional; private final String providers; private final String cipherSuiteNames; - private SimpleServerSslContext(Builder builder) { + private SimpleTlsServerSslContext(Builder builder) { super(builder); this.keyManager = builder.keyManager; this.trustManager = builder.trustManager; - this.securityDomain = builder.securityDomain; this.protocols = builder.protocols; this.needClientAuth = builder.needClientAuth; this.authenticationOptional = builder.authenticationOptional; @@ -51,9 +51,9 @@ private SimpleServerSslContext(Builder builder) { @Override public void create(ModelControllerClient client, CLIWrapper cli) throws Exception { - // /subsystem=elytron/server-ssl-context=twoWaySSC:add(key-manager=twoWayKM,protocols=["TLSv1.2"], + // /subsystem=elytron-tls/server-ssl-context=twoWaySSC:add(key-manager=twoWayKM,protocols=["TLSv1.2"], // trust-manager=twoWayTM,security-domain=test,need-client-auth=true) - StringBuilder sb = new StringBuilder("/subsystem=elytron/server-ssl-context=").append(name).append(":add("); + StringBuilder sb = new StringBuilder("/subsystem=elytron-tls/server-ssl-context=").append(name).append(":add("); if (StringUtils.isNotBlank(keyManager)) { sb.append("key-manager=\"").append(keyManager).append("\", "); } @@ -64,14 +64,11 @@ public void create(ModelControllerClient client, CLIWrapper cli) throws Exceptio joiner.add(s1); } sb.append("protocols=[") - .append(joiner.toString()).append("], "); + .append(joiner).append("], "); } if (StringUtils.isNotBlank(trustManager)) { sb.append("trust-manager=\"").append(trustManager).append("\", "); } - if (StringUtils.isNotBlank(securityDomain)) { - sb.append("security-domain=\"").append(securityDomain).append("\", "); - } if (authenticationOptional != null) { sb.append("authentication-optional=").append(authenticationOptional).append(", "); } @@ -87,11 +84,11 @@ public void create(ModelControllerClient client, CLIWrapper cli) throws Exceptio @Override public void remove(ModelControllerClient client, CLIWrapper cli) throws Exception { - cli.sendLine(String.format("/subsystem=elytron/server-ssl-context=%s:remove()", name)); + cli.sendLine(String.format("/subsystem=elytron-tls/server-ssl-context=%s:remove()", name)); } /** - * Creates builder to build {@link SimpleServerSslContext}. + * Creates builder to build {@link SimpleTlsServerSslContext}. * * @return created builder */ @@ -100,12 +97,11 @@ public static Builder builder() { } /** - * Builder to build {@link SimpleServerSslContext}. + * Builder to build {@link SimpleTlsServerSslContext}. */ public static final class Builder extends AbstractConfigurableElement.Builder { private String keyManager; private String trustManager; - private String securityDomain; private String[] protocols; private boolean needClientAuth; private Boolean authenticationOptional; @@ -125,11 +121,6 @@ public Builder withTrustManagers(String trustManagers) { return this; } - public Builder withSecurityDomain(String securityDomain) { - this.securityDomain = securityDomain; - return this; - } - public Builder withProtocols(String... protocols) { this.protocols = protocols; return this; @@ -155,8 +146,8 @@ public Builder withCipherSuiteNames(String cipherSuiteNames) { return this; } - public SimpleServerSslContext build() { - return new SimpleServerSslContext(this); + public SimpleTlsServerSslContext build() { + return new SimpleTlsServerSslContext(this); } @Override diff --git a/testsuite/integration/subsystem/src/test/java/org/wildfly/test/feature/pack/elytron/tls/subsystem/common/SimpleTrustManager.java b/testsuite/integration/subsystem/src/test/java/org/wildfly/test/security/common/elytron/tls/subsystem/SimpleTlsTrustManager.java similarity index 66% rename from testsuite/integration/subsystem/src/test/java/org/wildfly/test/feature/pack/elytron/tls/subsystem/common/SimpleTrustManager.java rename to testsuite/integration/subsystem/src/test/java/org/wildfly/test/security/common/elytron/tls/subsystem/SimpleTlsTrustManager.java index d101f60..18e6a99 100644 --- a/testsuite/integration/subsystem/src/test/java/org/wildfly/test/feature/pack/elytron/tls/subsystem/common/SimpleTrustManager.java +++ b/testsuite/integration/subsystem/src/test/java/org/wildfly/test/security/common/elytron/tls/subsystem/SimpleTlsTrustManager.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.wildfly.test.feature.pack.elytron.tls.subsystem.common; +package org.wildfly.test.security.common.elytron.tls.subsystem; import static org.wildfly.common.Assert.checkNotNullParamWithNullPointerException; @@ -21,36 +21,38 @@ import org.jboss.as.controller.client.ModelControllerClient; import org.jboss.as.test.integration.management.util.CLIWrapper; +import org.wildfly.test.security.common.elytron.AbstractConfigurableElement; /** - * Elytron trust-managers configuration implementation. + * Elytron TLS trust-managers configuration implementation. * * @author Josef Cacek + * @author Cameron Rodriguez */ -public class SimpleTrustManager extends AbstractConfigurableElement { +public class SimpleTlsTrustManager extends AbstractConfigurableElement { private final String keyStore; - private SimpleTrustManager(Builder builder) { + private SimpleTlsTrustManager(Builder builder) { super(builder); this.keyStore = checkNotNullParamWithNullPointerException("builder.keyStore", builder.keyStore); } @Override public void create(ModelControllerClient client, CLIWrapper cli) throws Exception { - // /subsystem=elytron/trust-manager=twoWayTM:add(key-store=twoWayTS,algorithm="SunX509") + // /subsystem=elytron-tls/trust-manager=twoWayTM:add(key-store=twoWayTS,algorithm="SunX509") - cli.sendLine(String.format("/subsystem=elytron/trust-manager=%s:add(key-store=\"%s\",algorithm=\"%s\")", name, + cli.sendLine(String.format("/subsystem=elytron-tls/trust-manager=%s:add(key-store=\"%s\",algorithm=\"%s\")", name, keyStore, KeyManagerFactory.getDefaultAlgorithm())); } @Override public void remove(ModelControllerClient client, CLIWrapper cli) throws Exception { - cli.sendLine(String.format("/subsystem=elytron/trust-manager=%s:remove()", name)); + cli.sendLine(String.format("/subsystem=elytron-tls/trust-manager=%s:remove()", name)); } /** - * Creates builder to build {@link SimpleTrustManager}. + * Creates builder to build {@link SimpleTlsTrustManager}. * * @return created builder */ @@ -59,7 +61,7 @@ public static Builder builder() { } /** - * Builder to build {@link SimpleTrustManager}. + * Builder to build {@link SimpleTlsTrustManager}. */ public static final class Builder extends AbstractConfigurableElement.Builder { private String keyStore; @@ -72,8 +74,8 @@ public Builder withKeyStore(String keyStore) { return this; } - public SimpleTrustManager build() { - return new SimpleTrustManager(this); + public SimpleTlsTrustManager build() { + return new SimpleTlsTrustManager(this); } @Override diff --git a/testsuite/pom.xml b/testsuite/pom.xml index 3f254d4..d166d54 100644 --- a/testsuite/pom.xml +++ b/testsuite/pom.xml @@ -96,7 +96,6 @@ none - -Djboss.dist=${jboss.dist} -Djava.io.tmpdir=${basedir}/target