Skip to content

Commit

Permalink
WFLY-7049 Elytron-based single sign-on integration
Browse files Browse the repository at this point in the history
  • Loading branch information
pferraro committed Jan 23, 2017
1 parent 8e9da74 commit 471b634
Show file tree
Hide file tree
Showing 12 changed files with 525 additions and 40 deletions.
Expand Up @@ -41,8 +41,10 @@
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.function.UnaryOperator;
import java.util.stream.Collectors; import java.util.stream.Collectors;


import javax.servlet.RequestDispatcher; import javax.servlet.RequestDispatcher;
Expand Down Expand Up @@ -79,10 +81,13 @@
import org.jboss.msc.service.ServiceController.State; import org.jboss.msc.service.ServiceController.State;
import org.jboss.msc.service.ServiceName; import org.jboss.msc.service.ServiceName;
import org.jboss.msc.service.ServiceRegistry; import org.jboss.msc.service.ServiceRegistry;
import org.jboss.msc.service.ServiceTarget;
import org.jboss.msc.service.StartContext; import org.jboss.msc.service.StartContext;
import org.jboss.msc.service.StartException; import org.jboss.msc.service.StartException;
import org.jboss.msc.service.StopContext; import org.jboss.msc.service.StopContext;
import org.jboss.msc.value.InjectedValue; import org.jboss.msc.value.InjectedValue;
import org.wildfly.clustering.service.Builder;
import org.wildfly.clustering.service.SimpleBuilder;
import org.wildfly.elytron.web.undertow.server.ElytronContextAssociationHandler; import org.wildfly.elytron.web.undertow.server.ElytronContextAssociationHandler;
import org.wildfly.elytron.web.undertow.server.ElytronHttpExchange; import org.wildfly.elytron.web.undertow.server.ElytronHttpExchange;
import org.wildfly.elytron.web.undertow.server.ElytronRunAsHandler; import org.wildfly.elytron.web.undertow.server.ElytronRunAsHandler;
Expand All @@ -91,15 +96,24 @@
import org.wildfly.security.http.HttpAuthenticationException; import org.wildfly.security.http.HttpAuthenticationException;
import org.wildfly.security.http.HttpScope; import org.wildfly.security.http.HttpScope;
import org.wildfly.security.http.HttpServerAuthenticationMechanism; import org.wildfly.security.http.HttpServerAuthenticationMechanism;
import org.wildfly.security.http.HttpServerAuthenticationMechanismFactory;
import org.wildfly.security.http.Scope; import org.wildfly.security.http.Scope;
import org.wildfly.security.http.util.PropertiesServerMechanismFactory; import org.wildfly.security.http.util.PropertiesServerMechanismFactory;
import org.wildfly.security.http.util.sso.DefaultSingleSignOnManager;
import org.wildfly.security.http.util.sso.SingleSignOnManager;
import org.wildfly.security.http.util.sso.SingleSignOnServerMechanismFactory;
import org.wildfly.security.http.util.sso.SingleSignOnServerMechanismFactory.SingleSignOnConfiguration;
import org.wildfly.security.http.util.sso.SingleSignOnSessionFactory;


import io.undertow.server.HttpHandler; import io.undertow.server.HttpHandler;
import io.undertow.server.HttpServerExchange; import io.undertow.server.HttpServerExchange;
import io.undertow.server.handlers.BlockingHandler; import io.undertow.server.handlers.BlockingHandler;
import io.undertow.server.session.SecureRandomSessionIdGenerator;
import io.undertow.server.session.SessionConfig; import io.undertow.server.session.SessionConfig;
import io.undertow.server.session.SessionIdGenerator;
import io.undertow.server.session.SessionManager; import io.undertow.server.session.SessionManager;
import io.undertow.servlet.api.AuthMethodConfig; import io.undertow.servlet.api.AuthMethodConfig;
import io.undertow.servlet.api.Deployment;
import io.undertow.servlet.api.DeploymentInfo; import io.undertow.servlet.api.DeploymentInfo;
import io.undertow.servlet.api.LoginConfig; import io.undertow.servlet.api.LoginConfig;
import io.undertow.servlet.handlers.ServletRequestContext; import io.undertow.servlet.handlers.ServletRequestContext;
Expand All @@ -121,18 +135,18 @@ public class ApplicationSecurityDomainDefinition extends PersistentResourceDefin


