diff --git a/core-feature-pack/common/pom.xml b/core-feature-pack/common/pom.xml
index e669b53d874..9c2eac5259e 100644
--- a/core-feature-pack/common/pom.xml
+++ b/core-feature-pack/common/pom.xml
@@ -322,6 +322,10 @@
org.wildfly.security
wildfly-elytron-digest
+
+ org.wildfly.security
+ wildfly-elytron-dynamic-ssl
+
org.wildfly.security
wildfly-elytron-encryption
diff --git a/core-feature-pack/common/src/main/resources/modules/system/layers/base/org/wildfly/security/elytron-base/main/module.xml b/core-feature-pack/common/src/main/resources/modules/system/layers/base/org/wildfly/security/elytron-base/main/module.xml
index 792b2bd42fa..8753b51d7e9 100644
--- a/core-feature-pack/common/src/main/resources/modules/system/layers/base/org/wildfly/security/elytron-base/main/module.xml
+++ b/core-feature-pack/common/src/main/resources/modules/system/layers/base/org/wildfly/security/elytron-base/main/module.xml
@@ -34,6 +34,7 @@
+
diff --git a/elytron/pom.xml b/elytron/pom.xml
index bf2df18ca9e..8832725e19d 100644
--- a/elytron/pom.xml
+++ b/elytron/pom.xml
@@ -64,6 +64,11 @@
wildfly-elytron-auth
+
+ org.wildfly.security
+ wildfly-elytron-dynamic-ssl
+
+
org.wildfly.security
wildfly-elytron-realm-jdbc
diff --git a/elytron/src/main/java/org/wildfly/extension/elytron/AuthenticationClientDefinitions.java b/elytron/src/main/java/org/wildfly/extension/elytron/AuthenticationClientDefinitions.java
index 7e602ea4d9f..0a2868ce658 100644
--- a/elytron/src/main/java/org/wildfly/extension/elytron/AuthenticationClientDefinitions.java
+++ b/elytron/src/main/java/org/wildfly/extension/elytron/AuthenticationClientDefinitions.java
@@ -15,6 +15,7 @@
import static org.wildfly.extension.elytron.Capabilities.SECURITY_DOMAIN_CAPABILITY;
import static org.wildfly.extension.elytron.Capabilities.SECURITY_FACTORY_CREDENTIAL_CAPABILITY;
import static org.wildfly.extension.elytron.Capabilities.SSL_CONTEXT_CAPABILITY;
+import static org.wildfly.extension.elytron.ElytronDefinition.commonRequirements;
import static org.wildfly.extension.elytron._private.ElytronSubsystemMessages.ROOT_LOGGER;
import java.util.HashMap;
@@ -42,6 +43,8 @@
import org.jboss.dmr.ModelNode;
import org.jboss.dmr.ModelType;
import org.jboss.msc.service.ServiceBuilder;
+import org.jboss.msc.service.ServiceController;
+import org.jboss.msc.service.ServiceTarget;
import org.jboss.msc.value.InjectedValue;
import org.wildfly.common.function.ExceptionSupplier;
import org.wildfly.extension.elytron.TrivialService.ValueSupplier;
@@ -491,6 +494,17 @@ protected ValueSupplier getValueSupplier(ServiceBuilder finalContext.apply(parentSupplier.get());
}
+ @Override
+ protected void performRuntime(OperationContext context, ModelNode operation, ModelNode model) throws OperationFailedException {
+ commonRequirements(installService(context, model)).setInitialMode(ServiceController.Mode.ON_DEMAND).install();
+ }
+
+ ServiceBuilder installService(OperationContext context, ModelNode model) throws OperationFailedException {
+ ServiceTarget serviceTarget = context.getCapabilityServiceTarget();
+ ServiceBuilder> serviceBuilder = context.getCapabilityServiceTarget().addCapability(AUTHENTICATION_CONTEXT_RUNTIME_CAPABILITY);
+ TrivialService authenticationContextTrivialService = new TrivialService(getValueSupplier((ServiceBuilder) serviceBuilder, context, model));
+ return serviceTarget.addService(AUTHENTICATION_CONTEXT_RUNTIME_CAPABILITY.getCapabilityServiceName(context.getCurrentAddressValue()), authenticationContextTrivialService);
+ }
};
return new TrivialResourceDefinition(ElytronDescriptionConstants.AUTHENTICATION_CONTEXT, add, attributes,
diff --git a/elytron/src/main/java/org/wildfly/extension/elytron/ElytronDefinition.java b/elytron/src/main/java/org/wildfly/extension/elytron/ElytronDefinition.java
index a21c73958f2..8d74e3c5cb4 100644
--- a/elytron/src/main/java/org/wildfly/extension/elytron/ElytronDefinition.java
+++ b/elytron/src/main/java/org/wildfly/extension/elytron/ElytronDefinition.java
@@ -294,6 +294,7 @@ public void registerChildren(ManagementResourceRegistration resourceRegistration
resourceRegistration.registerSubModel(SSLDefinitions.getServerSNISSLContextDefinition());
resourceRegistration.registerSubModel(new CertificateAuthorityDefinition());
resourceRegistration.registerSubModel(new CertificateAuthorityAccountDefinition());
+ resourceRegistration.registerSubModel(SSLDefinitions.getDynamicClientSSLContextDefinition());
// Credential Store Block
resourceRegistration.registerSubModel(new CredentialStoreResourceDefinition());
diff --git a/elytron/src/main/java/org/wildfly/extension/elytron/ElytronDescriptionConstants.java b/elytron/src/main/java/org/wildfly/extension/elytron/ElytronDescriptionConstants.java
index 87b2310f58d..87fd07f4ae6 100644
--- a/elytron/src/main/java/org/wildfly/extension/elytron/ElytronDescriptionConstants.java
+++ b/elytron/src/main/java/org/wildfly/extension/elytron/ElytronDescriptionConstants.java
@@ -173,6 +173,8 @@ interface ElytronDescriptionConstants {
String DISTINGUISHED_NAME = "distinguished-name";
String DISTRIBUTED_REALM = "distributed-realm";
String DOMAIN_NAMES = "domain-names";
+ String DYNAMIC_CLIENT_SSL_CONTEXT = "dynamic-client-ssl-context";
+ String DYNAMIC_CLIENT_SSL_CONTEXTS = "dynamic-client-ssl-contexts";
String ELYTRON_SECURITY = "elytron-security";
String ENABLE_CONNECTION_POOLING = "enable-connection-pooling";
diff --git a/elytron/src/main/java/org/wildfly/extension/elytron/ElytronSubsystemSchema.java b/elytron/src/main/java/org/wildfly/extension/elytron/ElytronSubsystemSchema.java
index b23e9200029..0fcd01f9820 100644
--- a/elytron/src/main/java/org/wildfly/extension/elytron/ElytronSubsystemSchema.java
+++ b/elytron/src/main/java/org/wildfly/extension/elytron/ElytronSubsystemSchema.java
@@ -26,6 +26,7 @@
import static org.wildfly.extension.elytron.ElytronDescriptionConstants.PERMISSION_SETS;
import static org.wildfly.extension.elytron.ElytronDescriptionConstants.SECURITY_PROPERTY;
import static org.wildfly.extension.elytron.PermissionMapperDefinitions.PERMISSIONS;
+import static org.wildfly.extension.elytron.SSLDefinitions.getDynamicClientSSLContextDefinition;
/**
* Enumeration of elytron subsystem schema versions.
@@ -191,7 +192,9 @@ private void addCredentialStoreParser(PersistentResourceXMLDescription.Persisten
private void addTlsParser(PersistentResourceXMLDescription.PersistentResourceXMLBuilder builder) {
TlsParser tlsParser = new TlsParser();
- if (this.since(ElytronSubsystemSchema.VERSION_14_0)) {
+ if (this.since(ElytronSubsystemSchema.VERSION_18_0_COMMUNITY) && this.enables(getDynamicClientSSLContextDefinition())) {
+ builder.addChild(tlsParser.tlsParserCommunity_18_0);
+ } else if (this.since(ElytronSubsystemSchema.VERSION_14_0)) {
builder.addChild(tlsParser.tlsParser_14_0);
} else if (this.since(ElytronSubsystemSchema.VERSION_12_0)) {
builder.addChild(tlsParser.tlsParser_12_0);
diff --git a/elytron/src/main/java/org/wildfly/extension/elytron/ElytronSubsystemTransformers.java b/elytron/src/main/java/org/wildfly/extension/elytron/ElytronSubsystemTransformers.java
index 1cb38056733..faa4b93f973 100644
--- a/elytron/src/main/java/org/wildfly/extension/elytron/ElytronSubsystemTransformers.java
+++ b/elytron/src/main/java/org/wildfly/extension/elytron/ElytronSubsystemTransformers.java
@@ -170,6 +170,8 @@ private static void from18(ChainedTransformationDescriptionBuilder chainedBuilde
.setDiscard(DiscardAttributeChecker.UNDEFINED, EMIT_EVENTS)
.addRejectCheck(new RejectAttributeChecker.SimpleRejectAttributeChecker(ModelNode.TRUE), IGNORE_UNAVAILABLE_REALMS)
.addRejectCheck(RejectAttributeChecker.DEFINED, EMIT_EVENTS);
+ builder.rejectChildResource(PathElement.pathElement(ElytronDescriptionConstants.DYNAMIC_CLIENT_SSL_CONTEXTS));
+ builder.rejectChildResource(PathElement.pathElement(ElytronDescriptionConstants.DYNAMIC_CLIENT_SSL_CONTEXT));
}
private static void from17(ChainedTransformationDescriptionBuilder chainedBuilder) {
diff --git a/elytron/src/main/java/org/wildfly/extension/elytron/SSLContextResource.java b/elytron/src/main/java/org/wildfly/extension/elytron/SSLContextResource.java
index c9e5f8ad4ab..3fe5ebabcd3 100644
--- a/elytron/src/main/java/org/wildfly/extension/elytron/SSLContextResource.java
+++ b/elytron/src/main/java/org/wildfly/extension/elytron/SSLContextResource.java
@@ -21,6 +21,7 @@
import org.jboss.msc.service.ServiceController;
import org.jboss.msc.service.ServiceController.State;
import org.wildfly.common.iteration.ByteIterator;
+import org.wildfly.security.dynamic.ssl.DynamicSSLContext;
/**
* A {@link Resource} to represent a server-ssl-context/client-ssl-context, the majority is actually model
@@ -129,6 +130,9 @@ public Resource clone() {
*/
private boolean hasActiveSessions() {
final SSLContext sslContext = getSSLContext(sslContextServiceController);
+ if (sslContext instanceof DynamicSSLContext) {
+ return false;
+ }
if (sslContext == null) return false;
SSLSessionContext sslSessionContext = server ? sslContext.getServerSessionContext() : sslContext.getClientSessionContext();
return sslSessionContext.getIds().hasMoreElements();
diff --git a/elytron/src/main/java/org/wildfly/extension/elytron/SSLDefinitions.java b/elytron/src/main/java/org/wildfly/extension/elytron/SSLDefinitions.java
index 2a0615ecf7f..e46fbfc91da 100644
--- a/elytron/src/main/java/org/wildfly/extension/elytron/SSLDefinitions.java
+++ b/elytron/src/main/java/org/wildfly/extension/elytron/SSLDefinitions.java
@@ -8,6 +8,7 @@
import static org.jboss.as.controller.capability.RuntimeCapability.buildDynamicCapabilityName;
import static org.jboss.as.controller.security.CredentialReference.handleCredentialReferenceUpdate;
import static org.jboss.as.controller.security.CredentialReference.rollbackCredentialStoreUpdate;
+import static org.wildfly.extension.elytron.Capabilities.AUTHENTICATION_CONTEXT_CAPABILITY;
import static org.wildfly.extension.elytron.Capabilities.KEY_MANAGER_CAPABILITY;
import static org.wildfly.extension.elytron.Capabilities.KEY_MANAGER_RUNTIME_CAPABILITY;
import static org.wildfly.extension.elytron.Capabilities.KEY_STORE_CAPABILITY;
@@ -56,6 +57,7 @@
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BooleanSupplier;
+import java.util.function.Supplier;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
@@ -98,6 +100,7 @@
import org.jboss.as.controller.security.CredentialReference;
import org.jboss.as.controller.services.path.PathManager;
import org.jboss.as.controller.services.path.PathManagerService;
+import org.jboss.as.version.Stability;
import org.jboss.dmr.ModelNode;
import org.jboss.dmr.ModelType;
import org.jboss.msc.service.ServiceBuilder;
@@ -112,12 +115,16 @@
import org.wildfly.extension.elytron.TrivialService.ValueSupplier;
import org.wildfly.extension.elytron._private.ElytronSubsystemMessages;
import org.wildfly.extension.elytron.capabilities.PrincipalTransformer;
+import org.wildfly.security.auth.client.AuthenticationContext;
+import org.wildfly.security.dynamic.ssl.DynamicSSLContextImpl;
import org.wildfly.security.auth.server.MechanismConfiguration;
import org.wildfly.security.auth.server.MechanismConfigurationSelector;
import org.wildfly.security.auth.server.RealmMapper;
import org.wildfly.security.auth.server.SecurityDomain;
import org.wildfly.security.credential.PasswordCredential;
import org.wildfly.security.credential.source.CredentialSource;
+import org.wildfly.security.dynamic.ssl.DynamicSSLContext;
+import org.wildfly.security.dynamic.ssl.DynamicSSLContextException;
import org.wildfly.security.keystore.AliasFilter;
import org.wildfly.security.keystore.FilteringKeyStore;
import org.wildfly.security.password.interfaces.ClearPassword;
@@ -147,6 +154,13 @@ class SSLDefinitions {
.setRestartAllServices()
.build();
+ static final SimpleAttributeDefinition AUTHENTICATION_CONTEXT_ATTRIBUTE = new SimpleAttributeDefinitionBuilder(ElytronDescriptionConstants.AUTHENTICATION_CONTEXT, ModelType.STRING, false)
+ .setMinSize(1)
+ .setRequired(true)
+ .setCapabilityReference(AUTHENTICATION_CONTEXT_CAPABILITY, SSL_CONTEXT_CAPABILITY)
+ .setRestartAllServices()
+ .build();
+
static final SimpleAttributeDefinition PROVIDER_NAME = new SimpleAttributeDefinitionBuilder(ElytronDescriptionConstants.PROVIDER_NAME, ModelType.STRING, true)
.setAllowExpression(true)
.setMinSize(1)
@@ -1208,11 +1222,17 @@ public X509Certificate[] getAcceptedIssuers() {
}
private static ResourceDefinition createSSLContextDefinition(String pathKey, boolean server, AbstractAddStepHandler addHandler, AttributeDefinition[] attributes, boolean serverOrHostController) {
+ return createSSLContextDefinition(pathKey, server, addHandler, attributes, serverOrHostController, Stability.DEFAULT);
+ }
+
+ private static ResourceDefinition createSSLContextDefinition(String pathKey, boolean server, AbstractAddStepHandler addHandler, AttributeDefinition[] attributes, boolean serverOrHostController, Stability stability) {
+
Builder builder = TrivialResourceDefinition.builder()
.setPathKey(pathKey)
.setAddHandler(addHandler)
.setAttributes(attributes)
- .setRuntimeCapabilities(SSL_CONTEXT_RUNTIME_CAPABILITY);
+ .setRuntimeCapabilities(SSL_CONTEXT_RUNTIME_CAPABILITY)
+ .setStability(stability);
if (serverOrHostController) {
builder.addReadOnlyAttribute(ACTIVE_SESSION_COUNT, new SSLContextRuntimeHandler() {
@@ -1511,6 +1531,42 @@ protected void installedForResource(ServiceController serviceControl
return createSSLContextDefinition(ElytronDescriptionConstants.CLIENT_SSL_CONTEXT, false, add, attributes, serverOrHostController);
}
+ static ResourceDefinition getDynamicClientSSLContextDefinition() {
+
+ AttributeDefinition[] attributes = new AttributeDefinition[]{AUTHENTICATION_CONTEXT_ATTRIBUTE};
+ AbstractAddStepHandler add = new TrivialAddHandler(SSLContext.class, attributes, SSL_CONTEXT_RUNTIME_CAPABILITY) {
+ @Override
+ protected ValueSupplier getValueSupplier(ServiceBuilder serviceBuilder, OperationContext context, ModelNode model) throws OperationFailedException {
+ final String authenticationContextName = AUTHENTICATION_CONTEXT_ATTRIBUTE.resolveModelAttribute(context, model).asString();
+ String authenticationContextCapability = buildDynamicCapabilityName(AUTHENTICATION_CONTEXT_CAPABILITY, authenticationContextName);
+ ServiceName acServiceName = context.getCapabilityServiceName(authenticationContextCapability, AuthenticationContext.class);
+ Supplier authenticationContextSupplier = serviceBuilder.requires(acServiceName);
+
+ return () -> {
+ try {
+ return new DynamicSSLContext(new DynamicSSLContextImpl(authenticationContextSupplier.get()));
+ } catch (DynamicSSLContextException | GeneralSecurityException e) {
+ throw new RuntimeException(e);
+ }
+ };
+ }
+
+ @Override
+ protected Resource createResource(OperationContext context) {
+ SSLContextResource resource = new SSLContextResource(Resource.Factory.create(), false);
+ context.addResource(PathAddress.EMPTY_ADDRESS, resource);
+ return resource;
+ }
+
+ @Override
+ protected void installedForResource(ServiceController serviceController, Resource resource) {
+ ((SSLContextResource) resource).setSSLContextServiceController(serviceController);
+ }
+ };
+
+ return createSSLContextDefinition(ElytronDescriptionConstants.DYNAMIC_CLIENT_SSL_CONTEXT, false, add, attributes, false, Stability.COMMUNITY);
+ }
+
private static Provider[] filterProviders(Provider[] all, String provider) {
if (provider == null || all == null) return all;
List list = new ArrayList<>();
diff --git a/elytron/src/main/java/org/wildfly/extension/elytron/TlsParser.java b/elytron/src/main/java/org/wildfly/extension/elytron/TlsParser.java
index 0aacf83fa57..cd8592d815e 100644
--- a/elytron/src/main/java/org/wildfly/extension/elytron/TlsParser.java
+++ b/elytron/src/main/java/org/wildfly/extension/elytron/TlsParser.java
@@ -13,6 +13,8 @@
import static org.wildfly.extension.elytron.ElytronDescriptionConstants.CERTIFICATE_AUTHORITY_ACCOUNTS;
import static org.wildfly.extension.elytron.ElytronDescriptionConstants.CLIENT_SSL_CONTEXT;
import static org.wildfly.extension.elytron.ElytronDescriptionConstants.CLIENT_SSL_CONTEXTS;
+import static org.wildfly.extension.elytron.ElytronDescriptionConstants.DYNAMIC_CLIENT_SSL_CONTEXT;
+import static org.wildfly.extension.elytron.ElytronDescriptionConstants.DYNAMIC_CLIENT_SSL_CONTEXTS;
import static org.wildfly.extension.elytron.ElytronDescriptionConstants.FILTERING_KEY_STORE;
import static org.wildfly.extension.elytron.ElytronDescriptionConstants.HOST;
import static org.wildfly.extension.elytron.ElytronDescriptionConstants.SNI_MAPPING;
@@ -195,6 +197,10 @@ class TlsParser {
.addAttribute(SSLDefinitions.PROVIDERS)
.addAttribute(SSLDefinitions.PROVIDER_NAME);
+ private PersistentResourceXMLBuilder dynamicClientSslContextParser = PersistentResourceXMLDescription.builder(PathElement.pathElement(DYNAMIC_CLIENT_SSL_CONTEXT))
+ .setXmlWrapperElement(DYNAMIC_CLIENT_SSL_CONTEXTS)
+ .addAttribute(SSLDefinitions.AUTHENTICATION_CONTEXT_ATTRIBUTE);
+
private PersistentResourceXMLBuilder certificateAuthorityParser = PersistentResourceXMLDescription.builder(PathElement.pathElement(CERTIFICATE_AUTHORITY))
.setXmlWrapperElement(CERTIFICATE_AUTHORITIES)
.addAttribute(CertificateAuthorityDefinition.URL)
@@ -349,4 +355,20 @@ public void marshallSingleElement(AttributeDefinition attribute, ModelNode mappi
.addChild(certificateAuthorityAccountParser)
.addChild(serverSslSniContextParser)
.build();
+
+ final PersistentResourceXMLDescription tlsParserCommunity_18_0 = decorator(TLS)
+ .addChild(decorator(KEY_STORES)
+ .addChild(keyStoreParser)
+ .addChild(ldapKeyStoreParser)
+ .addChild(filteringKeyStoreParser)
+ )
+ .addChild(keyManagerParser_12_0)
+ .addChild(trustManagerParser_14_0)
+ .addChild(serverSslContextParser_9_0)
+ .addChild(clientSslContextParser_9_0)
+ .addChild(certificateAuthorityParser)
+ .addChild(certificateAuthorityAccountParser)
+ .addChild(serverSslSniContextParser)
+ .addChild(dynamicClientSslContextParser) // new
+ .build();
}
diff --git a/elytron/src/main/java/org/wildfly/extension/elytron/TrivialResourceDefinition.java b/elytron/src/main/java/org/wildfly/extension/elytron/TrivialResourceDefinition.java
index 533ad118376..a3599d89867 100644
--- a/elytron/src/main/java/org/wildfly/extension/elytron/TrivialResourceDefinition.java
+++ b/elytron/src/main/java/org/wildfly/extension/elytron/TrivialResourceDefinition.java
@@ -23,6 +23,7 @@
import org.jboss.as.controller.descriptions.ResourceDescriptionResolver;
import org.jboss.as.controller.registry.ManagementResourceRegistration;
import org.jboss.as.controller.registry.OperationEntry;
+import org.jboss.as.version.Stability;
/**
* A trivial {@link ResourceDefinition}
@@ -36,9 +37,11 @@ final class TrivialResourceDefinition extends SimpleResourceDefinition {
private final Map readOnlyAttributes;
private final List children;
+ private final Stability stability;
+
private TrivialResourceDefinition(String pathKey, ResourceDescriptionResolver resourceDescriptionResolver, AbstractAddStepHandler add, AbstractRemoveStepHandler remove, AttributeDefinition[] attributes,
Map readOnlyAttributes, Map operations, List children,
- RuntimeCapability>[] runtimeCapabilities) {
+ RuntimeCapability>[] runtimeCapabilities, Stability stability) {
super(new Parameters(PathElement.pathElement(pathKey),
resourceDescriptionResolver)
.setAddHandler(add)
@@ -51,14 +54,15 @@ private TrivialResourceDefinition(String pathKey, ResourceDescriptionResolver re
this.readOnlyAttributes = readOnlyAttributes;
this.operations = operations;
this.children = children;
+ this.stability = stability;
}
TrivialResourceDefinition(String pathKey, ResourceDescriptionResolver resourceDescriptionResolver, AbstractAddStepHandler add, AttributeDefinition[] attributes, RuntimeCapability> ... runtimeCapabilities) {
- this(pathKey, resourceDescriptionResolver, add, new TrivialCapabilityServiceRemoveHandler(add, runtimeCapabilities), attributes, null, null, null, runtimeCapabilities);
+ this(pathKey, resourceDescriptionResolver, add, new TrivialCapabilityServiceRemoveHandler(add, runtimeCapabilities), attributes, null, null, null, runtimeCapabilities, Stability.DEFAULT);
}
TrivialResourceDefinition(String pathKey, AbstractAddStepHandler add, AttributeDefinition[] attributes, RuntimeCapability> ... runtimeCapabilities) {
- this(pathKey, ElytronExtension.getResourceDescriptionResolver(pathKey), add, new TrivialCapabilityServiceRemoveHandler(add, runtimeCapabilities), attributes, null, null, null, runtimeCapabilities);
+ this(pathKey, ElytronExtension.getResourceDescriptionResolver(pathKey), add, new TrivialCapabilityServiceRemoveHandler(add, runtimeCapabilities), attributes, null, null, null, runtimeCapabilities, Stability.DEFAULT);
}
@Override
@@ -97,6 +101,11 @@ public void registerChildren(ManagementResourceRegistration resourceRegistration
}
}
+ @Override
+ public Stability getStability() {
+ return this.stability;
+ }
+
public AttributeDefinition[] getAttributes() {
return attributes;
}
@@ -116,6 +125,7 @@ static class Builder {
private Map operations;
private RuntimeCapability>[] runtimeCapabilities;
private List children;
+ private Stability stability = Stability.DEFAULT;
Builder() {}
@@ -173,6 +183,11 @@ Builder setRuntimeCapabilities(RuntimeCapability> ... runtimeCapabilities) {
return this;
}
+ Builder setStability(Stability stability) {
+ this.stability = stability;
+ return this;
+ }
+
Builder addChild(ResourceDefinition child) {
if (children == null) {
children = new ArrayList<>();
@@ -187,7 +202,7 @@ ResourceDefinition build() {
ResourceDescriptionResolver resourceDescriptionResolver = this.resourceDescriptionResolver != null ? this.resourceDescriptionResolver : ElytronExtension.getResourceDescriptionResolver(pathKey);
return new TrivialResourceDefinition(pathKey, resourceDescriptionResolver, addHandler,
removeHandler != null ? removeHandler : new TrivialCapabilityServiceRemoveHandler(addHandler, runtimeCapabilities),
- attributes, readOnlyAttributes, operations, children, runtimeCapabilities);
+ attributes, readOnlyAttributes, operations, children, runtimeCapabilities, stability);
}
}
diff --git a/elytron/src/main/resources/org/wildfly/extension/elytron/LocalDescriptions.properties b/elytron/src/main/resources/org/wildfly/extension/elytron/LocalDescriptions.properties
index 64fa0e45c51..36d6297c7e5 100644
--- a/elytron/src/main/resources/org/wildfly/extension/elytron/LocalDescriptions.properties
+++ b/elytron/src/main/resources/org/wildfly/extension/elytron/LocalDescriptions.properties
@@ -1439,10 +1439,17 @@ elytron.client-ssl-context.ssl-session.peer-certificates.version=The certificate
# Operations
elytron.client-ssl-context.ssl-session.invalidate=Invalidate the SSLSession (Note: This does not terminate current connections, only prevents future connections from joining or resuming this session).
+elytron.dynamic-client-ssl-context=A Dynamic SSL context for use on the client side of a connection. It makes it possible to choose the SSL context to use based on the peer's host and port information
+# operations
+elytron.dynamic-client-ssl-context.add=Add the Dynamic Client SSL context definition.
+elytron.dynamic-client-ssl-context.remove=Remove the Dynamic Client SSL context definition.
+#Attributes
+elytron.dynamic-client-ssl-context.authentication-context=The authentication context that will be queried for SSL context based on peer information.
+
elytron.server-ssl-context=An SSLContext for use on the server side of a connection.
# operations
-elytron.server-ssl-context.add=Add the SSLContext definition.
-elytron.server-ssl-context.remove=Remove the SSLContext definition.
+elytron.server-ssl-context.add=Add the SSL context definition.
+elytron.server-ssl-context.remove=Remove the SSL context definition.
#Attributes
elytron.server-ssl-context.security-domain=The security domain to use for authentication during SSL session establishment.
elytron.server-ssl-context.cipher-suite-filter=The filter to apply to specify the enabled cipher suites for TLSv1.2 and below.
diff --git a/elytron/src/main/resources/schema/wildfly-elytron_community_18_0.xsd b/elytron/src/main/resources/schema/wildfly-elytron_community_18_0.xsd
index 6c4f3714355..3b7daeb7ff9 100644
--- a/elytron/src/main/resources/schema/wildfly-elytron_community_18_0.xsd
+++ b/elytron/src/main/resources/schema/wildfly-elytron_community_18_0.xsd
@@ -4779,6 +4779,7 @@
+
@@ -6402,4 +6403,39 @@
+
+
+
+
+
+ Container for client dynamic SSL context definitions.
+
+
+
+
+
+
+
+
+
+
+ Definitions of a single client side dynamic SSL context. This context chooses SSL context based on peer's host and port information.
+
+
+
+
+
+ The unique name of this client side dynamic SSL context.
+
+
+
+
+
+
+ The authentication context that will be used to query for rules when deciding which ssl context to use when connecting to a peer.
+
+
+
+
+
diff --git a/elytron/src/test/java/org/wildfly/extension/elytron/DefaultStabilityTestCase.java b/elytron/src/test/java/org/wildfly/extension/elytron/DefaultStabilityTestCase.java
new file mode 100644
index 00000000000..fcad0e6e8a1
--- /dev/null
+++ b/elytron/src/test/java/org/wildfly/extension/elytron/DefaultStabilityTestCase.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright The WildFly Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+package org.wildfly.extension.elytron;
+
+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.as.version.Stability;
+import org.jboss.dmr.ModelNode;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.FAILED;
+import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OUTCOME;
+
+public class DefaultStabilityTestCase extends AbstractSubsystemTest {
+
+ private static final String DYNAMIC_SSL_CLIENT_CONTEXT_NAME = "dcsc";
+ private static final String SUBSYSTEM = "subsystem";
+ private static final String ELYTRON = "elytron";
+
+ public DefaultStabilityTestCase() {
+ super(ElytronExtension.SUBSYSTEM_NAME, new ElytronExtension(), Stability.DEFAULT);
+ }
+
+ private static KernelServices services = null;
+
+ @Before
+ public void initServices() throws Exception {
+ TestEnvironment testEnvironment = new TestEnvironment(Stability.DEFAULT);
+ services = super.createKernelServicesBuilder(testEnvironment).setSubsystemXmlResource("authentication-client.xml").build();
+ if (!services.isSuccessfulBoot()) {
+ if (services.getBootError() != null) {
+ Assert.fail(services.getBootError().toString());
+ }
+ Assert.fail("Failed to boot, no reason provided");
+ }
+ }
+
+ @Test
+ public void testAddDynamicClientSSLContextFailsInDefaultStability() {
+ ModelNode operation = new ModelNode();
+ operation.get(ClientConstants.OP_ADDR)
+ .add(SUBSYSTEM, ELYTRON).add(ElytronDescriptionConstants.DYNAMIC_CLIENT_SSL_CONTEXT, DYNAMIC_SSL_CLIENT_CONTEXT_NAME);
+ operation.get(ClientConstants.OP).set(ClientConstants.ADD);
+ operation.get(ElytronDescriptionConstants.AUTHENTICATION_CONTEXT).set("ac");
+ ModelNode response = services.executeOperation(operation);
+
+ if (!response.get(OUTCOME).asString().equals(FAILED)) {
+ Assert.fail(response.toJSONString(false));
+ }
+
+ if (!response.get("failure-description").asString().contains("No resource definition is registered for address")) {
+ Assert.fail(response.toJSONString(false));
+ }
+ }
+}
\ No newline at end of file
diff --git a/elytron/src/test/java/org/wildfly/extension/elytron/DynamicSSLContextTestCase.java b/elytron/src/test/java/org/wildfly/extension/elytron/DynamicSSLContextTestCase.java
new file mode 100644
index 00000000000..ce6c6f380c8
--- /dev/null
+++ b/elytron/src/test/java/org/wildfly/extension/elytron/DynamicSSLContextTestCase.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright The WildFly Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+package org.wildfly.extension.elytron;
+
+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.as.version.Stability;
+import org.jboss.dmr.ModelNode;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+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.OPERATION_REQUIRES_RELOAD;
+import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OUTCOME;
+import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.RESPONSE_HEADERS;
+import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SUCCESS;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class DynamicSSLContextTestCase extends AbstractSubsystemTest {
+
+ private static final String DYNAMIC_SSL_CLIENT_CONTEXT_NAME = "dcsc";
+ private static final String SUBSYSTEM = "subsystem";
+ private static final String ELYTRON = "elytron";
+
+ public DynamicSSLContextTestCase() {
+ super(ElytronExtension.SUBSYSTEM_NAME, new ElytronExtension(), Stability.COMMUNITY);
+ }
+
+ private static KernelServices services = null;
+
+ @Before
+ public void initServices() throws Exception {
+ TestEnvironment testEnvironment = new TestEnvironment(Stability.COMMUNITY);
+ services = super.createKernelServicesBuilder(testEnvironment).setSubsystemXmlResource("authentication-client.xml").build();
+ if (!services.isSuccessfulBoot()) {
+ if (services.getBootError() != null) {
+ Assert.fail(services.getBootError().toString());
+ }
+ Assert.fail("Failed to boot, no reason provided");
+ }
+ }
+
+ @Test
+ public void testAddDynamicClientSSLContext() {
+ addDynamicSSLClientContext();
+ readDynamicSSLCientContextResource();
+ }
+
+ @Test
+ public void testRemoveDynamicClientSSLContext() {
+ addDynamicSSLClientContext();
+ ModelNode operation = new ModelNode();
+ operation.get(ClientConstants.OP_ADDR)
+ .add(SUBSYSTEM, ELYTRON).add(ElytronDescriptionConstants.DYNAMIC_CLIENT_SSL_CONTEXT, DYNAMIC_SSL_CLIENT_CONTEXT_NAME);
+ operation.get(ClientConstants.OP).set(ClientConstants.REMOVE_OPERATION);
+ assertSuccess(services.executeOperation(operation));
+
+ operation = new ModelNode();
+ operation.get(ClientConstants.OP_ADDR)
+ .add(SUBSYSTEM, ELYTRON).add(ElytronDescriptionConstants.DYNAMIC_CLIENT_SSL_CONTEXT, DYNAMIC_SSL_CLIENT_CONTEXT_NAME);
+ operation.get(ClientConstants.OP).set(ClientConstants.READ_RESOURCE_OPERATION);
+ assertFailed(services.executeOperation(operation));
+ }
+
+ @Test
+ public void testUpdateDynamicClientSSLContext() {
+ addDynamicSSLClientContext();
+ ModelNode operation = new ModelNode();
+ operation.get(ClientConstants.OP_ADDR)
+ .add(SUBSYSTEM, ELYTRON).add(ElytronDescriptionConstants.DYNAMIC_CLIENT_SSL_CONTEXT, DYNAMIC_SSL_CLIENT_CONTEXT_NAME);
+ operation.get(ClientConstants.OP).set(ClientConstants.WRITE_ATTRIBUTE_OPERATION);
+ operation.get(ClientConstants.NAME).set(ElytronDescriptionConstants.AUTHENTICATION_CONTEXT);
+ operation.get(ClientConstants.VALUE).set("base");
+ assertSuccess(services.executeOperation(operation));
+
+ operation = new ModelNode();
+ operation.get(ClientConstants.OP_ADDR)
+ .add(SUBSYSTEM, ELYTRON).add(ElytronDescriptionConstants.DYNAMIC_CLIENT_SSL_CONTEXT, DYNAMIC_SSL_CLIENT_CONTEXT_NAME);
+ operation.get(ClientConstants.OP).set(ClientConstants.READ_RESOURCE_OPERATION);
+ ModelNode result = assertSuccess(services.executeOperation(operation)).get(ClientConstants.RESULT);
+ assertEquals("base", result.get(ElytronDescriptionConstants.AUTHENTICATION_CONTEXT).asString());
+ }
+
+ @Test
+ public void testAddDynamicClientSSLContextWithoutACThrowsEx() {
+ ModelNode operation = new ModelNode();
+ operation.get(ClientConstants.OP_ADDR)
+ .add(SUBSYSTEM, ELYTRON).add(ElytronDescriptionConstants.DYNAMIC_CLIENT_SSL_CONTEXT, DYNAMIC_SSL_CLIENT_CONTEXT_NAME);
+ operation.get(ClientConstants.OP).set(ClientConstants.ADD);
+
+ ModelNode result = services.executeOperation(operation);
+ assertFailed(result);
+ String failureDescription = result.get(FAILURE_DESCRIPTION).asString();
+ assertTrue(failureDescription.contains("'authentication-context' may not be null"));
+ }
+
+ @Test
+ public void testAddDynamicClientSSLContextAsDefaultSSLContext() {
+ addDynamicSSLClientContext();
+ ModelNode operation = new ModelNode();
+ operation.get(ClientConstants.OP_ADDR).add(SUBSYSTEM, ELYTRON);
+ operation.get(ClientConstants.OP).set(ClientConstants.WRITE_ATTRIBUTE_OPERATION);
+ operation.get(ClientConstants.NAME).set(ElytronDescriptionConstants.DEFAULT_SSL_CONTEXT);
+ operation.get(ClientConstants.VALUE).set(DYNAMIC_SSL_CLIENT_CONTEXT_NAME);
+ ModelNode result = assertSuccess(services.executeOperation(operation));
+ result.has(RESPONSE_HEADERS, OPERATION_REQUIRES_RELOAD);
+ operation = new ModelNode();
+ operation.get(ClientConstants.OP_ADDR).add(SUBSYSTEM, ELYTRON);
+ operation.get(ClientConstants.OP).set(ClientConstants.READ_RESOURCE_OPERATION);
+ result = assertSuccess(services.executeOperation(operation)).get(ClientConstants.RESULT);
+ assertEquals(DYNAMIC_SSL_CLIENT_CONTEXT_NAME, result.get(ElytronDescriptionConstants.DEFAULT_SSL_CONTEXT).asString());
+ }
+
+ 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 addDynamicSSLClientContext() {
+ ModelNode operation = new ModelNode();
+ operation.get(ClientConstants.OP_ADDR)
+ .add(SUBSYSTEM, ELYTRON).add(ElytronDescriptionConstants.DYNAMIC_CLIENT_SSL_CONTEXT, DYNAMIC_SSL_CLIENT_CONTEXT_NAME);
+ operation.get(ClientConstants.OP).set(ClientConstants.ADD);
+ operation.get(ElytronDescriptionConstants.AUTHENTICATION_CONTEXT).set("ac");
+ assertSuccess(services.executeOperation(operation));
+ }
+
+ private void readDynamicSSLCientContextResource() {
+ ModelNode operation = new ModelNode();
+ operation.get(ClientConstants.OP_ADDR)
+ .add(SUBSYSTEM, ELYTRON).add(ElytronDescriptionConstants.DYNAMIC_CLIENT_SSL_CONTEXT, DYNAMIC_SSL_CLIENT_CONTEXT_NAME);
+ operation.get(ClientConstants.OP).set(ClientConstants.READ_RESOURCE_OPERATION);
+ ModelNode result = assertSuccess(services.executeOperation(operation)).get(ClientConstants.RESULT);
+ assertEquals("ac", result.get(ElytronDescriptionConstants.AUTHENTICATION_CONTEXT).asString());
+ }
+}
diff --git a/elytron/src/test/java/org/wildfly/extension/elytron/TestEnvironment.java b/elytron/src/test/java/org/wildfly/extension/elytron/TestEnvironment.java
index a9f34fa6b2e..ccd03216dc5 100644
--- a/elytron/src/test/java/org/wildfly/extension/elytron/TestEnvironment.java
+++ b/elytron/src/test/java/org/wildfly/extension/elytron/TestEnvironment.java
@@ -28,6 +28,7 @@
import org.jboss.as.subsystem.test.AdditionalInitialization;
import org.jboss.as.subsystem.test.ControllerInitializer;
import org.jboss.as.subsystem.test.KernelServices;
+import org.jboss.as.version.Stability;
import org.jboss.msc.service.ServiceController;
import org.jboss.msc.service.ServiceName;
import org.wildfly.security.x500.cert.BasicConstraintsExtension;
@@ -44,6 +45,8 @@ class TestEnvironment extends AdditionalInitialization {
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 Stability stability;
+
private static KeyStore loadKeyStore() throws Exception{
KeyStore ks = KeyStore.getInstance("JKS");
ks.load(null, null);
@@ -117,11 +120,25 @@ public static void setUpKeyStores() throws Exception {
private final RunningMode runningMode;
TestEnvironment() {
- this(RunningMode.NORMAL);
+ this(RunningMode.NORMAL, Stability.DEFAULT);
}
TestEnvironment(RunningMode runningMode) {
+ this(runningMode, Stability.DEFAULT);
+ }
+
+ TestEnvironment(Stability stability) {
+ this(RunningMode.NORMAL, stability);
+ }
+
+ TestEnvironment(RunningMode runningMode, Stability stability) {
this.runningMode = runningMode;
+ this.stability = stability;
+ }
+
+ @Override
+ public Stability getStability() {
+ return stability;
}
@Override
diff --git a/elytron/src/test/resources/org/wildfly/extension/elytron/authentication-client.xml b/elytron/src/test/resources/org/wildfly/extension/elytron/authentication-client.xml
index 12308781830..54cfe836b00 100644
--- a/elytron/src/test/resources/org/wildfly/extension/elytron/authentication-client.xml
+++ b/elytron/src/test/resources/org/wildfly/extension/elytron/authentication-client.xml
@@ -44,6 +44,10 @@
+
+
+
+
@@ -53,4 +57,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pom.xml b/pom.xml
index 8d35344f81c..93bf2dcd520 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1791,6 +1791,11 @@
wildfly-elytron-digest
${version.org.wildfly.security.elytron}
+
+ org.wildfly.security
+ wildfly-elytron-dynamic-ssl
+ ${version.org.wildfly.security.elytron}
+
org.wildfly.security
wildfly-elytron-encryption