CVE-2025-23368
Description
A flaw was found in Wildfly Elytron integration. The component does not implement sufficient measures to prevent multiple failed authentication attempts within a short time frame, making it more susceptible to brute force attacks via CLI.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Wildfly Elytron integration lacks rate limiting on authentication attempts, enabling brute force attacks via CLI.
Vulnerability
Description
The Wildfly Elytron integration contains a flaw where it does not implement sufficient measures to prevent multiple failed authentication attempts within a short time frame. This absence of rate limiting makes the component susceptible to brute force attacks via the command-line interface (CLI) [1][2][3].
Exploitation
An attacker with network access to the Wildfly management CLI can repeatedly attempt authentication without being throttled. No prior authentication or special privileges are required beyond network connectivity to the management endpoint. The lack of account lockout or progressive delays allows an attacker to systematically guess credentials [3].
Impact
Successful brute force exploitation could lead to unauthorized access to the Wildfly management interface. An attacker who gains valid credentials can then perform administrative actions on the application server, potentially compromising the entire system [3].
Mitigation
Red Hat has released security updates to address this vulnerability. Patches are available via RHSA-2026:18054, RHSA-2026:18055, and RHSA-2026:18059 for affected Red Hat Enterprise Linux and JBoss Enterprise Application Platform versions. Users are advised to apply the updates promptly [1][2][4].
AI Insight generated on May 20, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
org.wildfly.core:wildfly-elytron-integrationMaven | >= 32.0.0.Beta1, < 32.0.0.Beta3 | 32.0.0.Beta3 |
org.wildfly.core:wildfly-elytron-integrationMaven | < 31.0.3.Final | 31.0.3.Final |
Affected products
1- Range: 1.0.0.Alpha1, 1.0.0.Alpha10, 1.0.0.Alpha11, …
Patches
211e873031c52Merge pull request #6635 from darranl/WFCORE-7192/combined
12 files changed · +311 −100
elytron/src/main/java/org/wildfly/extension/elytron/CachingRealmDefinition.java+46 −23 modified@@ -7,14 +7,18 @@ import static org.wildfly.extension.elytron.Capabilities.SECURITY_REALM_CAPABILITY; import static org.wildfly.extension.elytron.Capabilities.SECURITY_REALM_RUNTIME_CAPABILITY; import static org.wildfly.extension.elytron.ElytronDefinition.commonDependencies; -import static org.wildfly.extension.elytron.ElytronExtension.getRequiredService; +import static org.wildfly.extension.elytron.RealmDefinitions.createBruteForceRealmTransformer; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Consumer; +import java.util.function.Function; import org.jboss.as.controller.AbstractAddStepHandler; import org.jboss.as.controller.AttributeDefinition; import org.jboss.as.controller.OperationContext; import org.jboss.as.controller.OperationFailedException; import org.jboss.as.controller.OperationStepHandler; -import org.jboss.as.controller.PathAddress; import org.jboss.as.controller.PathElement; import org.jboss.as.controller.ResourceDefinition; import org.jboss.as.controller.RunningMode; @@ -32,8 +36,8 @@ import org.jboss.msc.service.ServiceBuilder; import org.jboss.msc.service.ServiceController; import org.jboss.msc.service.ServiceName; -import org.jboss.msc.service.ServiceRegistry; import org.jboss.msc.service.ServiceTarget; +import org.jboss.msc.service.StartException; import org.jboss.msc.value.InjectedValue; import org.wildfly.extension.elytron._private.ElytronSubsystemMessages; import org.wildfly.security.auth.realm.CacheableSecurityRealm; @@ -73,6 +77,10 @@ class CachingRealmDefinition extends SimpleResourceDefinition { static final AttributeDefinition[] ATTRIBUTES = new AttributeDefinition[] {REALM_NAME, MAXIMUM_ENTRIES, MAXIMUM_AGE}; + // Callers are expected to just use a single method get / put / remove not multiple calls so we don't + // need complex locking beyond the Map itself.. + private static final Map<String, CachingSecurityRealm> REALMS = new ConcurrentHashMap<>(); + private static final AbstractAddStepHandler ADD = new RealmAddHandler(); private static final OperationStepHandler REMOVE = new TrivialCapabilityServiceRemoveHandler(ADD, SECURITY_REALM_RUNTIME_CAPABILITY); @@ -114,36 +122,55 @@ protected void performRuntime(OperationContext context, ModelNode operation, Mod int maxEntries = MAXIMUM_ENTRIES.resolveModelAttribute(context, model).asInt(); long maxAge = MAXIMUM_AGE.resolveModelAttribute(context, model).asInt(); InjectedValue<SecurityRealm> cacheableRealmValue = new InjectedValue<>(); - ServiceBuilder<SecurityRealm> serviceBuilder = serviceTarget.addService(realmName, createService(cacheableRealm, maxEntries, maxAge, cacheableRealmValue)); + + ServiceBuilder<?> serviceBuilder = serviceTarget.addService(); + Consumer<SecurityRealm> valueConsumer = serviceBuilder.provides(realmName); + + final Function<SecurityRealm, SecurityRealm> realmTransformer = + createBruteForceRealmTransformer(context.getCurrentAddressValue(), SecurityRealm.class, serviceBuilder); + + serviceBuilder.setInstance(createService(context.getCurrentAddressValue(), cacheableRealm, maxEntries, maxAge, cacheableRealmValue, realmTransformer, valueConsumer)); addRealmDependency(context, serviceBuilder, cacheableRealm, cacheableRealmValue); commonDependencies(serviceBuilder).setInitialMode(context.getRunningMode() == RunningMode.ADMIN_ONLY ? ServiceController.Mode.LAZY : ServiceController.Mode.ACTIVE).install(); } - private TrivialService<SecurityRealm> createService(String realmName, int maxEntries, long maxAge, InjectedValue<SecurityRealm> injector) { - return new TrivialService<>((TrivialService.ValueSupplier<SecurityRealm>) () -> { - SecurityRealm securityRealm = injector.getValue(); + private TrivialService<SecurityRealm> createService(String ourRealmName, String wrappedRealmName, int maxEntries, long maxAge, + InjectedValue<SecurityRealm> injector, Function<SecurityRealm, SecurityRealm> realmTransformer, Consumer<SecurityRealm> valueConsumer) { + return new TrivialService<>(new TrivialService.ValueSupplier<SecurityRealm>() { + + @Override + public SecurityRealm get() throws StartException { + SecurityRealm securityRealm = injector.getValue(); - if (securityRealm instanceof CacheableSecurityRealm) { - RealmIdentityCache cache = createRealmIdentityCache(maxEntries, maxAge); - CacheableSecurityRealm cacheableRealm = CacheableSecurityRealm.class.cast(securityRealm); + if (securityRealm instanceof CacheableSecurityRealm) { + RealmIdentityCache cache = createRealmIdentityCache(maxEntries, maxAge); + CacheableSecurityRealm cacheableRealm = CacheableSecurityRealm.class.cast(securityRealm); - if (securityRealm instanceof ModifiableSecurityRealm) { - return new CachingModifiableSecurityRealm(cacheableRealm, cache); + CachingSecurityRealm cachingRealm = securityRealm instanceof ModifiableSecurityRealm ? + new CachingModifiableSecurityRealm(cacheableRealm, cache) : new CachingSecurityRealm(cacheableRealm, cache); + + REALMS.put(ourRealmName, cachingRealm); + + return realmTransformer.apply(cachingRealm); } - return new CachingSecurityRealm(cacheableRealm, cache); + throw ElytronSubsystemMessages.ROOT_LOGGER.realmDoesNotSupportCache(wrappedRealmName); } - throw ElytronSubsystemMessages.ROOT_LOGGER.realmDoesNotSupportCache(realmName); - }); + @Override + public void dispose() { + REALMS.remove(ourRealmName); + } + + }, valueConsumer); } private LRURealmIdentityCache createRealmIdentityCache(int maxEntries, long maxAge) { return new LRURealmIdentityCache(maxEntries, maxAge); } - private void addRealmDependency(OperationContext context, ServiceBuilder<SecurityRealm> serviceBuilder, String realmName, Injector<SecurityRealm> securityRealmInjector) { + private void addRealmDependency(OperationContext context, ServiceBuilder<?> serviceBuilder, String realmName, Injector<SecurityRealm> securityRealmInjector) { String runtimeCapability = RuntimeCapability.buildDynamicCapabilityName(SECURITY_REALM_CAPABILITY, realmName); ServiceName realmServiceName = context.getCapabilityServiceName(runtimeCapability, SecurityRealm.class); REALM_SERVICE_UTIL.addInjection(serviceBuilder, securityRealmInjector, realmServiceName); @@ -165,15 +192,11 @@ private ClearCacheHandler() { @Override protected void executeRuntimeStep(final OperationContext context, final ModelNode operation) throws OperationFailedException { - ServiceRegistry serviceRegistry = context.getServiceRegistry(true); - PathAddress currentAddress = context.getCurrentAddress(); - RuntimeCapability<Void> runtimeCapability = SECURITY_REALM_RUNTIME_CAPABILITY.fromBaseCapability(currentAddress.getLastElement().getValue()); - ServiceName realmName = runtimeCapability.getCapabilityServiceName(); - ServiceController<SecurityRealm> serviceController = getRequiredService(serviceRegistry, realmName, SecurityRealm.class); - if(serviceController.getState() != ServiceController.State.UP) { + CachingSecurityRealm securityRealm = REALMS.get(context.getCurrentAddressValue()); + if (securityRealm == null) { throw ElytronSubsystemMessages.ROOT_LOGGER.cachedRealmServiceNotAvailable(); } - CachingSecurityRealm securityRealm = CachingSecurityRealm.class.cast(serviceController.getValue()); + securityRealm.removeAllFromCache(); } }
elytron/src/main/java/org/wildfly/extension/elytron/CustomComponentDefinition.java+26 −9 modified@@ -16,6 +16,7 @@ import java.security.PrivilegedExceptionAction; import java.util.Map; import java.util.Set; +import java.util.function.BiFunction; import java.util.function.Function; import org.jboss.as.controller.AbstractAddStepHandler; @@ -58,14 +59,14 @@ class CustomComponentDefinition<C, T> extends SimpleResourceDefinition { static final AttributeDefinition[] ATTRIBUTES = {MODULE, CLASS_NAME, CONFIGURATION}; - CustomComponentDefinition(Class<C> serviceType, Function<C, T> wrapper, String pathKey, @SuppressWarnings("rawtypes") RuntimeCapability ... runtimeCapabilities) { + CustomComponentDefinition(Class<C> serviceType, CustomComponentTransformer<C, T> transformer, String pathKey, @SuppressWarnings("rawtypes") RuntimeCapability ... runtimeCapabilities) { super(addAddRemoveHandlers(new Parameters(PathElement.pathElement(pathKey), ElytronExtension.getResourceDescriptionResolver(pathKey)) .setAddRestartLevel(OperationEntry.Flag.RESTART_RESOURCE_SERVICES) .setRemoveRestartLevel(OperationEntry.Flag.RESTART_RESOURCE_SERVICES) - .setCapabilities(runtimeCapabilities), serviceType, wrapper, runtimeCapabilities)); + .setCapabilities(runtimeCapabilities), serviceType, transformer, runtimeCapabilities)); } - private static <C, T> Parameters addAddRemoveHandlers(Parameters parameters, Class<C> serviceType, Function<C, T> wrapper, RuntimeCapability<?> ... runtimeCapabilities) { + private static <C, T> Parameters addAddRemoveHandlers(Parameters parameters, Class<C> serviceType, CustomComponentTransformer<C, T> wrapper, RuntimeCapability<?> ... runtimeCapabilities) { AbstractAddStepHandler add = new ComponentAddHandler<>(serviceType, wrapper, runtimeCapabilities); OperationStepHandler remove = new TrivialCapabilityServiceRemoveHandler(add, runtimeCapabilities); @@ -86,13 +87,13 @@ private static class ComponentAddHandler<C, T> extends BaseAddHandler { private final RuntimeCapability<?>[] runtimeCapabilities; private final Class<C> serviceType; - private final Function<C, T> wrapper; + private final CustomComponentTransformer<C, T> transformer; - private ComponentAddHandler(Class<C> serviceType, Function<C, T> wrapper, RuntimeCapability<?> ... runtimeCapabilities) { + private ComponentAddHandler(Class<C> serviceType, CustomComponentTransformer<C, T> transformer, RuntimeCapability<?> ... runtimeCapabilities) { super(Set.of(runtimeCapabilities)); this.runtimeCapabilities = runtimeCapabilities; this.serviceType = serviceType; - this.wrapper = wrapper; + this.transformer = transformer; } @Override @@ -115,8 +116,11 @@ protected void performRuntime(OperationContext context, ModelNode operation, Mod serviceBuilder.addAliases(toServiceName(runtimeCapabilities[i], address)); } + String name = context.getCurrentAddressValue(); + Object wrapperContext = transformer.prepareTransformer(name, serviceBuilder); + commonRequirements(serviceBuilder) - .setInstance(new TrivialService<>(() -> createValue(module, className, configurationMap))) + .setInstance(new TrivialService<>(() -> createValue(wrapperContext, module, className, configurationMap))) .setInitialMode(Mode.ACTIVE) .install(); } @@ -125,7 +129,7 @@ private ServiceName toServiceName(RuntimeCapability<?> runtimeCapability, String return runtimeCapability.fromBaseCapability(addressValue).getCapabilityServiceName(); } - private T createValue(String module, String className, Map<String, String> configuration) throws StartException { + private T createValue(Object transformerContext, String module, String className, Map<String, String> configuration) throws StartException { final ClassLoader classLoader; try { classLoader = doPrivileged((PrivilegedExceptionAction<ClassLoader>) () -> resolveClassLoader(module)); @@ -143,7 +147,7 @@ private T createValue(String module, String className, Map<String, String> confi } } - return wrapper.apply(component); + return transformer.apply(transformerContext, component); } catch (PrivilegedActionException e) { throw new StartException(e.getCause()); } catch (Exception e) { @@ -156,4 +160,17 @@ private T createValue(String module, String className, Map<String, String> confi } } + static <A, B> CustomComponentTransformer<A, B> wrapFunction(Function<A, B> function) { + return (s, a ) -> function.apply(a); + } + + @FunctionalInterface + interface CustomComponentTransformer<A, B> extends BiFunction<Object, A, B> { + + default Object prepareTransformer(final String name, final ServiceBuilder<?> serviceBuilder) { + return name; + } + + } + }
elytron/src/main/java/org/wildfly/extension/elytron/DistributedRealmDefinition.java+15 −9 modified@@ -8,7 +8,12 @@ import static org.wildfly.extension.elytron.Capabilities.SECURITY_REALM_CAPABILITY; import static org.wildfly.extension.elytron.Capabilities.SECURITY_REALM_RUNTIME_CAPABILITY; import static org.wildfly.extension.elytron.ElytronDefinition.commonDependencies; +import static org.wildfly.extension.elytron.RealmDefinitions.createBruteForceRealmTransformer; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; +import java.util.function.Function; import org.jboss.as.controller.AbstractAddStepHandler; import org.jboss.as.controller.AttributeDefinition; @@ -32,16 +37,11 @@ import org.jboss.msc.service.ServiceName; import org.jboss.msc.service.ServiceTarget; import org.jboss.msc.value.InjectedValue; - import org.wildfly.security.auth.realm.DistributedSecurityRealm; import org.wildfly.security.auth.server.SecurityDomain; import org.wildfly.security.auth.server.SecurityRealm; import org.wildfly.security.auth.server.event.SecurityRealmUnavailableEvent; -import java.util.ArrayList; -import java.util.List; -import java.util.function.Consumer; - /** * A {@link ResourceDefinition} for a {@link SecurityRealm} for authentication and authorization of identities distributed between multiple realms. * @@ -110,6 +110,12 @@ protected void performRuntime(OperationContext context, ModelNode operation, Mod List<String> distributedRealms = REALMS.unwrap(context, model); + ServiceBuilder<?> serviceBuilder = serviceTarget.addService(); + Consumer<SecurityRealm> valueConsumer = serviceBuilder.provides(realmName); + + final Function<SecurityRealm, SecurityRealm> realmTransformer = + createBruteForceRealmTransformer(context.getCurrentAddressValue(), SecurityRealm.class, serviceBuilder); + TrivialService<SecurityRealm> distributedRealmService = new TrivialService<SecurityRealm>(() -> { SecurityRealm[] realms = new SecurityRealm[distributedRealmValues.size()]; @@ -126,10 +132,10 @@ protected void performRuntime(OperationContext context, ModelNode operation, Mod realms[i] = distributedRealmValues.get(i).getValue(); } - return new DistributedSecurityRealm(ignoreUnavailableRealms, unavailableRealmConsumer, realms); - }); + return realmTransformer.apply(new DistributedSecurityRealm(ignoreUnavailableRealms, unavailableRealmConsumer, realms)); + }, valueConsumer); - ServiceBuilder<SecurityRealm> serviceBuilder = serviceTarget.addService(realmName, distributedRealmService); + serviceBuilder.setInstance(distributedRealmService); for (String distributedRealm : distributedRealms) { InjectedValue<SecurityRealm> authorizationRealmValue = new InjectedValue<SecurityRealm>(); @@ -142,7 +148,7 @@ protected void performRuntime(OperationContext context, ModelNode operation, Mod .install(); } - private void addRealmDependency(OperationContext context, ServiceBuilder<SecurityRealm> serviceBuilder, String realmName, Injector<SecurityRealm> securityRealmInjector) { + private void addRealmDependency(OperationContext context, ServiceBuilder<?> serviceBuilder, String realmName, Injector<SecurityRealm> securityRealmInjector) { String runtimeCapability = RuntimeCapability.buildDynamicCapabilityName(SECURITY_REALM_CAPABILITY, realmName); ServiceName realmServiceName = context.getCapabilityServiceName(runtimeCapability, SecurityRealm.class);
elytron/src/main/java/org/wildfly/extension/elytron/ElytronDefinition.java+18 −14 modified@@ -5,7 +5,6 @@ package org.wildfly.extension.elytron; - import static org.jboss.as.server.security.VirtualDomainUtil.VIRTUAL_SECURITY_DOMAIN_CREATION_SERVICE; import static org.wildfly.extension.elytron.Capabilities.AUTHENTICATION_CONTEXT_CAPABILITY; import static org.wildfly.extension.elytron.Capabilities.ELYTRON_RUNTIME_CAPABILITY; @@ -23,6 +22,7 @@ import static org.wildfly.extension.elytron.Capabilities.SECURITY_FACTORY_CREDENTIAL_RUNTIME_CAPABILITY; import static org.wildfly.extension.elytron.Capabilities.SECURITY_REALM_RUNTIME_CAPABILITY; import static org.wildfly.extension.elytron.Capabilities.SSL_CONTEXT_CAPABILITY; +import static org.wildfly.extension.elytron.CustomComponentDefinition.wrapFunction; import static org.wildfly.extension.elytron.ElytronExtension.isServerOrHostController; import static org.wildfly.extension.elytron.ElytronScheduledExecutorService.installScheduledExecutorService; import static org.wildfly.extension.elytron.ElytronScheduledExecutorService.uninstallScheduledExecutorService; @@ -41,6 +41,7 @@ import javax.net.ssl.SSLContext; +import jakarta.security.auth.message.config.AuthConfigFactory; import org.jboss.as.controller.AbstractBoottimeAddStepHandler; import org.jboss.as.controller.AttributeMarshaller; import org.jboss.as.controller.AttributeParser; @@ -77,6 +78,7 @@ import org.jboss.msc.service.ServiceName; import org.jboss.msc.service.ServiceRegistry; import org.jboss.msc.service.ServiceTarget; +import org.wildfly.extension.elytron.RealmDefinitions.CustomRealmBruteForceTransformer; import org.wildfly.extension.elytron.capabilities.CredentialSecurityFactory; import org.wildfly.extension.elytron.capabilities.PrincipalTransformer; import org.wildfly.extension.elytron.capabilities._private.SecurityEventListener; @@ -96,8 +98,6 @@ import org.wildfly.security.jakarta.authz.AuthorizationRegistration; import org.wildfly.security.manager.action.ReadPropertyAction; -import jakarta.security.auth.message.config.AuthConfigFactory; - /** * Top level {@link ResourceDefinition} for the Elytron subsystem. * @@ -180,7 +180,7 @@ public void registerChildren(ManagementResourceRegistration resourceRegistration // Audit resourceRegistration.registerSubModel(AuditResourceDefinitions.getAggregateSecurityEventListenerDefinition()); - resourceRegistration.registerSubModel(new CustomComponentDefinition<>(Consumer.class, SecurityEventListener::from, + resourceRegistration.registerSubModel(new CustomComponentDefinition<>(Consumer.class, wrapFunction(SecurityEventListener::from), ElytronDescriptionConstants.CUSTOM_SECURITY_EVENT_LISTENER, SECURITY_EVENT_LISTENER_RUNTIME_CAPABILITY)); resourceRegistration.registerSubModel(AuditResourceDefinitions.getFileAuditLogResourceDefinition()); resourceRegistration.registerSubModel(AuditResourceDefinitions.getPeriodicRotatingFileAuditLogResourceDefinition()); @@ -197,9 +197,9 @@ public void registerChildren(ManagementResourceRegistration resourceRegistration // Security Realms resourceRegistration.registerSubModel(new AggregateRealmDefinition()); - resourceRegistration.registerSubModel(new CustomComponentDefinition<>(SecurityRealm.class, Function.identity(), ElytronDescriptionConstants.CUSTOM_REALM, SECURITY_REALM_RUNTIME_CAPABILITY)); + resourceRegistration.registerSubModel(new CustomComponentDefinition<>(SecurityRealm.class, new CustomRealmBruteForceTransformer<>(SecurityRealm.class), ElytronDescriptionConstants.CUSTOM_REALM, SECURITY_REALM_RUNTIME_CAPABILITY)); resourceRegistration.registerSubModel(ModifiableRealmDecorator.wrap(new CustomComponentDefinition<>( - ModifiableSecurityRealm.class, Function.identity(), ElytronDescriptionConstants.CUSTOM_MODIFIABLE_REALM, + ModifiableSecurityRealm.class, new CustomRealmBruteForceTransformer<>(ModifiableSecurityRealm.class), ElytronDescriptionConstants.CUSTOM_MODIFIABLE_REALM, MODIFIABLE_SECURITY_REALM_RUNTIME_CAPABILITY, SECURITY_REALM_RUNTIME_CAPABILITY))); resourceRegistration.registerSubModel(RealmDefinitions.getIdentityRealmDefinition()); resourceRegistration.registerSubModel(new JdbcRealmDefinition()); @@ -214,11 +214,11 @@ public void registerChildren(ManagementResourceRegistration resourceRegistration resourceRegistration.registerSubModel(new JaasRealmDefinition()); // Security Factories - resourceRegistration.registerSubModel(new CustomComponentDefinition<>(SecurityFactory.class, CredentialSecurityFactory::from, ElytronDescriptionConstants.CUSTOM_CREDENTIAL_SECURITY_FACTORY, SECURITY_FACTORY_CREDENTIAL_RUNTIME_CAPABILITY)); + resourceRegistration.registerSubModel(new CustomComponentDefinition<>(SecurityFactory.class, wrapFunction(CredentialSecurityFactory::from), ElytronDescriptionConstants.CUSTOM_CREDENTIAL_SECURITY_FACTORY, SECURITY_FACTORY_CREDENTIAL_RUNTIME_CAPABILITY)); resourceRegistration.registerSubModel(KerberosSecurityFactoryDefinition.getKerberosSecurityFactoryDefinition()); // Permission Mappers - resourceRegistration.registerSubModel(new CustomComponentDefinition<>(PermissionMapper.class, Function.identity(), ElytronDescriptionConstants.CUSTOM_PERMISSION_MAPPER, PERMISSION_MAPPER_RUNTIME_CAPABILITY)); + resourceRegistration.registerSubModel(new CustomComponentDefinition<>(PermissionMapper.class, ElytronDefinition::identity, ElytronDescriptionConstants.CUSTOM_PERMISSION_MAPPER, PERMISSION_MAPPER_RUNTIME_CAPABILITY)); resourceRegistration.registerSubModel(PermissionMapperDefinitions.getLogicalPermissionMapper()); resourceRegistration.registerSubModel(PermissionMapperDefinitions.getSimplePermissionMapper()); resourceRegistration.registerSubModel(PermissionMapperDefinitions.getConstantPermissionMapper()); @@ -230,26 +230,26 @@ public void registerChildren(ManagementResourceRegistration resourceRegistration resourceRegistration.registerSubModel(PrincipalDecoderDefinitions.getAggregatePrincipalDecoderDefinition()); resourceRegistration.registerSubModel(PrincipalDecoderDefinitions.getConcatenatingPrincipalDecoder()); resourceRegistration.registerSubModel(PrincipalDecoderDefinitions.getConstantPrincipalDecoder()); - resourceRegistration.registerSubModel(new CustomComponentDefinition<>(PrincipalDecoder.class, Function.identity(), ElytronDescriptionConstants.CUSTOM_PRINCIPAL_DECODER, PRINCIPAL_DECODER_RUNTIME_CAPABILITY)); + resourceRegistration.registerSubModel(new CustomComponentDefinition<>(PrincipalDecoder.class, ElytronDefinition::identity, ElytronDescriptionConstants.CUSTOM_PRINCIPAL_DECODER, PRINCIPAL_DECODER_RUNTIME_CAPABILITY)); resourceRegistration.registerSubModel(PrincipalDecoderDefinitions.getX500AttributePrincipalDecoder()); // Principal Transformers resourceRegistration.registerSubModel(PrincipalTransformerDefinitions.getAggregatePrincipalTransformerDefinition()); resourceRegistration.registerSubModel(PrincipalTransformerDefinitions.getChainedPrincipalTransformerDefinition()); resourceRegistration.registerSubModel(PrincipalTransformerDefinitions.getConstantPrincipalTransformerDefinition()); - resourceRegistration.registerSubModel(new CustomComponentDefinition<>(Function.class, PrincipalTransformer::from, ElytronDescriptionConstants.CUSTOM_PRINCIPAL_TRANSFORMER, PRINCIPAL_TRANSFORMER_RUNTIME_CAPABILITY)); + resourceRegistration.registerSubModel(new CustomComponentDefinition<>(Function.class, wrapFunction(PrincipalTransformer::from), ElytronDescriptionConstants.CUSTOM_PRINCIPAL_TRANSFORMER, PRINCIPAL_TRANSFORMER_RUNTIME_CAPABILITY)); resourceRegistration.registerSubModel(PrincipalTransformerDefinitions.getRegexPrincipalTransformerDefinition()); resourceRegistration.registerSubModel(PrincipalTransformerDefinitions.getRegexValidatingPrincipalTransformerDefinition()); resourceRegistration.registerSubModel(PrincipalTransformerDefinitions.getCasePrincipalTransformerDefinition()); // Realm Mappers resourceRegistration.registerSubModel(RealmMapperDefinitions.getConstantRealmMapper()); - resourceRegistration.registerSubModel(new CustomComponentDefinition<>(RealmMapper.class, Function.identity(), ElytronDescriptionConstants.CUSTOM_REALM_MAPPER, REALM_MAPPER_RUNTIME_CAPABILITY)); + resourceRegistration.registerSubModel(new CustomComponentDefinition<>(RealmMapper.class, ElytronDefinition::identity, ElytronDescriptionConstants.CUSTOM_REALM_MAPPER, REALM_MAPPER_RUNTIME_CAPABILITY)); resourceRegistration.registerSubModel(RealmMapperDefinitions.getMappedRegexRealmMapper()); resourceRegistration.registerSubModel(RealmMapperDefinitions.getSimpleRegexRealmMapperDefinition()); // Role Decoders - resourceRegistration.registerSubModel(new CustomComponentDefinition<>(RoleDecoder.class, Function.identity(), ElytronDescriptionConstants.CUSTOM_ROLE_DECODER, ROLE_DECODER_RUNTIME_CAPABILITY)); + resourceRegistration.registerSubModel(new CustomComponentDefinition<>(RoleDecoder.class, ElytronDefinition::identity, ElytronDescriptionConstants.CUSTOM_ROLE_DECODER, ROLE_DECODER_RUNTIME_CAPABILITY)); resourceRegistration.registerSubModel(RoleDecoderDefinitions.getSimpleRoleDecoderDefinition()); resourceRegistration.registerSubModel(RoleDecoderDefinitions.getSourceAddressRoleDecoderDefinition()); resourceRegistration.registerSubModel(RoleDecoderDefinitions.getAggregateRoleDecoderDefinition()); @@ -259,15 +259,15 @@ public void registerChildren(ManagementResourceRegistration resourceRegistration resourceRegistration.registerSubModel(RoleMapperDefinitions.getAddPrefixRoleMapperDefinition()); resourceRegistration.registerSubModel(RoleMapperDefinitions.getAggregateRoleMapperDefinition()); resourceRegistration.registerSubModel(RoleMapperDefinitions.getConstantRoleMapperDefinition()); - resourceRegistration.registerSubModel(new CustomComponentDefinition<>(RoleMapper.class, Function.identity(), ElytronDescriptionConstants.CUSTOM_ROLE_MAPPER, ROLE_MAPPER_RUNTIME_CAPABILITY)); + resourceRegistration.registerSubModel(new CustomComponentDefinition<>(RoleMapper.class, ElytronDefinition::identity, ElytronDescriptionConstants.CUSTOM_ROLE_MAPPER, ROLE_MAPPER_RUNTIME_CAPABILITY)); resourceRegistration.registerSubModel(RoleMapperDefinitions.getLogicalRoleMapperDefinition()); resourceRegistration.registerSubModel(RoleMapperDefinitions.getMappedRoleMapperDefinition()); resourceRegistration.registerSubModel(RoleMapperDefinitions.getRegexRoleMapperDefinition()); // Evidence Decoders resourceRegistration.registerSubModel(EvidenceDecoderDefinitions.getX500SubjectEvidenceDecoderDefinition()); resourceRegistration.registerSubModel(EvidenceDecoderDefinitions.getX509SubjectAltNameEvidenceDecoderDefinition()); - resourceRegistration.registerSubModel(new CustomComponentDefinition<>(EvidenceDecoder.class, Function.identity(), ElytronDescriptionConstants.CUSTOM_EVIDENCE_DECODER, EVIDENCE_DECODER_RUNTIME_CAPABILITY)); + resourceRegistration.registerSubModel(new CustomComponentDefinition<>(EvidenceDecoder.class, ElytronDefinition::identity, ElytronDescriptionConstants.CUSTOM_EVIDENCE_DECODER, EVIDENCE_DECODER_RUNTIME_CAPABILITY)); resourceRegistration.registerSubModel(EvidenceDecoderDefinitions.getAggregateEvidenceDecoderDefinition()); // HTTP Mechanisms @@ -363,6 +363,10 @@ protected void revertUpdateToRuntime(OperationContext context, ModelNode operati }); } + private static <T, U> U identity(T t, U u) { + return u; + } + @Override public void registerAdditionalRuntimePackages(ManagementResourceRegistration resourceRegistration) { resourceRegistration.registerAdditionalRuntimePackages(RuntimePackageDependency.required("org.wildfly.security.elytron"));
elytron/src/main/java/org/wildfly/extension/elytron/FailoverRealmDefinition.java+12 −5 modified@@ -8,7 +8,7 @@ import static org.wildfly.extension.elytron.Capabilities.SECURITY_REALM_CAPABILITY; import static org.wildfly.extension.elytron.Capabilities.SECURITY_REALM_RUNTIME_CAPABILITY; import static org.wildfly.extension.elytron.ElytronDefinition.commonDependencies; - +import static org.wildfly.extension.elytron.RealmDefinitions.createBruteForceRealmTransformer; import org.jboss.as.controller.AbstractAddStepHandler; import org.jboss.as.controller.AttributeDefinition; @@ -40,6 +40,7 @@ import org.wildfly.security.auth.server.event.SecurityRealmUnavailableEvent; import java.util.function.Consumer; +import java.util.function.Function; /** * A {@link ResourceDefinition} for a {@link SecurityRealm} which wraps one realm and fails over to another in case the first is unavailable. @@ -111,6 +112,12 @@ protected void performRuntime(OperationContext context, ModelNode operation, Mod boolean emitEvents = EMIT_EVENTS.resolveModelAttribute(context, model).asBoolean(); + ServiceBuilder<?> serviceBuilder = serviceTarget.addService(); + Consumer<SecurityRealm> valueConsumer = serviceBuilder.provides(realmName); + + final Function<SecurityRealm, SecurityRealm> realmTransformer = + createBruteForceRealmTransformer(context.getCurrentAddressValue(), SecurityRealm.class, serviceBuilder); + TrivialService<SecurityRealm> failoverRealmService = new TrivialService<SecurityRealm>(() -> { SecurityRealm delegate = delegateRealmValue.getValue(); @@ -120,10 +127,10 @@ protected void performRuntime(OperationContext context, ModelNode operation, Mod domain.handleSecurityEvent(new SecurityRealmUnavailableEvent(domain.getCurrentSecurityIdentity(), delegateRealm)); } } : (e) -> {}; - return new FailoverSecurityRealm(delegate, failoverRealmValue.getValue(), failoverConsumer); - }); + return realmTransformer.apply(new FailoverSecurityRealm(delegate, failoverRealmValue.getValue(), failoverConsumer)); + }, valueConsumer); - ServiceBuilder<SecurityRealm> serviceBuilder = serviceTarget.addService(realmName, failoverRealmService); + serviceBuilder.setInstance(failoverRealmService); addRealmDependency(context, serviceBuilder, delegateRealm, delegateRealmValue); addRealmDependency(context, serviceBuilder, failoverRealm, failoverRealmValue); @@ -133,7 +140,7 @@ protected void performRuntime(OperationContext context, ModelNode operation, Mod .install(); } - private void addRealmDependency(OperationContext context, ServiceBuilder<SecurityRealm> serviceBuilder, String realmName, Injector<SecurityRealm> securityRealmInjector) { + private void addRealmDependency(OperationContext context, ServiceBuilder<?> serviceBuilder, String realmName, Injector<SecurityRealm> securityRealmInjector) { String runtimeCapability = RuntimeCapability.buildDynamicCapabilityName(SECURITY_REALM_CAPABILITY, realmName); ServiceName realmServiceName = context.getCapabilityServiceName(runtimeCapability, SecurityRealm.class);
elytron/src/main/java/org/wildfly/extension/elytron/FileSystemRealmDefinition.java+21 −5 modified@@ -20,6 +20,7 @@ import static org.wildfly.extension.elytron.FileAttributeDefinitions.pathName; import static org.wildfly.extension.elytron.FileAttributeDefinitions.pathResolver; import static org.wildfly.extension.elytron.KeyStoreServiceUtil.getModifiableKeyStoreService; +import static org.wildfly.extension.elytron.RealmDefinitions.createBruteForceRealmTransformer; import static org.wildfly.extension.elytron._private.ElytronSubsystemMessages.ROOT_LOGGER; import java.io.IOException; @@ -31,6 +32,8 @@ import java.security.PrivateKey; import java.security.PublicKey; import java.security.UnrecoverableKeyException; +import java.util.function.Consumer; +import java.util.function.Function; import java.util.stream.Stream; import javax.crypto.SecretKey; @@ -72,6 +75,7 @@ import org.wildfly.extension.elytron.FileAttributeDefinitions.PathResolver; import org.wildfly.security.auth.realm.FileSystemSecurityRealm; import org.wildfly.security.auth.realm.FileSystemSecurityRealmBuilder; +import org.wildfly.security.auth.server.ModifiableSecurityRealm; import org.wildfly.security.auth.server.NameRewriter; import org.wildfly.security.auth.server.RealmUnavailableException; import org.wildfly.security.auth.server.SecurityRealm; @@ -299,8 +303,8 @@ protected void performRuntime(OperationContext context, ModelNode operation, Mod ServiceTarget serviceTarget = context.getServiceTarget(); String address = context.getCurrentAddressValue(); - ServiceName mainServiceName = MODIFIABLE_SECURITY_REALM_RUNTIME_CAPABILITY.fromBaseCapability(address).getCapabilityServiceName(); - ServiceName aliasServiceName = SECURITY_REALM_RUNTIME_CAPABILITY.fromBaseCapability(address).getCapabilityServiceName(); + ServiceName modifiableServiceName = MODIFIABLE_SECURITY_REALM_RUNTIME_CAPABILITY.fromBaseCapability(address).getCapabilityServiceName(); + ServiceName standardServiceName = SECURITY_REALM_RUNTIME_CAPABILITY.fromBaseCapability(address).getCapabilityServiceName(); final int levels = LEVELS.resolveModelAttribute(context, model).asInt(); @@ -327,6 +331,15 @@ protected void performRuntime(OperationContext context, ModelNode operation, Mod final SecretKey finalKey = key; ServiceRegistry keyStoreServiceRegistry = context.getServiceRegistry(true); + ServiceBuilder<?> serviceBuilder = serviceTarget.addService(); + // This is the Service that will get pulled into a SecurityDomain etc... + Consumer<SecurityRealm> standardConsumer = serviceBuilder.provides(standardServiceName); + // This is the modifiable variant for resources that support modification operations etc.. + Consumer<ModifiableSecurityRealm> modifiableConsumer = serviceBuilder.provides(modifiableServiceName); + + Function<ModifiableSecurityRealm, ModifiableSecurityRealm> realmTransformer = + createBruteForceRealmTransformer(context.getCurrentAddressValue(), ModifiableSecurityRealm.class, serviceBuilder); + TrivialService<SecurityRealm> fileSystemRealmService = new TrivialService<>( new TrivialService.ValueSupplier<SecurityRealm>() { @@ -382,8 +395,12 @@ public SecurityRealm get() throws StartException { fileSystemRealmBuilder.setPrivateKey(privateKey); fileSystemRealmBuilder.setPublicKey(publicKey); } - return fileSystemRealmBuilder.build(); + ModifiableSecurityRealm modifiable = fileSystemRealmBuilder.build(); + ModifiableSecurityRealm wrapped = realmTransformer.apply(modifiable); + modifiableConsumer.accept(wrapped); + standardConsumer.accept(wrapped); + return modifiable; } @Override @@ -396,8 +413,6 @@ public void dispose() { }); - ServiceBuilder<SecurityRealm> serviceBuilder = serviceTarget.addService(mainServiceName, fileSystemRealmService) - .addAliases(aliasServiceName); if (credentialStore != null) { serviceBuilder.requires(context.getCapabilityServiceName(buildDynamicCapabilityName(CREDENTIAL_STORE_CAPABILITY, credentialStore), CredentialStore.class)); } @@ -410,6 +425,7 @@ public void dispose() { serviceBuilder.addDependency(PathManagerService.SERVICE_NAME, PathManager.class, pathManagerInjector); serviceBuilder.requires(pathName(relativeTo)); } + serviceBuilder.setInstance(fileSystemRealmService); serviceBuilder.install(); }
elytron/src/main/java/org/wildfly/extension/elytron/JaasRealmDefinition.java+26 −17 modified@@ -5,6 +5,22 @@ package org.wildfly.extension.elytron; +import static org.wildfly.extension.elytron.Capabilities.SECURITY_REALM_RUNTIME_CAPABILITY; +import static org.wildfly.extension.elytron.ClassLoadingAttributeDefinitions.resolveClassLoader; +import static org.wildfly.extension.elytron.ElytronDefinition.commonDependencies; +import static org.wildfly.extension.elytron.FileAttributeDefinitions.pathName; +import static org.wildfly.extension.elytron.FileAttributeDefinitions.pathResolver; +import static org.wildfly.extension.elytron.RealmDefinitions.createBruteForceRealmTransformer; +import static org.wildfly.extension.elytron.SecurityActions.doPrivileged; +import static org.wildfly.extension.elytron._private.ElytronSubsystemMessages.ROOT_LOGGER; + +import java.io.File; +import java.security.PrivilegedExceptionAction; +import java.util.function.Consumer; +import java.util.function.Function; + +import javax.security.auth.callback.CallbackHandler; + import org.jboss.as.controller.AbstractAddStepHandler; import org.jboss.as.controller.AttributeDefinition; import org.jboss.as.controller.OperationContext; @@ -31,18 +47,6 @@ import org.wildfly.security.auth.realm.JaasSecurityRealm; import org.wildfly.security.auth.server.SecurityRealm; -import javax.security.auth.callback.CallbackHandler; -import java.io.File; -import java.security.PrivilegedExceptionAction; - -import static org.wildfly.extension.elytron.Capabilities.SECURITY_REALM_RUNTIME_CAPABILITY; -import static org.wildfly.extension.elytron.ClassLoadingAttributeDefinitions.resolveClassLoader; -import static org.wildfly.extension.elytron.ElytronDefinition.commonDependencies; -import static org.wildfly.extension.elytron.FileAttributeDefinitions.pathName; -import static org.wildfly.extension.elytron.FileAttributeDefinitions.pathResolver; -import static org.wildfly.extension.elytron.SecurityActions.doPrivileged; -import static org.wildfly.extension.elytron._private.ElytronSubsystemMessages.ROOT_LOGGER; - /** * A {@link ResourceDefinition} for a {@link SecurityRealm} backed by a JAAS LoginContext. */ @@ -127,6 +131,13 @@ protected void performRuntime(OperationContext context, ModelNode operation, Mod final InjectedValue<PathManager> pathManagerInjector = new InjectedValue<>(); + ServiceName realmName = runtimeCapability.getCapabilityServiceName(SecurityRealm.class); + ServiceBuilder<?> serviceBuilder = serviceTarget.addService(); + Consumer<SecurityRealm> realmConsumer = serviceBuilder.provides(realmName); + + Function<SecurityRealm, SecurityRealm> realmTransformer = + createBruteForceRealmTransformer(context.getCurrentAddressValue(), SecurityRealm.class, serviceBuilder); + CallbackHandler finalCallbackHandler = callbackhandler; TrivialService<SecurityRealm> jaasRealmService = new TrivialService<>( new TrivialService.ValueSupplier<SecurityRealm>() { @@ -143,7 +154,7 @@ public SecurityRealm get() throws StartException { } rootPath = jaasConfigFile.getPath(); } - return new JaasSecurityRealm(entryName, rootPath, classLoader, finalCallbackHandler); + return realmTransformer.apply(new JaasSecurityRealm(entryName, rootPath, classLoader, finalCallbackHandler)); } @Override @@ -153,17 +164,15 @@ public void dispose() { pathResolver = null; } } - }); - - ServiceName realmName = runtimeCapability.getCapabilityServiceName(SecurityRealm.class); - ServiceBuilder<SecurityRealm> serviceBuilder = serviceTarget.addService(realmName, jaasRealmService); + }, realmConsumer); if (relativeTo != null) { serviceBuilder.addDependency(PathManagerService.SERVICE_NAME, PathManager.class, pathManagerInjector); serviceBuilder.requires(pathName(relativeTo)); } commonDependencies(serviceBuilder) + .setInstance(jaasRealmService) .setInitialMode(ServiceController.Mode.ACTIVE) .install(); }
elytron/src/main/java/org/wildfly/extension/elytron/JdbcRealmDefinition.java+11 −2 modified@@ -16,6 +16,7 @@ import static org.wildfly.extension.elytron.ElytronDescriptionConstants.SCRAM_MAPPER; import static org.wildfly.extension.elytron.ElytronDescriptionConstants.SIMPLE_DIGEST_MAPPER; import static org.wildfly.extension.elytron.ElytronDescriptionConstants.UTF_8; +import static org.wildfly.extension.elytron.RealmDefinitions.createBruteForceRealmTransformer; import static org.wildfly.extension.elytron._private.ElytronSubsystemMessages.ROOT_LOGGER; import java.nio.charset.Charset; @@ -24,6 +25,8 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.function.Consumer; +import java.util.function.Function; import javax.sql.DataSource; @@ -612,8 +615,13 @@ protected void performRuntime(OperationContext context, ModelNode operation, Mod final JdbcSecurityRealmBuilder builder = JdbcSecurityRealm.builder(); builder.setHashCharset(charset); - TrivialService<SecurityRealm> service = new TrivialService<SecurityRealm>(builder::build); - ServiceBuilder<SecurityRealm> serviceBuilder = serviceTarget.addService(realmName, service); + ServiceBuilder<?> serviceBuilder = serviceTarget.addService(); + Consumer<SecurityRealm> valueConsumer = serviceBuilder.provides(realmName); + + Function<SecurityRealm, SecurityRealm> realmTransformer = + createBruteForceRealmTransformer(context.getCurrentAddressValue(), SecurityRealm.class, serviceBuilder); + + TrivialService<SecurityRealm> service = new TrivialService<SecurityRealm>(() -> realmTransformer.apply(builder.build()), valueConsumer); for (ModelNode query : principalQueries.asList()) { String authenticationQuerySql = PrincipalQueryAttributes.SQL.resolveModelAttribute(context, query).asString(); @@ -639,6 +647,7 @@ public void uninject() { }); } + serviceBuilder.setInstance(service); commonDependencies(serviceBuilder) .setInitialMode(context.getRunningMode() == RunningMode.ADMIN_ONLY ? ServiceController.Mode.LAZY : ServiceController.Mode.ACTIVE) .install();
elytron/src/main/java/org/wildfly/extension/elytron/LdapRealmDefinition.java+26 −6 modified@@ -14,11 +14,14 @@ import static org.wildfly.extension.elytron.ElytronDescriptionConstants.HEX; import static org.wildfly.extension.elytron.ElytronDescriptionConstants.UTF_8; import static org.wildfly.extension.elytron._private.ElytronSubsystemMessages.ROOT_LOGGER; +import static org.wildfly.extension.elytron.RealmDefinitions.createBruteForceRealmTransformer; import java.nio.charset.Charset; import java.util.Arrays; import java.util.List; import java.util.Set; +import java.util.function.Consumer; +import java.util.function.Function; import javax.naming.InvalidNameException; import javax.naming.NamingException; @@ -59,6 +62,7 @@ import org.wildfly.security.auth.realm.ldap.AttributeMapping; import org.wildfly.security.auth.realm.ldap.LdapSecurityRealmBuilder; import org.wildfly.security.auth.realm.ldap.LdapSecurityRealmBuilder.IdentityMappingBuilder; +import org.wildfly.security.auth.server.ModifiableSecurityRealm; import org.wildfly.security.auth.server.SecurityRealm; import org.wildfly.security.password.spec.Encoding; @@ -435,8 +439,8 @@ protected void performRuntime(OperationContext context, ModelNode operation, Mod ServiceTarget serviceTarget = context.getServiceTarget(); String address = context.getCurrentAddressValue(); - ServiceName mainServiceName = MODIFIABLE_SECURITY_REALM_RUNTIME_CAPABILITY.fromBaseCapability(address).getCapabilityServiceName(); - ServiceName aliasServiceName = SECURITY_REALM_RUNTIME_CAPABILITY.fromBaseCapability(address).getCapabilityServiceName(); + ServiceName modifiableServiceName = MODIFIABLE_SECURITY_REALM_RUNTIME_CAPABILITY.fromBaseCapability(address).getCapabilityServiceName(); + ServiceName standardServiceName = SECURITY_REALM_RUNTIME_CAPABILITY.fromBaseCapability(address).getCapabilityServiceName(); final LdapSecurityRealmBuilder builder = LdapSecurityRealmBuilder.builder(); @@ -455,10 +459,26 @@ protected void performRuntime(OperationContext context, ModelNode operation, Mod builder.setHashEncoding(HEX.equals(hashEncoding) ? Encoding.HEX : Encoding.BASE64); builder.setHashCharset(charset); - TrivialService<SecurityRealm> ldapRealmService = new TrivialService<>(builder::build); - ServiceBuilder<SecurityRealm> serviceBuilder = serviceTarget.addService(mainServiceName, ldapRealmService) - .addAliases(aliasServiceName); + ServiceBuilder<?> serviceBuilder = serviceTarget.addService(); + // This is the Service that will get pulled into a SecurityDomain etc... + Consumer<SecurityRealm> standardConsumer = serviceBuilder.provides(standardServiceName); + // This is the modifiable variant for resources that support modification operations etc.. + Consumer<ModifiableSecurityRealm> modifiableConsumer = serviceBuilder.provides(modifiableServiceName); + Function<ModifiableSecurityRealm, ModifiableSecurityRealm> realmTransformer = + createBruteForceRealmTransformer(context.getCurrentAddressValue(), ModifiableSecurityRealm.class, serviceBuilder); + + TrivialService<SecurityRealm> ldapRealmService = + new TrivialService<>(() -> { + ModifiableSecurityRealm modifiable = builder.build(); + ModifiableSecurityRealm wrapped = realmTransformer.apply(modifiable); + modifiableConsumer.accept(wrapped); + standardConsumer.accept(wrapped); + + return modifiable; + }); + + serviceBuilder.setInstance(ldapRealmService); commonDependencies(serviceBuilder); configureIdentityMapping(context, model, builder); @@ -467,7 +487,7 @@ protected void performRuntime(OperationContext context, ModelNode operation, Mod serviceBuilder.setInitialMode(ServiceController.Mode.ACTIVE).install(); } - private void configureDirContext(OperationContext context, ModelNode model, LdapSecurityRealmBuilder realmBuilder, ServiceBuilder<SecurityRealm> serviceBuilder) throws OperationFailedException { + private void configureDirContext(OperationContext context, ModelNode model, LdapSecurityRealmBuilder realmBuilder, ServiceBuilder<?> serviceBuilder) throws OperationFailedException { String dirContextName = DIR_CONTEXT.resolveModelAttribute(context, model).asStringOrNull(); String runtimeCapability = RuntimeCapability.buildDynamicCapabilityName(DIR_CONTEXT_CAPABILITY, dirContextName);
elytron/src/main/java/org/wildfly/extension/elytron/PropertiesRealmDefinition.java+22 −8 modified@@ -5,6 +5,7 @@ package org.wildfly.extension.elytron; + import static org.wildfly.extension.elytron.Capabilities.SECURITY_REALM_RUNTIME_CAPABILITY; import static org.wildfly.extension.elytron.ElytronDescriptionConstants.BASE64; import static org.wildfly.extension.elytron.ElytronDescriptionConstants.HEX; @@ -13,6 +14,7 @@ import static org.wildfly.extension.elytron.ElytronExtension.getRequiredService; import static org.wildfly.extension.elytron.FileAttributeDefinitions.RELATIVE_TO; import static org.wildfly.extension.elytron.FileAttributeDefinitions.pathName; +import static org.wildfly.extension.elytron.RealmDefinitions.createBruteForceRealmTransformer; import static org.wildfly.extension.elytron.SecurityActions.doPrivileged; import static org.wildfly.extension.elytron._private.ElytronSubsystemMessages.ROOT_LOGGER; @@ -29,6 +31,8 @@ import java.util.ArrayList; import java.util.Date; import java.util.List; +import java.util.function.Function; +import java.util.function.LongSupplier; import org.jboss.as.controller.AbstractAddStepHandler; import org.jboss.as.controller.AttributeDefinition; @@ -58,6 +62,7 @@ import org.jboss.msc.service.ServiceName; import org.jboss.msc.service.StartException; import org.jboss.msc.value.InjectedValue; +import org.wildfly.common.function.ExceptionBiConsumer; import org.wildfly.extension.elytron.TrivialResourceDefinition.Builder; import org.wildfly.extension.elytron.TrivialService.ValueSupplier; import org.wildfly.security.auth.SupportLevel; @@ -181,6 +186,8 @@ protected ValueSupplier<SecurityRealm> getValueSupplier(ServiceBuilder<SecurityR } } + Function<SecurityRealm, SecurityRealm> realmTransformer = + createBruteForceRealmTransformer(context.getCurrentAddressValue(), SecurityRealm.class, serviceBuilder); return new ValueSupplier<SecurityRealm>() { private final List<Handle> callbackHandles = new ArrayList<>(); @@ -192,15 +199,17 @@ public SecurityRealm get() throws StartException { try (InputStream usersInputStream = new FileInputStream(usersFile); InputStream groupsInputStream = groupsFile != null ? new FileInputStream(groupsFile) : null) { - return new RealmWrapper(LegacyPropertiesSecurityRealm.builder() + LegacyPropertiesSecurityRealm baseRealm = LegacyPropertiesSecurityRealm.builder() .setUsersStream(usersInputStream) .setGroupsStream(groupsInputStream) .setPlainText(plainText) .setGroupsAttribute(groupsAttribute) .setDefaultRealm(digestRealmName) .setHashEncoding(BASE64.equalsIgnoreCase(hashEncoding) ? Encoding.BASE64 : Encoding.HEX) .setHashCharset(Charset.forName(hashCharset)) - .build(), usersFile, groupsFile); + .build(); + + return new RealmWrapper(realmTransformer.apply(baseRealm), usersFile, groupsFile, baseRealm::getLoadTime, baseRealm::load); } catch (FileNotFoundException e) { throw ROOT_LOGGER.propertyFilesDoesNotExist(e.getMessage()); @@ -310,14 +319,19 @@ protected void executeRuntimeStep(OperationContext context, ModelNode operation) private static final class RealmWrapper implements SecurityRealm { - private final LegacyPropertiesSecurityRealm delegate; + private final SecurityRealm delegate; private final File usersFile; private final File groupsFile; + private final LongSupplier loadTimeSupplier; + private final ExceptionBiConsumer<InputStream, InputStream, IOException> propertiesFileLoader; - RealmWrapper(LegacyPropertiesSecurityRealm delegate, File usersFile, File groupsFile) { + RealmWrapper(SecurityRealm delegate, File usersFile, File groupsFile, LongSupplier loadTimeSupplier, + ExceptionBiConsumer<InputStream, InputStream, IOException> propertiesFileLoader) { this.delegate = delegate; this.usersFile = usersFile; this.groupsFile = groupsFile; + this.loadTimeSupplier = loadTimeSupplier; + this.propertiesFileLoader = propertiesFileLoader; } @Override @@ -364,14 +378,14 @@ public void handleRealmEvent(RealmEvent event) { } long getLoadTime() { - return delegate.getLoadTime(); + return loadTimeSupplier.getAsLong(); } void reloadIfNeeded() throws IOException { - long loadTime = delegate.getLoadTime(); + long loadTime = loadTimeSupplier.getAsLong(); if (shouldReload(loadTime)) { synchronized(this) { - loadTime = delegate.getLoadTime(); + loadTime = loadTimeSupplier.getAsLong(); if (shouldReload(loadTime)) { reloadInternal(); } @@ -394,7 +408,7 @@ void reload() throws OperationFailedException { void reloadInternal() throws IOException { try (InputStream usersInputStream = new FileInputStream(usersFile); InputStream groupsInputStream = groupsFile != null ? new FileInputStream(groupsFile) : null) { - delegate.load(usersInputStream, groupsInputStream); + propertiesFileLoader.accept(usersInputStream, groupsInputStream); } }
elytron/src/main/java/org/wildfly/extension/elytron/RealmDefinitions.java+86 −0 modified@@ -4,12 +4,18 @@ */ package org.wildfly.extension.elytron; +import static org.wildfly.extension.elytron.Capabilities.SCHEDULED_EXECUTOR_RUNTIME_CAPABILITY; import static org.wildfly.extension.elytron.Capabilities.SECURITY_REALM_RUNTIME_CAPABILITY; +import static org.wildfly.extension.elytron._private.ElytronSubsystemMessages.ROOT_LOGGER; +import static org.wildfly.security.manager.WildFlySecurityManager.getPropertyPrivileged; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.concurrent.ScheduledExecutorService; +import java.util.function.Function; +import java.util.function.Supplier; import org.jboss.as.controller.AbstractAddStepHandler; import org.jboss.as.controller.AttributeDefinition; @@ -22,6 +28,7 @@ import org.jboss.dmr.ModelType; import org.jboss.msc.service.ServiceBuilder; import org.wildfly.extension.elytron.TrivialService.ValueSupplier; +import org.wildfly.security.auth.realm.BruteForceRealmWrapper; import org.wildfly.security.auth.realm.SimpleMapBackedSecurityRealm; import org.wildfly.security.auth.realm.SimpleRealmEntry; import org.wildfly.security.auth.server.SecurityRealm; @@ -33,6 +40,15 @@ */ class RealmDefinitions { + /** + * System properties used to configure brute force protection on the security realms. + */ + private static final String BRUTE_FORCE_ENABLED = "wildfly.elytron.realm.%s.brute-force.enabled"; + private static final String BRUTE_FORCE_MAX_FAILED_ATTEMPTS = "wildfly.elytron.realm.%s.brute-force.max-failed-attempts"; + private static final String BRUTE_FORCE_LOCKOUT_INTERVAL = "wildfly.elytron.realm.%s.brute-force.lockout-interval"; + private static final String BRUTE_FORCE_SESSION_TIMEOUT = "wildfly.elytron.realm.%s.brute-force.session-timeout"; + private static final String BRUTE_FORCE_MAX_CACHED_SESSIONS = "wildfly.elytron.realm.%s.brute-force.max-cached-sessions"; + static final AttributeDefinition IDENTITY = new SimpleAttributeDefinitionBuilder(ElytronDescriptionConstants.IDENTITY, ModelType.STRING, false) .setAllowExpression(true) .setMinSize(1) @@ -83,4 +99,74 @@ protected ValueSupplier<SecurityRealm> getValueSupplier(ServiceBuilder<SecurityR return new TrivialResourceDefinition(ElytronDescriptionConstants.IDENTITY_REALM, add, IDENTITY_REALM_ATTRIBUTES, SECURITY_REALM_RUNTIME_CAPABILITY); } + + private static boolean isBruteForceProtectionEnabled(final String realmName) { + return Boolean.parseBoolean(getPropertyPrivileged(String.format(BRUTE_FORCE_ENABLED, realmName), "true")); + } + + private static int getBruteForceConfigValue(final String realmName, final String systemPropertyTemplate) { + try { + return Integer.parseInt(getPropertyPrivileged(String.format(systemPropertyTemplate, realmName), "-1")); + } catch (NumberFormatException e) { + return -1; + } + } + + static <T extends SecurityRealm> T addBruteForceProtection(final T original, final Class<T> clazz, + final ScheduledExecutorService executor, final String realmName, final int maxAttempts, + final int lockoutInterval, final int sessionTimeout, final int maxCachedSessions) { + + return BruteForceRealmWrapper.create() + .wrapping(original) + .withExecutor(executor) + .setRealmName(realmName) + .setMaxFailedAttempts(maxAttempts) + .setLockoutInterval(lockoutInterval) + .setFailureSessionTimeout(sessionTimeout) + .setMaxCachedSessions(maxCachedSessions) + .wrap(clazz); + } + + static <T extends SecurityRealm> Function<T, T> createBruteForceRealmTransformer(String name, Class<T> clazz, ServiceBuilder<?> serviceBuilder) { + Function<T, T> transformer; + if (isBruteForceProtectionEnabled(name)) { + final Supplier<ScheduledExecutorService> executorSupplier = + serviceBuilder.requires(SCHEDULED_EXECUTOR_RUNTIME_CAPABILITY.getCapabilityServiceName()); + int maxAttempts = getBruteForceConfigValue(name, BRUTE_FORCE_MAX_FAILED_ATTEMPTS); + int lockoutInterval = getBruteForceConfigValue(name, BRUTE_FORCE_LOCKOUT_INTERVAL); + int sessionTimeout = getBruteForceConfigValue(name, BRUTE_FORCE_SESSION_TIMEOUT); + int maxCachedSessions = getBruteForceConfigValue(name, BRUTE_FORCE_MAX_CACHED_SESSIONS); + + ROOT_LOGGER.tracef("Applying brute force protection to '%s' security realm. maxAttempts=%d, lockoutTimeout=%d, sessionTimeout=%d, maxCachedSessions=%d", + name, maxAttempts, lockoutInterval, sessionTimeout, maxCachedSessions); + + transformer = (r) -> addBruteForceProtection(r, clazz, executorSupplier.get(), name, maxAttempts, lockoutInterval, sessionTimeout, maxCachedSessions); + } else { + ROOT_LOGGER.tracef("Not applying brute force protection to '%s' security realm.", name); + transformer = Function.identity(); + } + + return transformer; + } + + static class CustomRealmBruteForceTransformer<T extends SecurityRealm> implements CustomComponentDefinition.CustomComponentTransformer<T, T> { + + private final Class<T> securityRealmClazz; + + CustomRealmBruteForceTransformer(Class<T> securityRealmClazz) { + this.securityRealmClazz = securityRealmClazz; + } + + @Override + public Object prepareTransformer(String name, ServiceBuilder<?> serviceBuilder) { + return createBruteForceRealmTransformer(name, securityRealmClazz, serviceBuilder); + } + + @Override + public T apply(Object o, T securityRealm) { + return ((Function<T, T>) o).apply(securityRealm); + } + + } + }
elytron/src/test/java/org/wildfly/extension/elytron/RealmsTestCase.java+2 −2 modified@@ -625,10 +625,10 @@ public void testJAASRealm() throws Exception { Assert.fail(services.getBootError().toString()); } - JaasSecurityRealm securityRealm; + SecurityRealm securityRealm; if (!(JdkUtils.isIbmJdk())) { ServiceName serviceName = Capabilities.SECURITY_REALM_RUNTIME_CAPABILITY.getCapabilityServiceName("myJaasRealm"); - securityRealm = (JaasSecurityRealm) services.getContainer().getService(serviceName).getValue(); + securityRealm = (SecurityRealm) services.getContainer().getService(serviceName).getValue(); Assert.assertNotNull(securityRealm); } else { // IBM JDK 8 does not recognize default policy type "JavaLoginConfig" so the path to JAAS configuration file must be provided via system property
a6f9d7534aa4Merge pull request #6634 from darranl/WFCORE-7192/combined
12 files changed · +311 −100
elytron/src/main/java/org/wildfly/extension/elytron/CachingRealmDefinition.java+46 −23 modified@@ -7,14 +7,18 @@ import static org.wildfly.extension.elytron.Capabilities.SECURITY_REALM_CAPABILITY; import static org.wildfly.extension.elytron.Capabilities.SECURITY_REALM_RUNTIME_CAPABILITY; import static org.wildfly.extension.elytron.ElytronDefinition.commonDependencies; -import static org.wildfly.extension.elytron.ElytronExtension.getRequiredService; +import static org.wildfly.extension.elytron.RealmDefinitions.createBruteForceRealmTransformer; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Consumer; +import java.util.function.Function; import org.jboss.as.controller.AbstractAddStepHandler; import org.jboss.as.controller.AttributeDefinition; import org.jboss.as.controller.OperationContext; import org.jboss.as.controller.OperationFailedException; import org.jboss.as.controller.OperationStepHandler; -import org.jboss.as.controller.PathAddress; import org.jboss.as.controller.PathElement; import org.jboss.as.controller.ResourceDefinition; import org.jboss.as.controller.RunningMode; @@ -32,8 +36,8 @@ import org.jboss.msc.service.ServiceBuilder; import org.jboss.msc.service.ServiceController; import org.jboss.msc.service.ServiceName; -import org.jboss.msc.service.ServiceRegistry; import org.jboss.msc.service.ServiceTarget; +import org.jboss.msc.service.StartException; import org.jboss.msc.value.InjectedValue; import org.wildfly.extension.elytron._private.ElytronSubsystemMessages; import org.wildfly.security.auth.realm.CacheableSecurityRealm; @@ -73,6 +77,10 @@ class CachingRealmDefinition extends SimpleResourceDefinition { static final AttributeDefinition[] ATTRIBUTES = new AttributeDefinition[] {REALM_NAME, MAXIMUM_ENTRIES, MAXIMUM_AGE}; + // Callers are expected to just use a single method get / put / remove not multiple calls so we don't + // need complex locking beyond the Map itself.. + private static final Map<String, CachingSecurityRealm> REALMS = new ConcurrentHashMap<>(); + private static final AbstractAddStepHandler ADD = new RealmAddHandler(); private static final OperationStepHandler REMOVE = new TrivialCapabilityServiceRemoveHandler(ADD, SECURITY_REALM_RUNTIME_CAPABILITY); @@ -114,36 +122,55 @@ protected void performRuntime(OperationContext context, ModelNode operation, Mod int maxEntries = MAXIMUM_ENTRIES.resolveModelAttribute(context, model).asInt(); long maxAge = MAXIMUM_AGE.resolveModelAttribute(context, model).asInt(); InjectedValue<SecurityRealm> cacheableRealmValue = new InjectedValue<>(); - ServiceBuilder<SecurityRealm> serviceBuilder = serviceTarget.addService(realmName, createService(cacheableRealm, maxEntries, maxAge, cacheableRealmValue)); + + ServiceBuilder<?> serviceBuilder = serviceTarget.addService(); + Consumer<SecurityRealm> valueConsumer = serviceBuilder.provides(realmName); + + final Function<SecurityRealm, SecurityRealm> realmTransformer = + createBruteForceRealmTransformer(context.getCurrentAddressValue(), SecurityRealm.class, serviceBuilder); + + serviceBuilder.setInstance(createService(context.getCurrentAddressValue(), cacheableRealm, maxEntries, maxAge, cacheableRealmValue, realmTransformer, valueConsumer)); addRealmDependency(context, serviceBuilder, cacheableRealm, cacheableRealmValue); commonDependencies(serviceBuilder).setInitialMode(context.getRunningMode() == RunningMode.ADMIN_ONLY ? ServiceController.Mode.LAZY : ServiceController.Mode.ACTIVE).install(); } - private TrivialService<SecurityRealm> createService(String realmName, int maxEntries, long maxAge, InjectedValue<SecurityRealm> injector) { - return new TrivialService<>((TrivialService.ValueSupplier<SecurityRealm>) () -> { - SecurityRealm securityRealm = injector.getValue(); + private TrivialService<SecurityRealm> createService(String ourRealmName, String wrappedRealmName, int maxEntries, long maxAge, + InjectedValue<SecurityRealm> injector, Function<SecurityRealm, SecurityRealm> realmTransformer, Consumer<SecurityRealm> valueConsumer) { + return new TrivialService<>(new TrivialService.ValueSupplier<SecurityRealm>() { + + @Override + public SecurityRealm get() throws StartException { + SecurityRealm securityRealm = injector.getValue(); - if (securityRealm instanceof CacheableSecurityRealm) { - RealmIdentityCache cache = createRealmIdentityCache(maxEntries, maxAge); - CacheableSecurityRealm cacheableRealm = CacheableSecurityRealm.class.cast(securityRealm); + if (securityRealm instanceof CacheableSecurityRealm) { + RealmIdentityCache cache = createRealmIdentityCache(maxEntries, maxAge); + CacheableSecurityRealm cacheableRealm = CacheableSecurityRealm.class.cast(securityRealm); - if (securityRealm instanceof ModifiableSecurityRealm) { - return new CachingModifiableSecurityRealm(cacheableRealm, cache); + CachingSecurityRealm cachingRealm = securityRealm instanceof ModifiableSecurityRealm ? + new CachingModifiableSecurityRealm(cacheableRealm, cache) : new CachingSecurityRealm(cacheableRealm, cache); + + REALMS.put(ourRealmName, cachingRealm); + + return realmTransformer.apply(cachingRealm); } - return new CachingSecurityRealm(cacheableRealm, cache); + throw ElytronSubsystemMessages.ROOT_LOGGER.realmDoesNotSupportCache(wrappedRealmName); } - throw ElytronSubsystemMessages.ROOT_LOGGER.realmDoesNotSupportCache(realmName); - }); + @Override + public void dispose() { + REALMS.remove(ourRealmName); + } + + }, valueConsumer); } private LRURealmIdentityCache createRealmIdentityCache(int maxEntries, long maxAge) { return new LRURealmIdentityCache(maxEntries, maxAge); } - private void addRealmDependency(OperationContext context, ServiceBuilder<SecurityRealm> serviceBuilder, String realmName, Injector<SecurityRealm> securityRealmInjector) { + private void addRealmDependency(OperationContext context, ServiceBuilder<?> serviceBuilder, String realmName, Injector<SecurityRealm> securityRealmInjector) { String runtimeCapability = RuntimeCapability.buildDynamicCapabilityName(SECURITY_REALM_CAPABILITY, realmName); ServiceName realmServiceName = context.getCapabilityServiceName(runtimeCapability, SecurityRealm.class); REALM_SERVICE_UTIL.addInjection(serviceBuilder, securityRealmInjector, realmServiceName); @@ -165,15 +192,11 @@ private ClearCacheHandler() { @Override protected void executeRuntimeStep(final OperationContext context, final ModelNode operation) throws OperationFailedException { - ServiceRegistry serviceRegistry = context.getServiceRegistry(true); - PathAddress currentAddress = context.getCurrentAddress(); - RuntimeCapability<Void> runtimeCapability = SECURITY_REALM_RUNTIME_CAPABILITY.fromBaseCapability(currentAddress.getLastElement().getValue()); - ServiceName realmName = runtimeCapability.getCapabilityServiceName(); - ServiceController<SecurityRealm> serviceController = getRequiredService(serviceRegistry, realmName, SecurityRealm.class); - if(serviceController.getState() != ServiceController.State.UP) { + CachingSecurityRealm securityRealm = REALMS.get(context.getCurrentAddressValue()); + if (securityRealm == null) { throw ElytronSubsystemMessages.ROOT_LOGGER.cachedRealmServiceNotAvailable(); } - CachingSecurityRealm securityRealm = CachingSecurityRealm.class.cast(serviceController.getValue()); + securityRealm.removeAllFromCache(); } }
elytron/src/main/java/org/wildfly/extension/elytron/CustomComponentDefinition.java+26 −9 modified@@ -16,6 +16,7 @@ import java.security.PrivilegedExceptionAction; import java.util.Map; import java.util.Set; +import java.util.function.BiFunction; import java.util.function.Function; import org.jboss.as.controller.AbstractAddStepHandler; @@ -58,14 +59,14 @@ class CustomComponentDefinition<C, T> extends SimpleResourceDefinition { static final AttributeDefinition[] ATTRIBUTES = {MODULE, CLASS_NAME, CONFIGURATION}; - CustomComponentDefinition(Class<C> serviceType, Function<C, T> wrapper, String pathKey, @SuppressWarnings("rawtypes") RuntimeCapability ... runtimeCapabilities) { + CustomComponentDefinition(Class<C> serviceType, CustomComponentTransformer<C, T> transformer, String pathKey, @SuppressWarnings("rawtypes") RuntimeCapability ... runtimeCapabilities) { super(addAddRemoveHandlers(new Parameters(PathElement.pathElement(pathKey), ElytronExtension.getResourceDescriptionResolver(pathKey)) .setAddRestartLevel(OperationEntry.Flag.RESTART_RESOURCE_SERVICES) .setRemoveRestartLevel(OperationEntry.Flag.RESTART_RESOURCE_SERVICES) - .setCapabilities(runtimeCapabilities), serviceType, wrapper, runtimeCapabilities)); + .setCapabilities(runtimeCapabilities), serviceType, transformer, runtimeCapabilities)); } - private static <C, T> Parameters addAddRemoveHandlers(Parameters parameters, Class<C> serviceType, Function<C, T> wrapper, RuntimeCapability<?> ... runtimeCapabilities) { + private static <C, T> Parameters addAddRemoveHandlers(Parameters parameters, Class<C> serviceType, CustomComponentTransformer<C, T> wrapper, RuntimeCapability<?> ... runtimeCapabilities) { AbstractAddStepHandler add = new ComponentAddHandler<>(serviceType, wrapper, runtimeCapabilities); OperationStepHandler remove = new TrivialCapabilityServiceRemoveHandler(add, runtimeCapabilities); @@ -86,13 +87,13 @@ private static class ComponentAddHandler<C, T> extends BaseAddHandler { private final RuntimeCapability<?>[] runtimeCapabilities; private final Class<C> serviceType; - private final Function<C, T> wrapper; + private final CustomComponentTransformer<C, T> transformer; - private ComponentAddHandler(Class<C> serviceType, Function<C, T> wrapper, RuntimeCapability<?> ... runtimeCapabilities) { + private ComponentAddHandler(Class<C> serviceType, CustomComponentTransformer<C, T> transformer, RuntimeCapability<?> ... runtimeCapabilities) { super(Set.of(runtimeCapabilities)); this.runtimeCapabilities = runtimeCapabilities; this.serviceType = serviceType; - this.wrapper = wrapper; + this.transformer = transformer; } @Override @@ -115,8 +116,11 @@ protected void performRuntime(OperationContext context, ModelNode operation, Mod serviceBuilder.addAliases(toServiceName(runtimeCapabilities[i], address)); } + String name = context.getCurrentAddressValue(); + Object wrapperContext = transformer.prepareTransformer(name, serviceBuilder); + commonRequirements(serviceBuilder) - .setInstance(new TrivialService<>(() -> createValue(module, className, configurationMap))) + .setInstance(new TrivialService<>(() -> createValue(wrapperContext, module, className, configurationMap))) .setInitialMode(Mode.ACTIVE) .install(); } @@ -125,7 +129,7 @@ private ServiceName toServiceName(RuntimeCapability<?> runtimeCapability, String return runtimeCapability.fromBaseCapability(addressValue).getCapabilityServiceName(); } - private T createValue(String module, String className, Map<String, String> configuration) throws StartException { + private T createValue(Object transformerContext, String module, String className, Map<String, String> configuration) throws StartException { final ClassLoader classLoader; try { classLoader = doPrivileged((PrivilegedExceptionAction<ClassLoader>) () -> resolveClassLoader(module)); @@ -143,7 +147,7 @@ private T createValue(String module, String className, Map<String, String> confi } } - return wrapper.apply(component); + return transformer.apply(transformerContext, component); } catch (PrivilegedActionException e) { throw new StartException(e.getCause()); } catch (Exception e) { @@ -156,4 +160,17 @@ private T createValue(String module, String className, Map<String, String> confi } } + static <A, B> CustomComponentTransformer<A, B> wrapFunction(Function<A, B> function) { + return (s, a ) -> function.apply(a); + } + + @FunctionalInterface + interface CustomComponentTransformer<A, B> extends BiFunction<Object, A, B> { + + default Object prepareTransformer(final String name, final ServiceBuilder<?> serviceBuilder) { + return name; + } + + } + }
elytron/src/main/java/org/wildfly/extension/elytron/DistributedRealmDefinition.java+15 −9 modified@@ -8,7 +8,12 @@ import static org.wildfly.extension.elytron.Capabilities.SECURITY_REALM_CAPABILITY; import static org.wildfly.extension.elytron.Capabilities.SECURITY_REALM_RUNTIME_CAPABILITY; import static org.wildfly.extension.elytron.ElytronDefinition.commonDependencies; +import static org.wildfly.extension.elytron.RealmDefinitions.createBruteForceRealmTransformer; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; +import java.util.function.Function; import org.jboss.as.controller.AbstractAddStepHandler; import org.jboss.as.controller.AttributeDefinition; @@ -32,16 +37,11 @@ import org.jboss.msc.service.ServiceName; import org.jboss.msc.service.ServiceTarget; import org.jboss.msc.value.InjectedValue; - import org.wildfly.security.auth.realm.DistributedSecurityRealm; import org.wildfly.security.auth.server.SecurityDomain; import org.wildfly.security.auth.server.SecurityRealm; import org.wildfly.security.auth.server.event.SecurityRealmUnavailableEvent; -import java.util.ArrayList; -import java.util.List; -import java.util.function.Consumer; - /** * A {@link ResourceDefinition} for a {@link SecurityRealm} for authentication and authorization of identities distributed between multiple realms. * @@ -110,6 +110,12 @@ protected void performRuntime(OperationContext context, ModelNode operation, Mod List<String> distributedRealms = REALMS.unwrap(context, model); + ServiceBuilder<?> serviceBuilder = serviceTarget.addService(); + Consumer<SecurityRealm> valueConsumer = serviceBuilder.provides(realmName); + + final Function<SecurityRealm, SecurityRealm> realmTransformer = + createBruteForceRealmTransformer(context.getCurrentAddressValue(), SecurityRealm.class, serviceBuilder); + TrivialService<SecurityRealm> distributedRealmService = new TrivialService<SecurityRealm>(() -> { SecurityRealm[] realms = new SecurityRealm[distributedRealmValues.size()]; @@ -126,10 +132,10 @@ protected void performRuntime(OperationContext context, ModelNode operation, Mod realms[i] = distributedRealmValues.get(i).getValue(); } - return new DistributedSecurityRealm(ignoreUnavailableRealms, unavailableRealmConsumer, realms); - }); + return realmTransformer.apply(new DistributedSecurityRealm(ignoreUnavailableRealms, unavailableRealmConsumer, realms)); + }, valueConsumer); - ServiceBuilder<SecurityRealm> serviceBuilder = serviceTarget.addService(realmName, distributedRealmService); + serviceBuilder.setInstance(distributedRealmService); for (String distributedRealm : distributedRealms) { InjectedValue<SecurityRealm> authorizationRealmValue = new InjectedValue<SecurityRealm>(); @@ -142,7 +148,7 @@ protected void performRuntime(OperationContext context, ModelNode operation, Mod .install(); } - private void addRealmDependency(OperationContext context, ServiceBuilder<SecurityRealm> serviceBuilder, String realmName, Injector<SecurityRealm> securityRealmInjector) { + private void addRealmDependency(OperationContext context, ServiceBuilder<?> serviceBuilder, String realmName, Injector<SecurityRealm> securityRealmInjector) { String runtimeCapability = RuntimeCapability.buildDynamicCapabilityName(SECURITY_REALM_CAPABILITY, realmName); ServiceName realmServiceName = context.getCapabilityServiceName(runtimeCapability, SecurityRealm.class);
elytron/src/main/java/org/wildfly/extension/elytron/ElytronDefinition.java+18 −14 modified@@ -5,7 +5,6 @@ package org.wildfly.extension.elytron; - import static org.jboss.as.server.security.VirtualDomainUtil.VIRTUAL_SECURITY_DOMAIN_CREATION_SERVICE; import static org.wildfly.extension.elytron.Capabilities.AUTHENTICATION_CONTEXT_CAPABILITY; import static org.wildfly.extension.elytron.Capabilities.ELYTRON_RUNTIME_CAPABILITY; @@ -23,6 +22,7 @@ import static org.wildfly.extension.elytron.Capabilities.SECURITY_FACTORY_CREDENTIAL_RUNTIME_CAPABILITY; import static org.wildfly.extension.elytron.Capabilities.SECURITY_REALM_RUNTIME_CAPABILITY; import static org.wildfly.extension.elytron.Capabilities.SSL_CONTEXT_CAPABILITY; +import static org.wildfly.extension.elytron.CustomComponentDefinition.wrapFunction; import static org.wildfly.extension.elytron.ElytronExtension.isServerOrHostController; import static org.wildfly.extension.elytron.ElytronScheduledExecutorService.installScheduledExecutorService; import static org.wildfly.extension.elytron.ElytronScheduledExecutorService.uninstallScheduledExecutorService; @@ -41,6 +41,7 @@ import javax.net.ssl.SSLContext; +import jakarta.security.auth.message.config.AuthConfigFactory; import org.jboss.as.controller.AbstractBoottimeAddStepHandler; import org.jboss.as.controller.AttributeMarshaller; import org.jboss.as.controller.AttributeParser; @@ -77,6 +78,7 @@ import org.jboss.msc.service.ServiceName; import org.jboss.msc.service.ServiceRegistry; import org.jboss.msc.service.ServiceTarget; +import org.wildfly.extension.elytron.RealmDefinitions.CustomRealmBruteForceTransformer; import org.wildfly.extension.elytron.capabilities.CredentialSecurityFactory; import org.wildfly.extension.elytron.capabilities.PrincipalTransformer; import org.wildfly.extension.elytron.capabilities._private.SecurityEventListener; @@ -96,8 +98,6 @@ import org.wildfly.security.jakarta.authz.AuthorizationRegistration; import org.wildfly.security.manager.action.ReadPropertyAction; -import jakarta.security.auth.message.config.AuthConfigFactory; - /** * Top level {@link ResourceDefinition} for the Elytron subsystem. * @@ -180,7 +180,7 @@ public void registerChildren(ManagementResourceRegistration resourceRegistration // Audit resourceRegistration.registerSubModel(AuditResourceDefinitions.getAggregateSecurityEventListenerDefinition()); - resourceRegistration.registerSubModel(new CustomComponentDefinition<>(Consumer.class, SecurityEventListener::from, + resourceRegistration.registerSubModel(new CustomComponentDefinition<>(Consumer.class, wrapFunction(SecurityEventListener::from), ElytronDescriptionConstants.CUSTOM_SECURITY_EVENT_LISTENER, SECURITY_EVENT_LISTENER_RUNTIME_CAPABILITY)); resourceRegistration.registerSubModel(AuditResourceDefinitions.getFileAuditLogResourceDefinition()); resourceRegistration.registerSubModel(AuditResourceDefinitions.getPeriodicRotatingFileAuditLogResourceDefinition()); @@ -197,9 +197,9 @@ public void registerChildren(ManagementResourceRegistration resourceRegistration // Security Realms resourceRegistration.registerSubModel(new AggregateRealmDefinition()); - resourceRegistration.registerSubModel(new CustomComponentDefinition<>(SecurityRealm.class, Function.identity(), ElytronDescriptionConstants.CUSTOM_REALM, SECURITY_REALM_RUNTIME_CAPABILITY)); + resourceRegistration.registerSubModel(new CustomComponentDefinition<>(SecurityRealm.class, new CustomRealmBruteForceTransformer<>(SecurityRealm.class), ElytronDescriptionConstants.CUSTOM_REALM, SECURITY_REALM_RUNTIME_CAPABILITY)); resourceRegistration.registerSubModel(ModifiableRealmDecorator.wrap(new CustomComponentDefinition<>( - ModifiableSecurityRealm.class, Function.identity(), ElytronDescriptionConstants.CUSTOM_MODIFIABLE_REALM, + ModifiableSecurityRealm.class, new CustomRealmBruteForceTransformer<>(ModifiableSecurityRealm.class), ElytronDescriptionConstants.CUSTOM_MODIFIABLE_REALM, MODIFIABLE_SECURITY_REALM_RUNTIME_CAPABILITY, SECURITY_REALM_RUNTIME_CAPABILITY))); resourceRegistration.registerSubModel(RealmDefinitions.getIdentityRealmDefinition()); resourceRegistration.registerSubModel(new JdbcRealmDefinition()); @@ -214,11 +214,11 @@ public void registerChildren(ManagementResourceRegistration resourceRegistration resourceRegistration.registerSubModel(new JaasRealmDefinition()); // Security Factories - resourceRegistration.registerSubModel(new CustomComponentDefinition<>(SecurityFactory.class, CredentialSecurityFactory::from, ElytronDescriptionConstants.CUSTOM_CREDENTIAL_SECURITY_FACTORY, SECURITY_FACTORY_CREDENTIAL_RUNTIME_CAPABILITY)); + resourceRegistration.registerSubModel(new CustomComponentDefinition<>(SecurityFactory.class, wrapFunction(CredentialSecurityFactory::from), ElytronDescriptionConstants.CUSTOM_CREDENTIAL_SECURITY_FACTORY, SECURITY_FACTORY_CREDENTIAL_RUNTIME_CAPABILITY)); resourceRegistration.registerSubModel(KerberosSecurityFactoryDefinition.getKerberosSecurityFactoryDefinition()); // Permission Mappers - resourceRegistration.registerSubModel(new CustomComponentDefinition<>(PermissionMapper.class, Function.identity(), ElytronDescriptionConstants.CUSTOM_PERMISSION_MAPPER, PERMISSION_MAPPER_RUNTIME_CAPABILITY)); + resourceRegistration.registerSubModel(new CustomComponentDefinition<>(PermissionMapper.class, ElytronDefinition::identity, ElytronDescriptionConstants.CUSTOM_PERMISSION_MAPPER, PERMISSION_MAPPER_RUNTIME_CAPABILITY)); resourceRegistration.registerSubModel(PermissionMapperDefinitions.getLogicalPermissionMapper()); resourceRegistration.registerSubModel(PermissionMapperDefinitions.getSimplePermissionMapper()); resourceRegistration.registerSubModel(PermissionMapperDefinitions.getConstantPermissionMapper()); @@ -230,26 +230,26 @@ public void registerChildren(ManagementResourceRegistration resourceRegistration resourceRegistration.registerSubModel(PrincipalDecoderDefinitions.getAggregatePrincipalDecoderDefinition()); resourceRegistration.registerSubModel(PrincipalDecoderDefinitions.getConcatenatingPrincipalDecoder()); resourceRegistration.registerSubModel(PrincipalDecoderDefinitions.getConstantPrincipalDecoder()); - resourceRegistration.registerSubModel(new CustomComponentDefinition<>(PrincipalDecoder.class, Function.identity(), ElytronDescriptionConstants.CUSTOM_PRINCIPAL_DECODER, PRINCIPAL_DECODER_RUNTIME_CAPABILITY)); + resourceRegistration.registerSubModel(new CustomComponentDefinition<>(PrincipalDecoder.class, ElytronDefinition::identity, ElytronDescriptionConstants.CUSTOM_PRINCIPAL_DECODER, PRINCIPAL_DECODER_RUNTIME_CAPABILITY)); resourceRegistration.registerSubModel(PrincipalDecoderDefinitions.getX500AttributePrincipalDecoder()); // Principal Transformers resourceRegistration.registerSubModel(PrincipalTransformerDefinitions.getAggregatePrincipalTransformerDefinition()); resourceRegistration.registerSubModel(PrincipalTransformerDefinitions.getChainedPrincipalTransformerDefinition()); resourceRegistration.registerSubModel(PrincipalTransformerDefinitions.getConstantPrincipalTransformerDefinition()); - resourceRegistration.registerSubModel(new CustomComponentDefinition<>(Function.class, PrincipalTransformer::from, ElytronDescriptionConstants.CUSTOM_PRINCIPAL_TRANSFORMER, PRINCIPAL_TRANSFORMER_RUNTIME_CAPABILITY)); + resourceRegistration.registerSubModel(new CustomComponentDefinition<>(Function.class, wrapFunction(PrincipalTransformer::from), ElytronDescriptionConstants.CUSTOM_PRINCIPAL_TRANSFORMER, PRINCIPAL_TRANSFORMER_RUNTIME_CAPABILITY)); resourceRegistration.registerSubModel(PrincipalTransformerDefinitions.getRegexPrincipalTransformerDefinition()); resourceRegistration.registerSubModel(PrincipalTransformerDefinitions.getRegexValidatingPrincipalTransformerDefinition()); resourceRegistration.registerSubModel(PrincipalTransformerDefinitions.getCasePrincipalTransformerDefinition()); // Realm Mappers resourceRegistration.registerSubModel(RealmMapperDefinitions.getConstantRealmMapper()); - resourceRegistration.registerSubModel(new CustomComponentDefinition<>(RealmMapper.class, Function.identity(), ElytronDescriptionConstants.CUSTOM_REALM_MAPPER, REALM_MAPPER_RUNTIME_CAPABILITY)); + resourceRegistration.registerSubModel(new CustomComponentDefinition<>(RealmMapper.class, ElytronDefinition::identity, ElytronDescriptionConstants.CUSTOM_REALM_MAPPER, REALM_MAPPER_RUNTIME_CAPABILITY)); resourceRegistration.registerSubModel(RealmMapperDefinitions.getMappedRegexRealmMapper()); resourceRegistration.registerSubModel(RealmMapperDefinitions.getSimpleRegexRealmMapperDefinition()); // Role Decoders - resourceRegistration.registerSubModel(new CustomComponentDefinition<>(RoleDecoder.class, Function.identity(), ElytronDescriptionConstants.CUSTOM_ROLE_DECODER, ROLE_DECODER_RUNTIME_CAPABILITY)); + resourceRegistration.registerSubModel(new CustomComponentDefinition<>(RoleDecoder.class, ElytronDefinition::identity, ElytronDescriptionConstants.CUSTOM_ROLE_DECODER, ROLE_DECODER_RUNTIME_CAPABILITY)); resourceRegistration.registerSubModel(RoleDecoderDefinitions.getSimpleRoleDecoderDefinition()); resourceRegistration.registerSubModel(RoleDecoderDefinitions.getSourceAddressRoleDecoderDefinition()); resourceRegistration.registerSubModel(RoleDecoderDefinitions.getAggregateRoleDecoderDefinition()); @@ -259,15 +259,15 @@ public void registerChildren(ManagementResourceRegistration resourceRegistration resourceRegistration.registerSubModel(RoleMapperDefinitions.getAddPrefixRoleMapperDefinition()); resourceRegistration.registerSubModel(RoleMapperDefinitions.getAggregateRoleMapperDefinition()); resourceRegistration.registerSubModel(RoleMapperDefinitions.getConstantRoleMapperDefinition()); - resourceRegistration.registerSubModel(new CustomComponentDefinition<>(RoleMapper.class, Function.identity(), ElytronDescriptionConstants.CUSTOM_ROLE_MAPPER, ROLE_MAPPER_RUNTIME_CAPABILITY)); + resourceRegistration.registerSubModel(new CustomComponentDefinition<>(RoleMapper.class, ElytronDefinition::identity, ElytronDescriptionConstants.CUSTOM_ROLE_MAPPER, ROLE_MAPPER_RUNTIME_CAPABILITY)); resourceRegistration.registerSubModel(RoleMapperDefinitions.getLogicalRoleMapperDefinition()); resourceRegistration.registerSubModel(RoleMapperDefinitions.getMappedRoleMapperDefinition()); resourceRegistration.registerSubModel(RoleMapperDefinitions.getRegexRoleMapperDefinition()); // Evidence Decoders resourceRegistration.registerSubModel(EvidenceDecoderDefinitions.getX500SubjectEvidenceDecoderDefinition()); resourceRegistration.registerSubModel(EvidenceDecoderDefinitions.getX509SubjectAltNameEvidenceDecoderDefinition()); - resourceRegistration.registerSubModel(new CustomComponentDefinition<>(EvidenceDecoder.class, Function.identity(), ElytronDescriptionConstants.CUSTOM_EVIDENCE_DECODER, EVIDENCE_DECODER_RUNTIME_CAPABILITY)); + resourceRegistration.registerSubModel(new CustomComponentDefinition<>(EvidenceDecoder.class, ElytronDefinition::identity, ElytronDescriptionConstants.CUSTOM_EVIDENCE_DECODER, EVIDENCE_DECODER_RUNTIME_CAPABILITY)); resourceRegistration.registerSubModel(EvidenceDecoderDefinitions.getAggregateEvidenceDecoderDefinition()); // HTTP Mechanisms @@ -363,6 +363,10 @@ protected void revertUpdateToRuntime(OperationContext context, ModelNode operati }); } + private static <T, U> U identity(T t, U u) { + return u; + } + @Override public void registerAdditionalRuntimePackages(ManagementResourceRegistration resourceRegistration) { resourceRegistration.registerAdditionalRuntimePackages(RuntimePackageDependency.required("org.wildfly.security.elytron"));
elytron/src/main/java/org/wildfly/extension/elytron/FailoverRealmDefinition.java+12 −5 modified@@ -8,7 +8,7 @@ import static org.wildfly.extension.elytron.Capabilities.SECURITY_REALM_CAPABILITY; import static org.wildfly.extension.elytron.Capabilities.SECURITY_REALM_RUNTIME_CAPABILITY; import static org.wildfly.extension.elytron.ElytronDefinition.commonDependencies; - +import static org.wildfly.extension.elytron.RealmDefinitions.createBruteForceRealmTransformer; import org.jboss.as.controller.AbstractAddStepHandler; import org.jboss.as.controller.AttributeDefinition; @@ -40,6 +40,7 @@ import org.wildfly.security.auth.server.event.SecurityRealmUnavailableEvent; import java.util.function.Consumer; +import java.util.function.Function; /** * A {@link ResourceDefinition} for a {@link SecurityRealm} which wraps one realm and fails over to another in case the first is unavailable. @@ -111,6 +112,12 @@ protected void performRuntime(OperationContext context, ModelNode operation, Mod boolean emitEvents = EMIT_EVENTS.resolveModelAttribute(context, model).asBoolean(); + ServiceBuilder<?> serviceBuilder = serviceTarget.addService(); + Consumer<SecurityRealm> valueConsumer = serviceBuilder.provides(realmName); + + final Function<SecurityRealm, SecurityRealm> realmTransformer = + createBruteForceRealmTransformer(context.getCurrentAddressValue(), SecurityRealm.class, serviceBuilder); + TrivialService<SecurityRealm> failoverRealmService = new TrivialService<SecurityRealm>(() -> { SecurityRealm delegate = delegateRealmValue.getValue(); @@ -120,10 +127,10 @@ protected void performRuntime(OperationContext context, ModelNode operation, Mod domain.handleSecurityEvent(new SecurityRealmUnavailableEvent(domain.getCurrentSecurityIdentity(), delegateRealm)); } } : (e) -> {}; - return new FailoverSecurityRealm(delegate, failoverRealmValue.getValue(), failoverConsumer); - }); + return realmTransformer.apply(new FailoverSecurityRealm(delegate, failoverRealmValue.getValue(), failoverConsumer)); + }, valueConsumer); - ServiceBuilder<SecurityRealm> serviceBuilder = serviceTarget.addService(realmName, failoverRealmService); + serviceBuilder.setInstance(failoverRealmService); addRealmDependency(context, serviceBuilder, delegateRealm, delegateRealmValue); addRealmDependency(context, serviceBuilder, failoverRealm, failoverRealmValue); @@ -133,7 +140,7 @@ protected void performRuntime(OperationContext context, ModelNode operation, Mod .install(); } - private void addRealmDependency(OperationContext context, ServiceBuilder<SecurityRealm> serviceBuilder, String realmName, Injector<SecurityRealm> securityRealmInjector) { + private void addRealmDependency(OperationContext context, ServiceBuilder<?> serviceBuilder, String realmName, Injector<SecurityRealm> securityRealmInjector) { String runtimeCapability = RuntimeCapability.buildDynamicCapabilityName(SECURITY_REALM_CAPABILITY, realmName); ServiceName realmServiceName = context.getCapabilityServiceName(runtimeCapability, SecurityRealm.class);
elytron/src/main/java/org/wildfly/extension/elytron/FileSystemRealmDefinition.java+21 −5 modified@@ -20,6 +20,7 @@ import static org.wildfly.extension.elytron.FileAttributeDefinitions.pathName; import static org.wildfly.extension.elytron.FileAttributeDefinitions.pathResolver; import static org.wildfly.extension.elytron.KeyStoreServiceUtil.getModifiableKeyStoreService; +import static org.wildfly.extension.elytron.RealmDefinitions.createBruteForceRealmTransformer; import static org.wildfly.extension.elytron._private.ElytronSubsystemMessages.ROOT_LOGGER; import java.io.IOException; @@ -31,6 +32,8 @@ import java.security.PrivateKey; import java.security.PublicKey; import java.security.UnrecoverableKeyException; +import java.util.function.Consumer; +import java.util.function.Function; import java.util.stream.Stream; import javax.crypto.SecretKey; @@ -72,6 +75,7 @@ import org.wildfly.extension.elytron.FileAttributeDefinitions.PathResolver; import org.wildfly.security.auth.realm.FileSystemSecurityRealm; import org.wildfly.security.auth.realm.FileSystemSecurityRealmBuilder; +import org.wildfly.security.auth.server.ModifiableSecurityRealm; import org.wildfly.security.auth.server.NameRewriter; import org.wildfly.security.auth.server.RealmUnavailableException; import org.wildfly.security.auth.server.SecurityRealm; @@ -299,8 +303,8 @@ protected void performRuntime(OperationContext context, ModelNode operation, Mod ServiceTarget serviceTarget = context.getServiceTarget(); String address = context.getCurrentAddressValue(); - ServiceName mainServiceName = MODIFIABLE_SECURITY_REALM_RUNTIME_CAPABILITY.fromBaseCapability(address).getCapabilityServiceName(); - ServiceName aliasServiceName = SECURITY_REALM_RUNTIME_CAPABILITY.fromBaseCapability(address).getCapabilityServiceName(); + ServiceName modifiableServiceName = MODIFIABLE_SECURITY_REALM_RUNTIME_CAPABILITY.fromBaseCapability(address).getCapabilityServiceName(); + ServiceName standardServiceName = SECURITY_REALM_RUNTIME_CAPABILITY.fromBaseCapability(address).getCapabilityServiceName(); final int levels = LEVELS.resolveModelAttribute(context, model).asInt(); @@ -327,6 +331,15 @@ protected void performRuntime(OperationContext context, ModelNode operation, Mod final SecretKey finalKey = key; ServiceRegistry keyStoreServiceRegistry = context.getServiceRegistry(true); + ServiceBuilder<?> serviceBuilder = serviceTarget.addService(); + // This is the Service that will get pulled into a SecurityDomain etc... + Consumer<SecurityRealm> standardConsumer = serviceBuilder.provides(standardServiceName); + // This is the modifiable variant for resources that support modification operations etc.. + Consumer<ModifiableSecurityRealm> modifiableConsumer = serviceBuilder.provides(modifiableServiceName); + + Function<ModifiableSecurityRealm, ModifiableSecurityRealm> realmTransformer = + createBruteForceRealmTransformer(context.getCurrentAddressValue(), ModifiableSecurityRealm.class, serviceBuilder); + TrivialService<SecurityRealm> fileSystemRealmService = new TrivialService<>( new TrivialService.ValueSupplier<SecurityRealm>() { @@ -382,8 +395,12 @@ public SecurityRealm get() throws StartException { fileSystemRealmBuilder.setPrivateKey(privateKey); fileSystemRealmBuilder.setPublicKey(publicKey); } - return fileSystemRealmBuilder.build(); + ModifiableSecurityRealm modifiable = fileSystemRealmBuilder.build(); + ModifiableSecurityRealm wrapped = realmTransformer.apply(modifiable); + modifiableConsumer.accept(wrapped); + standardConsumer.accept(wrapped); + return modifiable; } @Override @@ -396,8 +413,6 @@ public void dispose() { }); - ServiceBuilder<SecurityRealm> serviceBuilder = serviceTarget.addService(mainServiceName, fileSystemRealmService) - .addAliases(aliasServiceName); if (credentialStore != null) { serviceBuilder.requires(context.getCapabilityServiceName(buildDynamicCapabilityName(CREDENTIAL_STORE_CAPABILITY, credentialStore), CredentialStore.class)); } @@ -410,6 +425,7 @@ public void dispose() { serviceBuilder.addDependency(PathManagerService.SERVICE_NAME, PathManager.class, pathManagerInjector); serviceBuilder.requires(pathName(relativeTo)); } + serviceBuilder.setInstance(fileSystemRealmService); serviceBuilder.install(); }
elytron/src/main/java/org/wildfly/extension/elytron/JaasRealmDefinition.java+26 −17 modified@@ -5,6 +5,22 @@ package org.wildfly.extension.elytron; +import static org.wildfly.extension.elytron.Capabilities.SECURITY_REALM_RUNTIME_CAPABILITY; +import static org.wildfly.extension.elytron.ClassLoadingAttributeDefinitions.resolveClassLoader; +import static org.wildfly.extension.elytron.ElytronDefinition.commonDependencies; +import static org.wildfly.extension.elytron.FileAttributeDefinitions.pathName; +import static org.wildfly.extension.elytron.FileAttributeDefinitions.pathResolver; +import static org.wildfly.extension.elytron.RealmDefinitions.createBruteForceRealmTransformer; +import static org.wildfly.extension.elytron.SecurityActions.doPrivileged; +import static org.wildfly.extension.elytron._private.ElytronSubsystemMessages.ROOT_LOGGER; + +import java.io.File; +import java.security.PrivilegedExceptionAction; +import java.util.function.Consumer; +import java.util.function.Function; + +import javax.security.auth.callback.CallbackHandler; + import org.jboss.as.controller.AbstractAddStepHandler; import org.jboss.as.controller.AttributeDefinition; import org.jboss.as.controller.OperationContext; @@ -31,18 +47,6 @@ import org.wildfly.security.auth.realm.JaasSecurityRealm; import org.wildfly.security.auth.server.SecurityRealm; -import javax.security.auth.callback.CallbackHandler; -import java.io.File; -import java.security.PrivilegedExceptionAction; - -import static org.wildfly.extension.elytron.Capabilities.SECURITY_REALM_RUNTIME_CAPABILITY; -import static org.wildfly.extension.elytron.ClassLoadingAttributeDefinitions.resolveClassLoader; -import static org.wildfly.extension.elytron.ElytronDefinition.commonDependencies; -import static org.wildfly.extension.elytron.FileAttributeDefinitions.pathName; -import static org.wildfly.extension.elytron.FileAttributeDefinitions.pathResolver; -import static org.wildfly.extension.elytron.SecurityActions.doPrivileged; -import static org.wildfly.extension.elytron._private.ElytronSubsystemMessages.ROOT_LOGGER; - /** * A {@link ResourceDefinition} for a {@link SecurityRealm} backed by a JAAS LoginContext. */ @@ -127,6 +131,13 @@ protected void performRuntime(OperationContext context, ModelNode operation, Mod final InjectedValue<PathManager> pathManagerInjector = new InjectedValue<>(); + ServiceName realmName = runtimeCapability.getCapabilityServiceName(SecurityRealm.class); + ServiceBuilder<?> serviceBuilder = serviceTarget.addService(); + Consumer<SecurityRealm> realmConsumer = serviceBuilder.provides(realmName); + + Function<SecurityRealm, SecurityRealm> realmTransformer = + createBruteForceRealmTransformer(context.getCurrentAddressValue(), SecurityRealm.class, serviceBuilder); + CallbackHandler finalCallbackHandler = callbackhandler; TrivialService<SecurityRealm> jaasRealmService = new TrivialService<>( new TrivialService.ValueSupplier<SecurityRealm>() { @@ -143,7 +154,7 @@ public SecurityRealm get() throws StartException { } rootPath = jaasConfigFile.getPath(); } - return new JaasSecurityRealm(entryName, rootPath, classLoader, finalCallbackHandler); + return realmTransformer.apply(new JaasSecurityRealm(entryName, rootPath, classLoader, finalCallbackHandler)); } @Override @@ -153,17 +164,15 @@ public void dispose() { pathResolver = null; } } - }); - - ServiceName realmName = runtimeCapability.getCapabilityServiceName(SecurityRealm.class); - ServiceBuilder<SecurityRealm> serviceBuilder = serviceTarget.addService(realmName, jaasRealmService); + }, realmConsumer); if (relativeTo != null) { serviceBuilder.addDependency(PathManagerService.SERVICE_NAME, PathManager.class, pathManagerInjector); serviceBuilder.requires(pathName(relativeTo)); } commonDependencies(serviceBuilder) + .setInstance(jaasRealmService) .setInitialMode(ServiceController.Mode.ACTIVE) .install(); }
elytron/src/main/java/org/wildfly/extension/elytron/JdbcRealmDefinition.java+11 −2 modified@@ -16,6 +16,7 @@ import static org.wildfly.extension.elytron.ElytronDescriptionConstants.SCRAM_MAPPER; import static org.wildfly.extension.elytron.ElytronDescriptionConstants.SIMPLE_DIGEST_MAPPER; import static org.wildfly.extension.elytron.ElytronDescriptionConstants.UTF_8; +import static org.wildfly.extension.elytron.RealmDefinitions.createBruteForceRealmTransformer; import static org.wildfly.extension.elytron._private.ElytronSubsystemMessages.ROOT_LOGGER; import java.nio.charset.Charset; @@ -24,6 +25,8 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.function.Consumer; +import java.util.function.Function; import javax.sql.DataSource; @@ -612,8 +615,13 @@ protected void performRuntime(OperationContext context, ModelNode operation, Mod final JdbcSecurityRealmBuilder builder = JdbcSecurityRealm.builder(); builder.setHashCharset(charset); - TrivialService<SecurityRealm> service = new TrivialService<SecurityRealm>(builder::build); - ServiceBuilder<SecurityRealm> serviceBuilder = serviceTarget.addService(realmName, service); + ServiceBuilder<?> serviceBuilder = serviceTarget.addService(); + Consumer<SecurityRealm> valueConsumer = serviceBuilder.provides(realmName); + + Function<SecurityRealm, SecurityRealm> realmTransformer = + createBruteForceRealmTransformer(context.getCurrentAddressValue(), SecurityRealm.class, serviceBuilder); + + TrivialService<SecurityRealm> service = new TrivialService<SecurityRealm>(() -> realmTransformer.apply(builder.build()), valueConsumer); for (ModelNode query : principalQueries.asList()) { String authenticationQuerySql = PrincipalQueryAttributes.SQL.resolveModelAttribute(context, query).asString(); @@ -639,6 +647,7 @@ public void uninject() { }); } + serviceBuilder.setInstance(service); commonDependencies(serviceBuilder) .setInitialMode(context.getRunningMode() == RunningMode.ADMIN_ONLY ? ServiceController.Mode.LAZY : ServiceController.Mode.ACTIVE) .install();
elytron/src/main/java/org/wildfly/extension/elytron/LdapRealmDefinition.java+26 −6 modified@@ -14,11 +14,14 @@ import static org.wildfly.extension.elytron.ElytronDescriptionConstants.HEX; import static org.wildfly.extension.elytron.ElytronDescriptionConstants.UTF_8; import static org.wildfly.extension.elytron._private.ElytronSubsystemMessages.ROOT_LOGGER; +import static org.wildfly.extension.elytron.RealmDefinitions.createBruteForceRealmTransformer; import java.nio.charset.Charset; import java.util.Arrays; import java.util.List; import java.util.Set; +import java.util.function.Consumer; +import java.util.function.Function; import javax.naming.InvalidNameException; import javax.naming.NamingException; @@ -59,6 +62,7 @@ import org.wildfly.security.auth.realm.ldap.AttributeMapping; import org.wildfly.security.auth.realm.ldap.LdapSecurityRealmBuilder; import org.wildfly.security.auth.realm.ldap.LdapSecurityRealmBuilder.IdentityMappingBuilder; +import org.wildfly.security.auth.server.ModifiableSecurityRealm; import org.wildfly.security.auth.server.SecurityRealm; import org.wildfly.security.password.spec.Encoding; @@ -435,8 +439,8 @@ protected void performRuntime(OperationContext context, ModelNode operation, Mod ServiceTarget serviceTarget = context.getServiceTarget(); String address = context.getCurrentAddressValue(); - ServiceName mainServiceName = MODIFIABLE_SECURITY_REALM_RUNTIME_CAPABILITY.fromBaseCapability(address).getCapabilityServiceName(); - ServiceName aliasServiceName = SECURITY_REALM_RUNTIME_CAPABILITY.fromBaseCapability(address).getCapabilityServiceName(); + ServiceName modifiableServiceName = MODIFIABLE_SECURITY_REALM_RUNTIME_CAPABILITY.fromBaseCapability(address).getCapabilityServiceName(); + ServiceName standardServiceName = SECURITY_REALM_RUNTIME_CAPABILITY.fromBaseCapability(address).getCapabilityServiceName(); final LdapSecurityRealmBuilder builder = LdapSecurityRealmBuilder.builder(); @@ -455,10 +459,26 @@ protected void performRuntime(OperationContext context, ModelNode operation, Mod builder.setHashEncoding(HEX.equals(hashEncoding) ? Encoding.HEX : Encoding.BASE64); builder.setHashCharset(charset); - TrivialService<SecurityRealm> ldapRealmService = new TrivialService<>(builder::build); - ServiceBuilder<SecurityRealm> serviceBuilder = serviceTarget.addService(mainServiceName, ldapRealmService) - .addAliases(aliasServiceName); + ServiceBuilder<?> serviceBuilder = serviceTarget.addService(); + // This is the Service that will get pulled into a SecurityDomain etc... + Consumer<SecurityRealm> standardConsumer = serviceBuilder.provides(standardServiceName); + // This is the modifiable variant for resources that support modification operations etc.. + Consumer<ModifiableSecurityRealm> modifiableConsumer = serviceBuilder.provides(modifiableServiceName); + Function<ModifiableSecurityRealm, ModifiableSecurityRealm> realmTransformer = + createBruteForceRealmTransformer(context.getCurrentAddressValue(), ModifiableSecurityRealm.class, serviceBuilder); + + TrivialService<SecurityRealm> ldapRealmService = + new TrivialService<>(() -> { + ModifiableSecurityRealm modifiable = builder.build(); + ModifiableSecurityRealm wrapped = realmTransformer.apply(modifiable); + modifiableConsumer.accept(wrapped); + standardConsumer.accept(wrapped); + + return modifiable; + }); + + serviceBuilder.setInstance(ldapRealmService); commonDependencies(serviceBuilder); configureIdentityMapping(context, model, builder); @@ -467,7 +487,7 @@ protected void performRuntime(OperationContext context, ModelNode operation, Mod serviceBuilder.setInitialMode(ServiceController.Mode.ACTIVE).install(); } - private void configureDirContext(OperationContext context, ModelNode model, LdapSecurityRealmBuilder realmBuilder, ServiceBuilder<SecurityRealm> serviceBuilder) throws OperationFailedException { + private void configureDirContext(OperationContext context, ModelNode model, LdapSecurityRealmBuilder realmBuilder, ServiceBuilder<?> serviceBuilder) throws OperationFailedException { String dirContextName = DIR_CONTEXT.resolveModelAttribute(context, model).asStringOrNull(); String runtimeCapability = RuntimeCapability.buildDynamicCapabilityName(DIR_CONTEXT_CAPABILITY, dirContextName);
elytron/src/main/java/org/wildfly/extension/elytron/PropertiesRealmDefinition.java+22 −8 modified@@ -5,6 +5,7 @@ package org.wildfly.extension.elytron; + import static org.wildfly.extension.elytron.Capabilities.SECURITY_REALM_RUNTIME_CAPABILITY; import static org.wildfly.extension.elytron.ElytronDescriptionConstants.BASE64; import static org.wildfly.extension.elytron.ElytronDescriptionConstants.HEX; @@ -13,6 +14,7 @@ import static org.wildfly.extension.elytron.ElytronExtension.getRequiredService; import static org.wildfly.extension.elytron.FileAttributeDefinitions.RELATIVE_TO; import static org.wildfly.extension.elytron.FileAttributeDefinitions.pathName; +import static org.wildfly.extension.elytron.RealmDefinitions.createBruteForceRealmTransformer; import static org.wildfly.extension.elytron.SecurityActions.doPrivileged; import static org.wildfly.extension.elytron._private.ElytronSubsystemMessages.ROOT_LOGGER; @@ -29,6 +31,8 @@ import java.util.ArrayList; import java.util.Date; import java.util.List; +import java.util.function.Function; +import java.util.function.LongSupplier; import org.jboss.as.controller.AbstractAddStepHandler; import org.jboss.as.controller.AttributeDefinition; @@ -58,6 +62,7 @@ import org.jboss.msc.service.ServiceName; import org.jboss.msc.service.StartException; import org.jboss.msc.value.InjectedValue; +import org.wildfly.common.function.ExceptionBiConsumer; import org.wildfly.extension.elytron.TrivialResourceDefinition.Builder; import org.wildfly.extension.elytron.TrivialService.ValueSupplier; import org.wildfly.security.auth.SupportLevel; @@ -181,6 +186,8 @@ protected ValueSupplier<SecurityRealm> getValueSupplier(ServiceBuilder<SecurityR } } + Function<SecurityRealm, SecurityRealm> realmTransformer = + createBruteForceRealmTransformer(context.getCurrentAddressValue(), SecurityRealm.class, serviceBuilder); return new ValueSupplier<SecurityRealm>() { private final List<Handle> callbackHandles = new ArrayList<>(); @@ -192,15 +199,17 @@ public SecurityRealm get() throws StartException { try (InputStream usersInputStream = new FileInputStream(usersFile); InputStream groupsInputStream = groupsFile != null ? new FileInputStream(groupsFile) : null) { - return new RealmWrapper(LegacyPropertiesSecurityRealm.builder() + LegacyPropertiesSecurityRealm baseRealm = LegacyPropertiesSecurityRealm.builder() .setUsersStream(usersInputStream) .setGroupsStream(groupsInputStream) .setPlainText(plainText) .setGroupsAttribute(groupsAttribute) .setDefaultRealm(digestRealmName) .setHashEncoding(BASE64.equalsIgnoreCase(hashEncoding) ? Encoding.BASE64 : Encoding.HEX) .setHashCharset(Charset.forName(hashCharset)) - .build(), usersFile, groupsFile); + .build(); + + return new RealmWrapper(realmTransformer.apply(baseRealm), usersFile, groupsFile, baseRealm::getLoadTime, baseRealm::load); } catch (FileNotFoundException e) { throw ROOT_LOGGER.propertyFilesDoesNotExist(e.getMessage()); @@ -310,14 +319,19 @@ protected void executeRuntimeStep(OperationContext context, ModelNode operation) private static final class RealmWrapper implements SecurityRealm { - private final LegacyPropertiesSecurityRealm delegate; + private final SecurityRealm delegate; private final File usersFile; private final File groupsFile; + private final LongSupplier loadTimeSupplier; + private final ExceptionBiConsumer<InputStream, InputStream, IOException> propertiesFileLoader; - RealmWrapper(LegacyPropertiesSecurityRealm delegate, File usersFile, File groupsFile) { + RealmWrapper(SecurityRealm delegate, File usersFile, File groupsFile, LongSupplier loadTimeSupplier, + ExceptionBiConsumer<InputStream, InputStream, IOException> propertiesFileLoader) { this.delegate = delegate; this.usersFile = usersFile; this.groupsFile = groupsFile; + this.loadTimeSupplier = loadTimeSupplier; + this.propertiesFileLoader = propertiesFileLoader; } @Override @@ -364,14 +378,14 @@ public void handleRealmEvent(RealmEvent event) { } long getLoadTime() { - return delegate.getLoadTime(); + return loadTimeSupplier.getAsLong(); } void reloadIfNeeded() throws IOException { - long loadTime = delegate.getLoadTime(); + long loadTime = loadTimeSupplier.getAsLong(); if (shouldReload(loadTime)) { synchronized(this) { - loadTime = delegate.getLoadTime(); + loadTime = loadTimeSupplier.getAsLong(); if (shouldReload(loadTime)) { reloadInternal(); } @@ -394,7 +408,7 @@ void reload() throws OperationFailedException { void reloadInternal() throws IOException { try (InputStream usersInputStream = new FileInputStream(usersFile); InputStream groupsInputStream = groupsFile != null ? new FileInputStream(groupsFile) : null) { - delegate.load(usersInputStream, groupsInputStream); + propertiesFileLoader.accept(usersInputStream, groupsInputStream); } }
elytron/src/main/java/org/wildfly/extension/elytron/RealmDefinitions.java+86 −0 modified@@ -4,12 +4,18 @@ */ package org.wildfly.extension.elytron; +import static org.wildfly.extension.elytron.Capabilities.SCHEDULED_EXECUTOR_RUNTIME_CAPABILITY; import static org.wildfly.extension.elytron.Capabilities.SECURITY_REALM_RUNTIME_CAPABILITY; +import static org.wildfly.extension.elytron._private.ElytronSubsystemMessages.ROOT_LOGGER; +import static org.wildfly.security.manager.WildFlySecurityManager.getPropertyPrivileged; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.concurrent.ScheduledExecutorService; +import java.util.function.Function; +import java.util.function.Supplier; import org.jboss.as.controller.AbstractAddStepHandler; import org.jboss.as.controller.AttributeDefinition; @@ -22,6 +28,7 @@ import org.jboss.dmr.ModelType; import org.jboss.msc.service.ServiceBuilder; import org.wildfly.extension.elytron.TrivialService.ValueSupplier; +import org.wildfly.security.auth.realm.BruteForceRealmWrapper; import org.wildfly.security.auth.realm.SimpleMapBackedSecurityRealm; import org.wildfly.security.auth.realm.SimpleRealmEntry; import org.wildfly.security.auth.server.SecurityRealm; @@ -33,6 +40,15 @@ */ class RealmDefinitions { + /** + * System properties used to configure brute force protection on the security realms. + */ + private static final String BRUTE_FORCE_ENABLED = "wildfly.elytron.realm.%s.brute-force.enabled"; + private static final String BRUTE_FORCE_MAX_FAILED_ATTEMPTS = "wildfly.elytron.realm.%s.brute-force.max-failed-attempts"; + private static final String BRUTE_FORCE_LOCKOUT_INTERVAL = "wildfly.elytron.realm.%s.brute-force.lockout-interval"; + private static final String BRUTE_FORCE_SESSION_TIMEOUT = "wildfly.elytron.realm.%s.brute-force.session-timeout"; + private static final String BRUTE_FORCE_MAX_CACHED_SESSIONS = "wildfly.elytron.realm.%s.brute-force.max-cached-sessions"; + static final AttributeDefinition IDENTITY = new SimpleAttributeDefinitionBuilder(ElytronDescriptionConstants.IDENTITY, ModelType.STRING, false) .setAllowExpression(true) .setMinSize(1) @@ -83,4 +99,74 @@ protected ValueSupplier<SecurityRealm> getValueSupplier(ServiceBuilder<SecurityR return new TrivialResourceDefinition(ElytronDescriptionConstants.IDENTITY_REALM, add, IDENTITY_REALM_ATTRIBUTES, SECURITY_REALM_RUNTIME_CAPABILITY); } + + private static boolean isBruteForceProtectionEnabled(final String realmName) { + return Boolean.parseBoolean(getPropertyPrivileged(String.format(BRUTE_FORCE_ENABLED, realmName), "true")); + } + + private static int getBruteForceConfigValue(final String realmName, final String systemPropertyTemplate) { + try { + return Integer.parseInt(getPropertyPrivileged(String.format(systemPropertyTemplate, realmName), "-1")); + } catch (NumberFormatException e) { + return -1; + } + } + + static <T extends SecurityRealm> T addBruteForceProtection(final T original, final Class<T> clazz, + final ScheduledExecutorService executor, final String realmName, final int maxAttempts, + final int lockoutInterval, final int sessionTimeout, final int maxCachedSessions) { + + return BruteForceRealmWrapper.create() + .wrapping(original) + .withExecutor(executor) + .setRealmName(realmName) + .setMaxFailedAttempts(maxAttempts) + .setLockoutInterval(lockoutInterval) + .setFailureSessionTimeout(sessionTimeout) + .setMaxCachedSessions(maxCachedSessions) + .wrap(clazz); + } + + static <T extends SecurityRealm> Function<T, T> createBruteForceRealmTransformer(String name, Class<T> clazz, ServiceBuilder<?> serviceBuilder) { + Function<T, T> transformer; + if (isBruteForceProtectionEnabled(name)) { + final Supplier<ScheduledExecutorService> executorSupplier = + serviceBuilder.requires(SCHEDULED_EXECUTOR_RUNTIME_CAPABILITY.getCapabilityServiceName()); + int maxAttempts = getBruteForceConfigValue(name, BRUTE_FORCE_MAX_FAILED_ATTEMPTS); + int lockoutInterval = getBruteForceConfigValue(name, BRUTE_FORCE_LOCKOUT_INTERVAL); + int sessionTimeout = getBruteForceConfigValue(name, BRUTE_FORCE_SESSION_TIMEOUT); + int maxCachedSessions = getBruteForceConfigValue(name, BRUTE_FORCE_MAX_CACHED_SESSIONS); + + ROOT_LOGGER.tracef("Applying brute force protection to '%s' security realm. maxAttempts=%d, lockoutTimeout=%d, sessionTimeout=%d, maxCachedSessions=%d", + name, maxAttempts, lockoutInterval, sessionTimeout, maxCachedSessions); + + transformer = (r) -> addBruteForceProtection(r, clazz, executorSupplier.get(), name, maxAttempts, lockoutInterval, sessionTimeout, maxCachedSessions); + } else { + ROOT_LOGGER.tracef("Not applying brute force protection to '%s' security realm.", name); + transformer = Function.identity(); + } + + return transformer; + } + + static class CustomRealmBruteForceTransformer<T extends SecurityRealm> implements CustomComponentDefinition.CustomComponentTransformer<T, T> { + + private final Class<T> securityRealmClazz; + + CustomRealmBruteForceTransformer(Class<T> securityRealmClazz) { + this.securityRealmClazz = securityRealmClazz; + } + + @Override + public Object prepareTransformer(String name, ServiceBuilder<?> serviceBuilder) { + return createBruteForceRealmTransformer(name, securityRealmClazz, serviceBuilder); + } + + @Override + public T apply(Object o, T securityRealm) { + return ((Function<T, T>) o).apply(securityRealm); + } + + } + }
elytron/src/test/java/org/wildfly/extension/elytron/RealmsTestCase.java+2 −2 modified@@ -625,10 +625,10 @@ public void testJAASRealm() throws Exception { Assert.fail(services.getBootError().toString()); } - JaasSecurityRealm securityRealm; + SecurityRealm securityRealm; if (!(JdkUtils.isIbmJdk())) { ServiceName serviceName = Capabilities.SECURITY_REALM_RUNTIME_CAPABILITY.getCapabilityServiceName("myJaasRealm"); - securityRealm = (JaasSecurityRealm) services.getContainer().getService(serviceName).getValue(); + securityRealm = (SecurityRealm) services.getContainer().getService(serviceName).getValue(); Assert.assertNotNull(securityRealm); } else { // IBM JDK 8 does not recognize default policy type "JavaLoginConfig" so the path to JAAS configuration file must be provided via system property
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
13- www.gruppotim.it/it/footer/red-team.htmlnvdExploitThird Party AdvisoryWEB
- access.redhat.com/security/cve/CVE-2025-23368nvdVendor AdvisoryWEB
- bugzilla.redhat.com/show_bug.cginvdVendor AdvisoryWEB
- github.com/advisories/GHSA-qhp6-6p8p-2rqhghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2025-23368ghsaADVISORY
- github.com/wildfly/wildfly-core/commit/11e873031c522a0b36afb59880ce4dd59efd0bc0ghsaWEB
- github.com/wildfly/wildfly-core/commit/a6f9d7534aa44de741337756f8377ad3a81f7695ghsaWEB
- github.com/wildfly/wildfly-core/pull/6634ghsaWEB
- github.com/wildfly/wildfly-core/pull/6635ghsaWEB
- github.com/wildfly/wildfly-core/security/advisories/GHSA-qhp6-6p8p-2rqhghsaWEB
- access.redhat.com/errata/RHSA-2026:18054nvd
- access.redhat.com/errata/RHSA-2026:18055nvd
- access.redhat.com/errata/RHSA-2026:18059nvd
News mentions
0No linked articles in our index yet.