private static final String HTTP_AUTHENITCATION_FACTORY_CAPABILITY = "org.wildfly.security.http-authentication-factory"; private static final String HTTP_AUTHENITCATION_FACTORY_CAPABILITY = "org.wildfly.security.http-authentication-factory";


static SimpleAttributeDefinition HTTP_AUTHENTICATION_FACTORY = new SimpleAttributeDefinitionBuilder(Constants.HTTP_AUTHENITCATION_FACTORY, ModelType.STRING, false) static final SimpleAttributeDefinition HTTP_AUTHENTICATION_FACTORY = new SimpleAttributeDefinitionBuilder(Constants.HTTP_AUTHENITCATION_FACTORY, ModelType.STRING, false)
.setMinSize(1) .setMinSize(1)
.setRestartAllServices() .setRestartAllServices()
.setCapabilityReference(HTTP_AUTHENITCATION_FACTORY_CAPABILITY, APPLICATION_SECURITY_DOMAIN_CAPABILITY, true) .setCapabilityReference(HTTP_AUTHENITCATION_FACTORY_CAPABILITY, APPLICATION_SECURITY_DOMAIN_CAPABILITY, true)
.build(); .build();


static SimpleAttributeDefinition OVERRIDE_DEPLOYMENT_CONFIG = new SimpleAttributeDefinitionBuilder(Constants.OVERRIDE_DEPLOYMENT_CONFIG, ModelType.BOOLEAN, true) static final SimpleAttributeDefinition OVERRIDE_DEPLOYMENT_CONFIG = new SimpleAttributeDefinitionBuilder(Constants.OVERRIDE_DEPLOYMENT_CONFIG, ModelType.BOOLEAN, true)
.setDefaultValue(new ModelNode(false)) .setDefaultValue(new ModelNode(false))
.setRestartAllServices() .setRestartAllServices()
.build(); .build();


private static StringListAttributeDefinition REFERENCING_DEPLOYMENTS = new StringListAttributeDefinition.Builder(Constants.REFERENCING_DEPLOYMENTS) private static final StringListAttributeDefinition REFERENCING_DEPLOYMENTS = new StringListAttributeDefinition.Builder(Constants.REFERENCING_DEPLOYMENTS)
.setStorageRuntime() .setStorageRuntime()
.build(); .build();


Expand All @@ -158,6 +172,11 @@ public void registerAttributes(ManagementResourceRegistration resourceRegistrati
resourceRegistration.registerReadOnlyAttribute(REFERENCING_DEPLOYMENTS, new ReferencingDeploymentsHandler()); resourceRegistration.registerReadOnlyAttribute(REFERENCING_DEPLOYMENTS, new ReferencingDeploymentsHandler());
} }


@Override
protected List<? extends PersistentResourceDefinition> getChildren() {
return Collections.singletonList(new ApplicationSecurityDomainSingleSignOnDefinition());
}

