Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[WFLY-5774] Add security domain to Elytron mappings to the Undertow s…
…ubsystem and use these for authentication if available. If no mapping exists we continue to use the existing PicketBox security domains.
- Loading branch information
Showing
13 changed files
with
433 additions
and
25 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
278 changes: 278 additions & 0 deletions
278
...tow/src/main/java/org/wildfly/extension/undertow/ApplicationSecurityDomainDefinition.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,278 @@ | ||
/* | ||
* JBoss, Home of Professional Open Source. | ||
* Copyright 2015, Red Hat, Inc., and individual contributors | ||
* as indicated by the @author tags. See the copyright.txt file in the | ||
* distribution for a full listing of individual contributors. | ||
* | ||
* This is free software; you can redistribute it and/or modify it | ||
* under the terms of the GNU Lesser General Public License as | ||
* published by the Free Software Foundation; either version 2.1 of | ||
* the License, or (at your option) any later version. | ||
* | ||
* This software is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
* Lesser General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU Lesser General Public | ||
* License along with this software; if not, write to the Free | ||
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
* 02110-1301 USA, or see the FSF site: http://www.fsf.org. | ||
*/ | ||
|
||
package org.wildfly.extension.undertow; | ||
|
||
import static org.jboss.as.controller.capability.RuntimeCapability.buildDynamicCapabilityName; | ||
|
||
import java.util.ArrayList; | ||
import java.util.Arrays; | ||
import java.util.Collection; | ||
import java.util.Collections; | ||
import java.util.HashSet; | ||
import java.util.List; | ||
import java.util.Set; | ||
import java.util.function.Function; | ||
import java.util.function.Predicate; | ||
import java.util.function.Supplier; | ||
import java.util.stream.Collectors; | ||
|
||
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.PathElement; | ||
import org.jboss.as.controller.PersistentResourceDefinition; | ||
import org.jboss.as.controller.ResourceDefinition; | ||
import org.jboss.as.controller.ServiceRemoveStepHandler; | ||
import org.jboss.as.controller.SimpleAttributeDefinition; | ||
import org.jboss.as.controller.SimpleAttributeDefinitionBuilder; | ||
import org.jboss.as.controller.capability.RuntimeCapability; | ||
import org.jboss.as.controller.registry.AttributeAccess; | ||
import org.jboss.as.controller.registry.Resource; | ||
import org.jboss.dmr.ModelNode; | ||
import org.jboss.dmr.ModelType; | ||
import org.jboss.msc.inject.Injector; | ||
import org.jboss.msc.service.Service; | ||
import org.jboss.msc.service.ServiceBuilder; | ||
import org.jboss.msc.service.ServiceController.Mode; | ||
import org.jboss.msc.service.ServiceName; | ||
import org.jboss.msc.service.StartContext; | ||
import org.jboss.msc.service.StartException; | ||
import org.jboss.msc.service.StopContext; | ||
import org.jboss.msc.value.InjectedValue; | ||
import org.wildfly.elytron.web.undertow.server.ElytronContextAssociationHandler; | ||
import org.wildfly.elytron.web.undertow.server.ElytronRunAsHandler; | ||
import org.wildfly.security.auth.server.HttpAuthenticationFactory; | ||
import org.wildfly.security.http.HttpAuthenticationException; | ||
import org.wildfly.security.http.HttpServerAuthenticationMechanism; | ||
|
||
import io.undertow.server.HttpHandler; | ||
import io.undertow.server.handlers.BlockingHandler; | ||
import io.undertow.servlet.api.DeploymentInfo; | ||
|
||
/** | ||
* A {@link ResourceDefinition} to define the mapping from a security domain as specified in a web application | ||
* to an {@link HttpAuthenticationFactory} plus additional policy information. | ||
* | ||
* @author <a href="mailto:darran.lofthouse@jboss.com">Darran Lofthouse</a> | ||
*/ | ||
public class ApplicationSecurityDomainDefinition extends PersistentResourceDefinition { | ||
|
||
public static final String APPLICATION_SECURITY_DOMAIN_CAPABILITY = "org.wildfly.extension.undertow.application-security-domain"; | ||
|
||
static final RuntimeCapability<Void> APPLICATION_SECURITY_DOMAIN_RUNTIME_CAPABILITY = RuntimeCapability | ||
.Builder.of(APPLICATION_SECURITY_DOMAIN_CAPABILITY, true, Function.class) | ||
.build(); | ||
|
||
private static final String HTTP_AUTHENITCATION_FACTORY_CAPABILITY = "org.wildfly.security.http-server-authentication"; | ||
|
||
static SimpleAttributeDefinition HTTP_SERVER_MECHANISM_FACTORY = new SimpleAttributeDefinitionBuilder(Constants.HTTP_AUTHENITCATION_FACTORY, ModelType.STRING, false) | ||
.setMinSize(1) | ||
.setFlags(AttributeAccess.Flag.RESTART_RESOURCE_SERVICES) | ||
.setCapabilityReference(HTTP_AUTHENITCATION_FACTORY_CAPABILITY, APPLICATION_SECURITY_DOMAIN_CAPABILITY, true) | ||
.build(); | ||
|
||
static SimpleAttributeDefinition OVERRIDE_DEPLOYMENT_CONFIG = new SimpleAttributeDefinitionBuilder(Constants.OVERRIDE_DEPLOYMENT_CONFIG, ModelType.BOOLEAN, true) | ||
.setDefaultValue(new ModelNode(false)) | ||
.setFlags(AttributeAccess.Flag.RESTART_RESOURCE_SERVICES) | ||
.build(); | ||
|
||
private static final AttributeDefinition[] ATTRIBUTES = new AttributeDefinition[] { HTTP_SERVER_MECHANISM_FACTORY, OVERRIDE_DEPLOYMENT_CONFIG }; | ||
|
||
static final ApplicationSecurityDomainDefinition INSTANCE = new ApplicationSecurityDomainDefinition(); | ||
|
||
private static final Set<String> knownApplicationSecurityDomains = Collections.synchronizedSet(new HashSet<>()); | ||
|
||
private ApplicationSecurityDomainDefinition() { | ||
this(new Parameters(PathElement.pathElement(Constants.APPLICATION_SECURITY_DOMAIN), UndertowExtension.getResolver(Constants.APPLICATION_SECURITY_DOMAIN)) | ||
.setCapabilities(APPLICATION_SECURITY_DOMAIN_RUNTIME_CAPABILITY), new AddHandler()); | ||
} | ||
|
||
private ApplicationSecurityDomainDefinition(Parameters parameters, AbstractAddStepHandler add) { | ||
super(parameters.setAddHandler(add).setRemoveHandler(new RemoveHandler(add))); | ||
} | ||
|
||
private static class AddHandler extends AbstractAddStepHandler { | ||
|
||
private AddHandler() { | ||
super(ATTRIBUTES); | ||
} | ||
|
||
/* (non-Javadoc) | ||
* @see org.jboss.as.controller.AbstractAddStepHandler#populateModel(org.jboss.as.controller.OperationContext, org.jboss.dmr.ModelNode, org.jboss.as.controller.registry.Resource) | ||
*/ | ||
@Override | ||
protected void populateModel(OperationContext context, ModelNode operation, Resource resource) throws OperationFailedException { | ||
super.populateModel(context, operation, resource); | ||
knownApplicationSecurityDomains.add(context.getCurrentAddressValue()); | ||
} | ||
|
||
@Override | ||
protected void performRuntime(OperationContext context, ModelNode operation, ModelNode model) throws OperationFailedException { | ||
|
||
String httpServerMechanismFactory = HTTP_SERVER_MECHANISM_FACTORY.resolveModelAttribute(context, model).asString(); | ||
boolean overrideDeploymentConfig = OVERRIDE_DEPLOYMENT_CONFIG.resolveModelAttribute(context, model).asBoolean(); | ||
|
||
RuntimeCapability<?> runtimeCapability = APPLICATION_SECURITY_DOMAIN_RUNTIME_CAPABILITY.fromBaseCapability(context.getCurrentAddressValue()); | ||
ServiceName serviceName = runtimeCapability.getCapabilityServiceName(Function.class); | ||
|
||
ApplicationSecurityDomainService applicationSecurityDomainService = new ApplicationSecurityDomainService(overrideDeploymentConfig); | ||
|
||
ServiceBuilder<Function<DeploymentInfo, Registration>> serviceBuilder = context.getServiceTarget().addService(serviceName, applicationSecurityDomainService) | ||
.setInitialMode(Mode.LAZY); | ||
|
||
serviceBuilder.addDependency(context.getCapabilityServiceName( | ||
buildDynamicCapabilityName(HTTP_AUTHENITCATION_FACTORY_CAPABILITY, httpServerMechanismFactory), HttpAuthenticationFactory.class), | ||
HttpAuthenticationFactory.class, applicationSecurityDomainService.getHttpAuthenticationFactoryInjector()); | ||
|
||
serviceBuilder.install(); | ||
} | ||
|
||
} | ||
|
||
private static class RemoveHandler extends ServiceRemoveStepHandler { | ||
|
||
/** | ||
* @param addOperation | ||
*/ | ||
protected RemoveHandler(AbstractAddStepHandler addOperation) { | ||
super(addOperation); | ||
} | ||
|
||
@Override | ||
protected void performRemove(OperationContext context, ModelNode operation, ModelNode model) throws OperationFailedException { | ||
super.performRemove(context, operation, model); | ||
knownApplicationSecurityDomains.remove(context.getCurrentAddressValue()); | ||
} | ||
|
||
@Override | ||
protected ServiceName serviceName(String name) { | ||
RuntimeCapability<?> dynamicCapability = APPLICATION_SECURITY_DOMAIN_RUNTIME_CAPABILITY.fromBaseCapability(name); | ||
return dynamicCapability.getCapabilityServiceName(HttpAuthenticationFactory.class); | ||
} | ||
|
||
} | ||
|
||
@Override | ||
public Collection<AttributeDefinition> getAttributes() { | ||
return Arrays.asList(ATTRIBUTES); | ||
} | ||
|
||
Predicate<String> getKnownSecurityDomainPredicate() { | ||
return knownApplicationSecurityDomains::contains; | ||
} | ||
|
||
private static class ApplicationSecurityDomainService implements Service<Function<DeploymentInfo, Registration>> { | ||
|
||
private final boolean overrideDeploymentConfig; | ||
private final InjectedValue<HttpAuthenticationFactory> httpAuthenticationFactoryInjector = new InjectedValue<>(); | ||
private final Set<Registration> registrations = new HashSet<>(); | ||
|
||
private HttpAuthenticationFactory httpAuthenticationFactory; | ||
|
||
private ApplicationSecurityDomainService(final boolean overrideDeploymentConfig) { | ||
this.overrideDeploymentConfig = overrideDeploymentConfig; | ||
} | ||
|
||
@Override | ||
public void start(StartContext context) throws StartException { | ||
httpAuthenticationFactory = httpAuthenticationFactoryInjector.getValue(); | ||
} | ||
|
||
@Override | ||
public void stop(StopContext context) { | ||
httpAuthenticationFactory = null; | ||
} | ||
|
||
@Override | ||
public Function<DeploymentInfo, Registration> getValue() throws IllegalStateException, IllegalArgumentException { | ||
return this::applyElytronSecurity; | ||
} | ||
|
||
private Injector<HttpAuthenticationFactory> getHttpAuthenticationFactoryInjector() { | ||
return httpAuthenticationFactoryInjector; | ||
} | ||
|
||
private Registration applyElytronSecurity(final DeploymentInfo deploymentInfo) { | ||
deploymentInfo.addInnerHandlerChainWrapper(this::finalSecurityHandlers); | ||
deploymentInfo.setInitialSecurityWrapper(h -> initialSecurityHandler(deploymentInfo, h)); | ||
|
||
Registration registration = new RegistrationImpl(deploymentInfo); | ||
registrations.add(registration); | ||
return registration; | ||
} | ||
|
||
private List<String> desiredMechanisms(DeploymentInfo deploymentInfo) { | ||
if (overrideDeploymentConfig) { | ||
return new ArrayList<>(httpAuthenticationFactory.getMechanismNames()); | ||
} else { | ||
return deploymentInfo.getLoginConfig().getAuthMethods().stream().map(c -> c.getName()) | ||
.collect(Collectors.toList()); | ||
} | ||
} | ||
|
||
private HttpServerAuthenticationMechanism createMechanism(final String name) { | ||
try { | ||
return httpAuthenticationFactory.createMechanism(name); | ||
} catch (HttpAuthenticationException e) { | ||
throw new IllegalStateException(e); | ||
} | ||
} | ||
private List<HttpServerAuthenticationMechanism> getAuthenticationMechanisms(Supplier<List<String>> mechanismNames) { | ||
return mechanismNames.get().stream().map(this::createMechanism).collect(Collectors.toList()); | ||
} | ||
|
||
private HttpHandler initialSecurityHandler(final DeploymentInfo deploymentInfo, HttpHandler toWrap) { | ||
return new ElytronContextAssociationHandler(toWrap, () -> getAuthenticationMechanisms(() -> desiredMechanisms(deploymentInfo))); | ||
} | ||
|
||
private HttpHandler finalSecurityHandlers(HttpHandler toWrap) { | ||
return new BlockingHandler(new ElytronRunAsHandler(toWrap)); | ||
} | ||
|
||
private class RegistrationImpl implements Registration { | ||
|
||
private final DeploymentInfo deploymentInfo; | ||
|
||
private RegistrationImpl(DeploymentInfo deploymentInfo) { | ||
this.deploymentInfo = deploymentInfo; | ||
} | ||
|
||
@Override | ||
public void cancel() { | ||
registrations.remove(this); | ||
} | ||
|
||
} | ||
|
||
} | ||
|
||
public interface Registration { | ||
|
||
/** | ||
* Cancel the registration. | ||
*/ | ||
void cancel(); | ||
|
||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.