private static class ReferencingDeploymentsHandler implements OperationStepHandler { private static class ReferencingDeploymentsHandler implements OperationStepHandler {


@Override @Override
Expand Down Expand Up @@ -199,22 +218,50 @@ protected void populateModel(OperationContext context, ModelNode operation, Reso
} }


@Override @Override
protected void performRuntime(OperationContext context, ModelNode operation, ModelNode model) throws OperationFailedException { protected void performRuntime(OperationContext context, ModelNode operation, Resource resource) throws OperationFailedException {
ModelNode model = resource.getModel();
ServiceTarget target = context.getServiceTarget();


String httpServerMechanismFactory = HTTP_AUTHENTICATION_FACTORY.resolveModelAttribute(context, model).asString(); String httpServerMechanismFactory = HTTP_AUTHENTICATION_FACTORY.resolveModelAttribute(context, model).asString();
boolean overrideDeploymentConfig = OVERRIDE_DEPLOYMENT_CONFIG.resolveModelAttribute(context, model).asBoolean(); boolean overrideDeploymentConfig = OVERRIDE_DEPLOYMENT_CONFIG.resolveModelAttribute(context, model).asBoolean();


RuntimeCapability<?> runtimeCapability = APPLICATION_SECURITY_DOMAIN_RUNTIME_CAPABILITY.fromBaseCapability(context.getCurrentAddressValue()); String securityDomainName = context.getCurrentAddressValue();
RuntimeCapability<?> runtimeCapability = APPLICATION_SECURITY_DOMAIN_RUNTIME_CAPABILITY.fromBaseCapability(securityDomainName);
ServiceName serviceName = runtimeCapability.getCapabilityServiceName(Function.class); ServiceName serviceName = runtimeCapability.getCapabilityServiceName(Function.class);


ApplicationSecurityDomainService applicationSecurityDomainService = new ApplicationSecurityDomainService(overrideDeploymentConfig); ApplicationSecurityDomainService applicationSecurityDomainService = new ApplicationSecurityDomainService(overrideDeploymentConfig);


ServiceBuilder<Function<DeploymentInfo, Registration>> serviceBuilder = context.getServiceTarget().addService(serviceName, applicationSecurityDomainService) ServiceBuilder<Function<DeploymentInfo, Registration>> serviceBuilder = target.addService(serviceName, applicationSecurityDomainService)
.setInitialMode(Mode.LAZY); .setInitialMode(Mode.LAZY);


serviceBuilder.addDependency(context.getCapabilityServiceName(HTTP_AUTHENITCATION_FACTORY_CAPABILITY, serviceBuilder.addDependency(context.getCapabilityServiceName(HTTP_AUTHENITCATION_FACTORY_CAPABILITY,
httpServerMechanismFactory, HttpAuthenticationFactory.class), HttpAuthenticationFactory.class, applicationSecurityDomainService.getHttpAuthenticationFactoryInjector()); httpServerMechanismFactory, HttpAuthenticationFactory.class), HttpAuthenticationFactory.class, applicationSecurityDomainService.getHttpAuthenticationFactoryInjector());


if (resource.hasChild(UndertowExtension.PATH_SSO)) {
ModelNode ssoModel = resource.getChild(UndertowExtension.PATH_SSO).getModel();

String cookieName = SingleSignOnDefinition.Attribute.COOKIE_NAME.resolveModelAttribute(context, ssoModel).asString();
String domain = SingleSignOnDefinition.Attribute.DOMAIN.resolveModelAttribute(context, ssoModel).asString();
String path = SingleSignOnDefinition.Attribute.PATH.resolveModelAttribute(context, ssoModel).asString();
boolean httpOnly = SingleSignOnDefinition.Attribute.HTTP_ONLY.resolveModelAttribute(context, ssoModel).asBoolean();
boolean secure = SingleSignOnDefinition.Attribute.SECURE.resolveModelAttribute(context, ssoModel).asBoolean();
SingleSignOnConfiguration singleSignOnConfiguration = new SingleSignOnConfiguration(cookieName, domain, path, httpOnly, secure);

ServiceName managerServiceName = new SingleSignOnManagerServiceNameProvider(securityDomainName).getServiceName();
SessionIdGenerator generator = new SecureRandomSessionIdGenerator();

SingleSignOnManager manager = new DefaultSingleSignOnManager(new ConcurrentHashMap<>(), () -> generator.createSessionId());
new SimpleBuilder<>(managerServiceName, manager).build(target).install();

Builder<SingleSignOnSessionFactory> factoryBuilder = new SingleSignOnSessionFactoryBuilder(securityDomainName).configure(context, ssoModel);
factoryBuilder.build(target).setInitialMode(ServiceController.Mode.ON_DEMAND).install();

InjectedValue<SingleSignOnSessionFactory> singleSignOnSessionFactory = new InjectedValue<>();
serviceBuilder.addDependency(factoryBuilder.getServiceName(), SingleSignOnSessionFactory.class, singleSignOnSessionFactory);

applicationSecurityDomainService.getSingleSignOnSessionFactoryInjector().inject(factory -> new SingleSignOnServerMechanismFactory(factory, singleSignOnSessionFactory.getValue(), singleSignOnConfiguration));
}

serviceBuilder.install(); serviceBuilder.install();
} }


Expand All @@ -231,6 +278,11 @@ protected RemoveHandler(AbstractAddStepHandler addOperation) {


@Override @Override
protected void performRemove(OperationContext context, ModelNode operation, ModelNode model) throws OperationFailedException { protected void performRemove(OperationContext context, ModelNode operation, ModelNode model) throws OperationFailedException {
if (context.isResourceServiceRestartAllowed()) {
String securityDomainName = context.getCurrentAddressValue();
context.removeService(new SingleSignOnManagerServiceNameProvider(securityDomainName).getServiceName());
context.removeService(new SingleSignOnSessionFactoryServiceNameProvider(securityDomainName).getServiceName());
}
super.performRemove(context, operation, model); super.performRemove(context, operation, model);
knownApplicationSecurityDomains.remove(context.getCurrentAddressValue()); knownApplicationSecurityDomains.remove(context.getCurrentAddressValue());
} }
Expand All @@ -256,6 +308,7 @@ private static class ApplicationSecurityDomainService implements Service<Functio


private final boolean overrideDeploymentConfig; private final boolean overrideDeploymentConfig;
private final InjectedValue<HttpAuthenticationFactory> httpAuthenticationFactoryInjector = new InjectedValue<>(); private final InjectedValue<HttpAuthenticationFactory> httpAuthenticationFactoryInjector = new InjectedValue<>();
private final InjectedValue<UnaryOperator<HttpServerAuthenticationMechanismFactory>> singleSignOnTransformer = new InjectedValue<>();
private final Set<RegistrationImpl> registrations = new HashSet<>(); private final Set<RegistrationImpl> registrations = new HashSet<>();


private HttpAuthenticationFactory httpAuthenticationFactory; private HttpAuthenticationFactory httpAuthenticationFactory;
Expand Down Expand Up @@ -283,6 +336,10 @@ private Injector<HttpAuthenticationFactory> getHttpAuthenticationFactoryInjector
return httpAuthenticationFactoryInjector; return httpAuthenticationFactoryInjector;
} }


Injector<UnaryOperator<HttpServerAuthenticationMechanismFactory>> getSingleSignOnSessionFactoryInjector() {
return this.singleSignOnTransformer;
}

private Registration applyElytronSecurity(final DeploymentInfo deploymentInfo) { private Registration applyElytronSecurity(final DeploymentInfo deploymentInfo) {
final ScopeSessionListener scopeSessionListener = ScopeSessionListener.builder() final ScopeSessionListener scopeSessionListener = ScopeSessionListener.builder()
.addScopeResolver(Scope.APPLICATION, ApplicationSecurityDomainService::applicationScope) .addScopeResolver(Scope.APPLICATION, ApplicationSecurityDomainService::applicationScope)
Expand All @@ -303,7 +360,12 @@ private List<HttpServerAuthenticationMechanism> getAuthenticationMechanisms(Map<
List<HttpServerAuthenticationMechanism> mechanisms = new ArrayList<>(selectedMechanisms.size()); List<HttpServerAuthenticationMechanism> mechanisms = new ArrayList<>(selectedMechanisms.size());
selectedMechanisms.forEach((n, c) -> { selectedMechanisms.forEach((n, c) -> {
try { try {
HttpServerAuthenticationMechanism mechanism = httpAuthenticationFactory.createMechanism(n, (f) -> new PropertiesServerMechanismFactory(f, c)); UnaryOperator<HttpServerAuthenticationMechanismFactory> singleSignOnTransformer = this.singleSignOnTransformer.getOptionalValue();
UnaryOperator<HttpServerAuthenticationMechanismFactory> factoryTransformation = f -> {
HttpServerAuthenticationMechanismFactory factory = new PropertiesServerMechanismFactory(f, c);
return (singleSignOnTransformer != null) ? singleSignOnTransformer.apply(factory) : factory;
};
HttpServerAuthenticationMechanism mechanism = httpAuthenticationFactory.createMechanism(n, factoryTransformation);
if (mechanism!= null) mechanisms.add(mechanism); if (mechanism!= null) mechanisms.add(mechanism);
} catch (HttpAuthenticationException e) { } catch (HttpAuthenticationException e) {
throw new IllegalStateException(e); throw new IllegalStateException(e);
Expand Down Expand Up @@ -431,8 +493,13 @@ private static HttpScope applicationScope(HttpServerExchange exchange) {
ServletRequestContext servletRequestContext = exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY); ServletRequestContext servletRequestContext = exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);


if (servletRequestContext != null) { if (servletRequestContext != null) {
final ServletContext servletContext = servletRequestContext.getDeployment().getServletContext(); final Deployment deployment = servletRequestContext.getDeployment();
final ServletContext servletContext = deployment.getServletContext();
return new HttpScope() { return new HttpScope() {
@Override
public String getID() {
return deployment.getDeploymentInfo().getDeploymentName();
}


@Override @Override
public boolean supportsAttachments() { public boolean supportsAttachments() {
Expand All @@ -458,8 +525,6 @@ public boolean supportsResources() {
public InputStream getResource(String path) { public InputStream getResource(String path) {
return servletContext.getResourceAsStream(path); return servletContext.getResourceAsStream(path);
} }


}; };
} }


Expand Down
@@ -0,0 +1,97 @@
/*
* JBoss, Home of Professional Open Source.
* Copyright 2017, 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 java.util.function.UnaryOperator;

import org.jboss.as.clustering.controller.CapabilityReference;
import org.jboss.as.clustering.controller.CommonUnaryRequirement;
import org.jboss.as.clustering.controller.ReloadRequiredResourceRegistration;
import org.jboss.as.clustering.controller.ResourceDescriptor;
import org.jboss.as.controller.AttributeDefinition;
import org.jboss.as.controller.PathAddress;
import org.jboss.as.controller.SimpleAttributeDefinitionBuilder;
import org.jboss.as.controller.capability.RuntimeCapability;
import org.jboss.as.controller.registry.ManagementResourceRegistration;
import org.jboss.as.controller.security.CredentialReference;
import org.jboss.dmr.ModelType;

/**
* @author Paul Ferraro
*/
public class ApplicationSecurityDomainSingleSignOnDefinition extends SingleSignOnDefinition {

enum Capability implements org.jboss.as.clustering.controller.Capability {
SSO_CREDENTIAL_STORE("org.wildfly.extension.undertow.application-security-domain.single-sign-on.credential-store"),
SSO_KEY_STORE("org.wildfly.extension.undertow.application-security-domain.single-sign-on.key-store"),
SSO_SSL_CONTEXT("org.wildfly.extension.undertow.application-security-domain.single-sign-on.client-ssl-context"),
;
private final RuntimeCapability<Void> definition;

Capability(String name) {
this.definition = RuntimeCapability.Builder.of(name, true).build();
}

@Override
public RuntimeCapability<Void> getDefinition() {
return this.definition;
}

@Override
public RuntimeCapability<?> resolve(PathAddress address) {
return this.definition.fromBaseCapability(address.getParent().getLastElement().getValue());
}
}

enum Attribute implements org.jboss.as.clustering.controller.Attribute {
CREDENTIAL(CredentialReference.getAttributeBuilder(CredentialReference.CREDENTIAL_REFERENCE, CredentialReference.CREDENTIAL_REFERENCE, false).setCapabilityReference(new CapabilityReference(Capability.SSO_CREDENTIAL_STORE, CommonUnaryRequirement.CREDENTIAL_STORE)).build()),
KEY_ALIAS("key-alias", ModelType.STRING, builder -> builder.setAllowExpression(true)),
KEY_STORE("key-store", ModelType.STRING, builder -> builder.setCapabilityReference(new CapabilityReference(Capability.SSO_KEY_STORE, CommonUnaryRequirement.KEY_STORE))),
SSL_CONTEXT("client-ssl-context", ModelType.STRING, builder -> builder.setRequired(false).setCapabilityReference(new CapabilityReference(Capability.SSO_SSL_CONTEXT, CommonUnaryRequirement.SSL_CONTEXT))),
;
private final AttributeDefinition definition;

Attribute(String name, ModelType type, UnaryOperator<SimpleAttributeDefinitionBuilder> configurator) {
this.definition = configurator.apply(new SimpleAttributeDefinitionBuilder(name, type).setRequired(true)).build();
}

Attribute(AttributeDefinition definition) {
this.definition = definition;
}

@Override
public AttributeDefinition getDefinition() {
return this.definition;
}
}

@Override
public void registerOperations(ManagementResourceRegistration registration) {
ResourceDescriptor descriptor = new ResourceDescriptor(this.getResourceDescriptionResolver())
.addAttributes(Attribute.class)
.addAttributes(SingleSignOnDefinition.Attribute.class)
.addCapabilities(Capability.class)
;
new ReloadRequiredResourceRegistration(descriptor).register(registration);
}
}
@@ -0,0 +1,43 @@
/*
* JBoss, Home of Professional Open Source.
* Copyright 2017, 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 org.jboss.msc.service.ServiceName;
import org.wildfly.clustering.service.ServiceNameProvider;

/**
* @author Paul Ferraro
*/
public class SingleSignOnManagerServiceNameProvider implements ServiceNameProvider {

private final ServiceName name;

public SingleSignOnManagerServiceNameProvider(String securityDomainName) {
this.name = ApplicationSecurityDomainDefinition.APPLICATION_SECURITY_DOMAIN_RUNTIME_CAPABILITY.fromBaseCapability(securityDomainName).getCapabilityServiceName().append("sso", "manager");
}

@Override
public ServiceName getServiceName() {
return this.name;
}
}

0 comments on commit 471b634

Please sign in to comment.