From a6dfb4ccdd607fc37a7a2f04537f43d348d8616a Mon Sep 17 00:00:00 2001 From: Pedro Igor Date: Thu, 17 Aug 2017 12:32:19 -0300 Subject: [PATCH] [KEYCLOAK-5015] - Support for Elytron capabilities in subsystem --- .../oidc/wildfly/wildfly-subsystem/pom.xml | 8 + ...stractAdapterConfigurationAddHandler.java} | 29 +- ...bstractAdapterConfigurationDefinition.java | 164 +++++++++++ ...actAdapterConfigurationRemoveHandler.java} | 6 +- ...erConfigurationWriteAttributeHandler.java} | 10 +- ...cloakAdapterConfigDeploymentProcessor.java | 34 +++ .../KeycloakAdapterConfigService.java | 54 +++- .../KeycloakDependencyProcessorWildFly.java | 3 +- .../adapter/extension/KeycloakExtension.java | 7 +- ...cloakHttpAuthenticationFactoryService.java | 67 +++++ ...henticationMechanismFactoryDefinition.java | 117 ++++++++ .../extension/KeycloakSubsystemParser.java | 42 ++- .../extension/SecureDeploymentDefinition.java | 184 +++++-------- .../extension/SecureServerDefinition.java | 258 ++++++++++++++++++ .../extension/LocalDescriptions.properties | 43 +++ .../resources/schema/wildfly-keycloak_1_1.xsd | 9 +- .../extension/SubsystemParsingTestCase.java | 2 +- .../adapter/extension/keycloak-1.1.xml | 16 ++ .../main/module.xml | 4 + testsuite/integration-arquillian/pom.xml | 4 +- 20 files changed, 885 insertions(+), 176 deletions(-) rename adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/{SecureDeploymentAddHandler.java => AbstractAdapterConfigurationAddHandler.java} (58%) create mode 100755 adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/AbstractAdapterConfigurationDefinition.java rename adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/{SecureDeploymentRemoveHandler.java => AbstractAdapterConfigurationRemoveHandler.java} (85%) rename adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/{SecureDeploymentWriteAttributeHandler.java => AbstractAdapterConfigurationWriteAttributeHandler.java} (83%) create mode 100644 adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakHttpAuthenticationFactoryService.java create mode 100644 adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakHttpServerAuthenticationMechanismFactoryDefinition.java create mode 100755 adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/SecureServerDefinition.java diff --git a/adapters/oidc/wildfly/wildfly-subsystem/pom.xml b/adapters/oidc/wildfly/wildfly-subsystem/pom.xml index 1d8e6cf916..8c18a39a74 100755 --- a/adapters/oidc/wildfly/wildfly-subsystem/pom.xml +++ b/adapters/oidc/wildfly/wildfly-subsystem/pom.xml @@ -68,6 +68,10 @@ wildfly-web-common provided + + org.wildfly.security + wildfly-elytron + org.jboss.logging jboss-logging-annotations @@ -101,5 +105,9 @@ keycloak-wildfly-adapter ${project.version} + + org.keycloak + keycloak-wildfly-elytron-oidc-adapter + diff --git a/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/SecureDeploymentAddHandler.java b/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/AbstractAdapterConfigurationAddHandler.java similarity index 58% rename from adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/SecureDeploymentAddHandler.java rename to adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/AbstractAdapterConfigurationAddHandler.java index 110d385a75..55c83ca96b 100755 --- a/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/SecureDeploymentAddHandler.java +++ b/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/AbstractAdapterConfigurationAddHandler.java @@ -17,41 +17,32 @@ package org.keycloak.subsystem.adapter.extension; +import java.util.List; + 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.SimpleAttributeDefinition; +import org.jboss.as.controller.capability.RuntimeCapability; import org.jboss.dmr.ModelNode; -import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ADD; -import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP; - /** * Add a deployment to a realm. * * @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc. */ -public final class SecureDeploymentAddHandler extends AbstractAddStepHandler { +abstract class AbstractAdapterConfigurationAddHandler extends AbstractAddStepHandler { - public static SecureDeploymentAddHandler INSTANCE = new SecureDeploymentAddHandler(); + private final boolean elytronEnabled; - private SecureDeploymentAddHandler() {} - - @Override - protected void populateModel(ModelNode operation, ModelNode model) throws OperationFailedException { - // TODO: localize exception. get id number - if (!operation.get(OP).asString().equals(ADD)) { - throw new OperationFailedException("Unexpected operation for add secure deployment. operation=" + operation.toString()); - } - - for (AttributeDefinition attr : SecureDeploymentDefinition.ALL_ATTRIBUTES) { - attr.validateAndSet(operation, model); - } + AbstractAdapterConfigurationAddHandler(RuntimeCapability runtimeCapability, List attributes) { + super(runtimeCapability, attributes); + elytronEnabled = runtimeCapability != null; } @Override protected void performRuntime(OperationContext context, ModelNode operation, ModelNode model) throws OperationFailedException { KeycloakAdapterConfigService ckService = KeycloakAdapterConfigService.getInstance(); - ckService.addSecureDeployment(operation, context.resolveExpressions(model)); + ckService.addSecureDeployment(operation, context.resolveExpressions(model), elytronEnabled); } } diff --git a/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/AbstractAdapterConfigurationDefinition.java b/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/AbstractAdapterConfigurationDefinition.java new file mode 100755 index 0000000000..613c946f4f --- /dev/null +++ b/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/AbstractAdapterConfigurationDefinition.java @@ -0,0 +1,164 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2016 Red Hat, Inc., and individual contributors + * as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.subsystem.adapter.extension; + +import org.jboss.as.controller.AttributeDefinition; +import org.jboss.as.controller.PathElement; +import org.jboss.as.controller.SimpleAttributeDefinition; +import org.jboss.as.controller.SimpleAttributeDefinitionBuilder; +import org.jboss.as.controller.SimpleResourceDefinition; +import org.jboss.as.controller.operations.common.GenericSubsystemDescribeHandler; +import org.jboss.as.controller.operations.validation.IntRangeValidator; +import org.jboss.as.controller.operations.validation.StringLengthValidator; +import org.jboss.as.controller.registry.ManagementResourceRegistration; +import org.jboss.dmr.ModelNode; +import org.jboss.dmr.ModelType; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Defines attributes and operations for a secure-deployment. + * + * @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc. + */ +abstract class AbstractAdapterConfigurationDefinition extends SimpleResourceDefinition { + + protected static final SimpleAttributeDefinition REALM = + new SimpleAttributeDefinitionBuilder("realm", ModelType.STRING, true) + .setXmlName("realm") + .setAllowExpression(true) + .setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, true, true)) + .build(); + protected static final SimpleAttributeDefinition RESOURCE = + new SimpleAttributeDefinitionBuilder("resource", ModelType.STRING, true) + .setXmlName("resource") + .setAllowExpression(true) + .setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, true, true)) + .build(); + protected static final SimpleAttributeDefinition USE_RESOURCE_ROLE_MAPPINGS = + new SimpleAttributeDefinitionBuilder("use-resource-role-mappings", ModelType.BOOLEAN, true) + .setXmlName("use-resource-role-mappings") + .setAllowExpression(true) + .setDefaultValue(new ModelNode(false)) + .build(); + protected static final SimpleAttributeDefinition BEARER_ONLY = + new SimpleAttributeDefinitionBuilder("bearer-only", ModelType.BOOLEAN, true) + .setXmlName("bearer-only") + .setAllowExpression(true) + .setDefaultValue(new ModelNode(false)) + .build(); + protected static final SimpleAttributeDefinition ENABLE_BASIC_AUTH = + new SimpleAttributeDefinitionBuilder("enable-basic-auth", ModelType.BOOLEAN, true) + .setXmlName("enable-basic-auth") + .setAllowExpression(true) + .setDefaultValue(new ModelNode(false)) + .build(); + protected static final SimpleAttributeDefinition PUBLIC_CLIENT = + new SimpleAttributeDefinitionBuilder("public-client", ModelType.BOOLEAN, true) + .setXmlName("public-client") + .setAllowExpression(true) + .setDefaultValue(new ModelNode(false)) + .build(); + protected static final SimpleAttributeDefinition TURN_OFF_CHANGE_SESSION = + new SimpleAttributeDefinitionBuilder("turn-off-change-session-id-on-login", ModelType.BOOLEAN, true) + .setXmlName("turn-off-change-session-id-on-login") + .setAllowExpression(true) + .setDefaultValue(new ModelNode(false)) + .build(); + protected static final SimpleAttributeDefinition TOKEN_MINIMUM_TIME_TO_LIVE = + new SimpleAttributeDefinitionBuilder("token-minimum-time-to-live", ModelType.INT, true) + .setXmlName("token-minimum-time-to-live") + .setValidator(new IntRangeValidator(-1, true)) + .setAllowExpression(true) + .build(); + protected static final SimpleAttributeDefinition MIN_TIME_BETWEEN_JWKS_REQUESTS = + new SimpleAttributeDefinitionBuilder("min-time-between-jwks-requests", ModelType.INT, true) + .setXmlName("min-time-between-jwks-requests") + .setValidator(new IntRangeValidator(-1, true)) + .setAllowExpression(true) + .build(); + + static final List DEPLOYMENT_ONLY_ATTRIBUTES = new ArrayList(); + + static { + DEPLOYMENT_ONLY_ATTRIBUTES.add(REALM); + DEPLOYMENT_ONLY_ATTRIBUTES.add(RESOURCE); + DEPLOYMENT_ONLY_ATTRIBUTES.add(USE_RESOURCE_ROLE_MAPPINGS); + DEPLOYMENT_ONLY_ATTRIBUTES.add(BEARER_ONLY); + DEPLOYMENT_ONLY_ATTRIBUTES.add(ENABLE_BASIC_AUTH); + DEPLOYMENT_ONLY_ATTRIBUTES.add(PUBLIC_CLIENT); + DEPLOYMENT_ONLY_ATTRIBUTES.add(TURN_OFF_CHANGE_SESSION); + DEPLOYMENT_ONLY_ATTRIBUTES.add(TOKEN_MINIMUM_TIME_TO_LIVE); + DEPLOYMENT_ONLY_ATTRIBUTES.add(MIN_TIME_BETWEEN_JWKS_REQUESTS); + } + + static final List ALL_ATTRIBUTES = new ArrayList(); + + static { + ALL_ATTRIBUTES.addAll(DEPLOYMENT_ONLY_ATTRIBUTES); + ALL_ATTRIBUTES.addAll(SharedAttributeDefinitons.ATTRIBUTES); + } + + static final Map XML_ATTRIBUTES = new HashMap(); + + static { + for (SimpleAttributeDefinition def : ALL_ATTRIBUTES) { + XML_ATTRIBUTES.put(def.getXmlName(), def); + } + } + + private static final Map DEFINITION_LOOKUP = new HashMap(); + static { + for (SimpleAttributeDefinition def : ALL_ATTRIBUTES) { + DEFINITION_LOOKUP.put(def.getXmlName(), def); + } + } + + private final AbstractAdapterConfigurationWriteAttributeHandler attrWriteHandler; + private final List attributes; + + protected AbstractAdapterConfigurationDefinition(String name, List attributes, AbstractAdapterConfigurationAddHandler addHandler, AbstractAdapterConfigurationRemoveHandler removeHandler, AbstractAdapterConfigurationWriteAttributeHandler attrWriteHandler) { + super(PathElement.pathElement(name), + KeycloakExtension.getResourceDescriptionResolver(name), + addHandler, + removeHandler); + this.attributes = attributes; + this.attrWriteHandler = attrWriteHandler; + } + + @Override + public void registerOperations(ManagementResourceRegistration resourceRegistration) { + super.registerOperations(resourceRegistration); + resourceRegistration.registerOperationHandler(GenericSubsystemDescribeHandler.DEFINITION, GenericSubsystemDescribeHandler.INSTANCE); + } + + @Override + public void registerAttributes(ManagementResourceRegistration resourceRegistration) { + super.registerAttributes(resourceRegistration); + for (AttributeDefinition attrDef : this.attributes) { + resourceRegistration.registerReadWriteAttribute(attrDef, null, this.attrWriteHandler); + } + } + + public static SimpleAttributeDefinition lookup(String name) { + return DEFINITION_LOOKUP.get(name); + } +} diff --git a/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/SecureDeploymentRemoveHandler.java b/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/AbstractAdapterConfigurationRemoveHandler.java similarity index 85% rename from adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/SecureDeploymentRemoveHandler.java rename to adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/AbstractAdapterConfigurationRemoveHandler.java index 7088ec0cf9..2066aed3f4 100644 --- a/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/SecureDeploymentRemoveHandler.java +++ b/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/AbstractAdapterConfigurationRemoveHandler.java @@ -27,11 +27,7 @@ import org.jboss.dmr.ModelNode; * * @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc. */ -public final class SecureDeploymentRemoveHandler extends AbstractRemoveStepHandler { - - public static SecureDeploymentRemoveHandler INSTANCE = new SecureDeploymentRemoveHandler(); - - private SecureDeploymentRemoveHandler() {} +abstract class AbstractAdapterConfigurationRemoveHandler extends AbstractRemoveStepHandler { @Override protected void performRuntime(OperationContext context, ModelNode operation, ModelNode model) throws OperationFailedException { diff --git a/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/SecureDeploymentWriteAttributeHandler.java b/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/AbstractAdapterConfigurationWriteAttributeHandler.java similarity index 83% rename from adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/SecureDeploymentWriteAttributeHandler.java rename to adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/AbstractAdapterConfigurationWriteAttributeHandler.java index 908526c828..127ea538f1 100755 --- a/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/SecureDeploymentWriteAttributeHandler.java +++ b/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/AbstractAdapterConfigurationWriteAttributeHandler.java @@ -31,14 +31,10 @@ import java.util.List; * * @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc. */ -public class SecureDeploymentWriteAttributeHandler extends AbstractWriteAttributeHandler { +abstract class AbstractAdapterConfigurationWriteAttributeHandler extends AbstractWriteAttributeHandler { - public SecureDeploymentWriteAttributeHandler(List definitions) { - this(definitions.toArray(new AttributeDefinition[definitions.size()])); - } - - public SecureDeploymentWriteAttributeHandler(AttributeDefinition... definitions) { - super(definitions); + AbstractAdapterConfigurationWriteAttributeHandler(List definitions) { + super(definitions.toArray(new AttributeDefinition[definitions.size()])); } @Override diff --git a/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakAdapterConfigDeploymentProcessor.java b/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakAdapterConfigDeploymentProcessor.java index 24e9ac0b74..1500808e32 100755 --- a/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakAdapterConfigDeploymentProcessor.java +++ b/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakAdapterConfigDeploymentProcessor.java @@ -25,7 +25,9 @@ import org.jboss.as.web.common.WarMetaData; import org.jboss.logging.Logger; import org.jboss.metadata.javaee.spec.ParamValueMetaData; import org.jboss.metadata.web.jboss.JBossWebMetaData; +import org.jboss.metadata.web.spec.ListenerMetaData; import org.jboss.metadata.web.spec.LoginConfigMetaData; +import org.keycloak.adapters.elytron.KeycloakConfigurationServletListener; import org.keycloak.subsystem.adapter.logging.KeycloakLogger; import java.util.ArrayList; @@ -69,6 +71,9 @@ public class KeycloakAdapterConfigDeploymentProcessor implements DeploymentUnitP KeycloakAdapterConfigService service = KeycloakAdapterConfigService.getInstance(); if (service.isSecureDeployment(deploymentUnit) && service.isDeploymentConfigured(deploymentUnit)) { addKeycloakAuthData(phaseContext, service); + } else if (service.isElytronEnabled(deploymentUnit)) { + WarMetaData warMetaData = deploymentUnit.getAttachment(WarMetaData.ATTACHMENT_KEY); + addConfigurationListener(warMetaData); } // FYI, Undertow Extension will find deployments that have auth-method set to KEYCLOAK @@ -99,6 +104,10 @@ public class KeycloakAdapterConfigDeploymentProcessor implements DeploymentUnitP loginConfig.setAuthMethod("KEYCLOAK"); loginConfig.setRealmName(service.getRealmName(deploymentUnit)); KeycloakLogger.ROOT_LOGGER.deploymentSecured(deploymentUnit.getName()); + + if (service.isElytronEnabled(deploymentUnit)) { + addConfigurationListener(warMetaData); + } } private void addJSONData(String json, WarMetaData warMetaData) { @@ -121,6 +130,31 @@ public class KeycloakAdapterConfigDeploymentProcessor implements DeploymentUnitP webMetaData.setContextParams(contextParams); } + private void addConfigurationListener(WarMetaData warMetaData) { + if (warMetaData == null) { + return; + } + + JBossWebMetaData webMetaData = warMetaData.getMergedJBossWebMetaData(); + if (webMetaData == null) { + webMetaData = new JBossWebMetaData(); + warMetaData.setMergedJBossWebMetaData(webMetaData); + } + + LoginConfigMetaData loginConfig = webMetaData.getLoginConfig(); + if (loginConfig == null) { + return; + } + if (!loginConfig.getAuthMethod().equals("KEYCLOAK")) { + return; + } + ListenerMetaData listenerMetaData = new ListenerMetaData(); + + listenerMetaData.setListenerClass(KeycloakConfigurationServletListener.class.getName()); + + webMetaData.getListeners().add(listenerMetaData); + } + @Override public void undeploy(DeploymentUnit du) { diff --git a/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakAdapterConfigService.java b/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakAdapterConfigService.java index 5a71e615a8..496c311982 100755 --- a/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakAdapterConfigService.java +++ b/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakAdapterConfigService.java @@ -23,8 +23,11 @@ import org.jboss.dmr.ModelNode; import org.jboss.dmr.Property; import org.jboss.metadata.web.jboss.JBossWebMetaData; +import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; +import java.util.Set; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ADDRESS; @@ -50,6 +53,7 @@ public final class KeycloakAdapterConfigService { // keycloak-secured deployments private final Map secureDeployments = new HashMap(); + private final Set elytronEnabledDeployments = new HashSet<>(); private KeycloakAdapterConfigService() { @@ -68,9 +72,13 @@ public final class KeycloakAdapterConfigService { this.realms.remove(realmNameFromOp(operation)); } - public void addSecureDeployment(ModelNode operation, ModelNode model) { + public void addSecureDeployment(ModelNode operation, ModelNode model, boolean elytronEnabled) { ModelNode deployment = model.clone(); - this.secureDeployments.put(deploymentNameFromOp(operation), deployment); + String name = deploymentNameFromOp(operation); + this.secureDeployments.put(name, deployment); + if (elytronEnabled) { + elytronEnabledDeployments.add(name); + } } public void updateSecureDeployment(ModelNode operation, String attrName, ModelNode resolvedValue) { @@ -79,7 +87,9 @@ public final class KeycloakAdapterConfigService { } public void removeSecureDeployment(ModelNode operation) { - this.secureDeployments.remove(deploymentNameFromOp(operation)); + String name = deploymentNameFromOp(operation); + this.secureDeployments.remove(name); + elytronEnabledDeployments.remove(name); } public void addCredential(ModelNode operation, ModelNode model) { @@ -187,7 +197,19 @@ public final class KeycloakAdapterConfigService { } private String deploymentNameFromOp(ModelNode operation) { - return valueFromOpAddress(SecureDeploymentDefinition.TAG_NAME, operation); + String deploymentName = valueFromOpAddress(SecureDeploymentDefinition.TAG_NAME, operation); + + if (deploymentName == null) { + deploymentName = valueFromOpAddress(KeycloakHttpServerAuthenticationMechanismFactoryDefinition.TAG_NAME, operation); + } + + if (deploymentName == null) { + deploymentName = valueFromOpAddress(SecureServerDefinition.TAG_NAME, operation); + } + + if (deploymentName == null) throw new RuntimeException("Can't find deployment name in address " + operation); + + return deploymentName; } private String credentialNameFromOp(ModelNode operation) { @@ -199,9 +221,7 @@ public final class KeycloakAdapterConfigService { } private String valueFromOpAddress(String addrElement, ModelNode operation) { - String deploymentName = getValueOfAddrElement(operation.get(ADDRESS), addrElement); - if (deploymentName == null) throw new RuntimeException("Can't find '" + addrElement + "' in address " + operation.toString()); - return deploymentName; + return getValueOfAddrElement(operation.get(ADDRESS), addrElement); } private String getValueOfAddrElement(ModelNode address, String elementName) { @@ -241,8 +261,22 @@ public final class KeycloakAdapterConfigService { return json.toJSONString(true); } + public String getJSON(String deploymentName) { + ModelNode deployment = this.secureDeployments.get(deploymentName); + String realmName = deployment.get(RealmDefinition.TAG_NAME).asString(); + ModelNode realm = this.realms.get(realmName); + + ModelNode json = new ModelNode(); + json.get(RealmDefinition.TAG_NAME).set(realmName); + + // Realm values set first. Some can be overridden by deployment values. + if (realm != null) setJSONValues(json, realm); + setJSONValues(json, deployment); + return json.toJSONString(true); + } + private void setJSONValues(ModelNode json, ModelNode values) { - for (Property prop : values.asPropertyList()) { + for (Property prop : new ArrayList<>(values.asPropertyList())) { String name = prop.getName(); ModelNode value = prop.getValue(); if (value.isDefined()) { @@ -258,6 +292,10 @@ public final class KeycloakAdapterConfigService { return this.secureDeployments.containsKey(deploymentName); } + public boolean isElytronEnabled(DeploymentUnit deploymentUnit) { + return elytronEnabledDeployments.contains(preferredDeploymentName(deploymentUnit)); + } + private ModelNode getSecureDeployment(DeploymentUnit deploymentUnit) { String deploymentName = preferredDeploymentName(deploymentUnit); return this.secureDeployments.containsKey(deploymentName) diff --git a/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakDependencyProcessorWildFly.java b/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakDependencyProcessorWildFly.java index e306a7f630..61d670c8cd 100755 --- a/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakDependencyProcessorWildFly.java +++ b/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakDependencyProcessorWildFly.java @@ -29,13 +29,14 @@ import org.jboss.modules.ModuleLoader; */ public class KeycloakDependencyProcessorWildFly extends KeycloakDependencyProcessor { + private static final ModuleIdentifier KEYCLOAK_ELYTRON_ADAPTER = ModuleIdentifier.create("org.keycloak.keycloak-wildfly-elytron-oidc-adapter"); private static final ModuleIdentifier KEYCLOAK_WILDFLY_ADAPTER = ModuleIdentifier.create("org.keycloak.keycloak-wildfly-adapter"); private static final ModuleIdentifier KEYCLOAK_UNDERTOW_ADAPTER = ModuleIdentifier.create("org.keycloak.keycloak-undertow-adapter"); @Override protected void addPlatformSpecificModules(ModuleSpecification moduleSpecification, ModuleLoader moduleLoader) { - // ModuleDependency(ModuleLoader moduleLoader, ModuleIdentifier identifier, boolean optional, boolean export, boolean importServices, boolean userSpecified) moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_WILDFLY_ADAPTER, false, false, true, false)); moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_UNDERTOW_ADAPTER, false, false, false, false)); + moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_ELYTRON_ADAPTER, true, false, false, false)); } } diff --git a/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakExtension.java b/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakExtension.java index d04e72d403..52113c0825 100755 --- a/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakExtension.java +++ b/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakExtension.java @@ -47,8 +47,9 @@ public class KeycloakExtension implements Extension { private static final ResourceDefinition KEYCLOAK_SUBSYSTEM_RESOURCE = new KeycloakSubsystemDefinition(); static final RealmDefinition REALM_DEFINITION = new RealmDefinition(); static final SecureDeploymentDefinition SECURE_DEPLOYMENT_DEFINITION = new SecureDeploymentDefinition(); + static final SecureServerDefinition SECURE_SERVER_DEFINITION = new SecureServerDefinition(); static final CredentialDefinition CREDENTIAL_DEFINITION = new CredentialDefinition(); - static final RedirecRewritetRuleDefinition REDIRECT_RULE_DEFINITON = new RedirecRewritetRuleDefinition(); + static final RedirecRewritetRuleDefinition REDIRECT_RULE_DEFINITON = new RedirecRewritetRuleDefinition(); public static StandardResourceDescriptionResolver getResourceDescriptionResolver(final String... keyPrefix) { StringBuilder prefix = new StringBuilder(SUBSYSTEM_NAME); @@ -80,6 +81,10 @@ public class KeycloakExtension implements Extension { secureDeploymentRegistration.registerSubModel(CREDENTIAL_DEFINITION); secureDeploymentRegistration.registerSubModel(REDIRECT_RULE_DEFINITON); + ManagementResourceRegistration secureServerRegistration = registration.registerSubModel(SECURE_SERVER_DEFINITION); + secureServerRegistration.registerSubModel(CREDENTIAL_DEFINITION); + secureServerRegistration.registerSubModel(REDIRECT_RULE_DEFINITON); + subsystem.registerXMLElementWriter(PARSER); } } diff --git a/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakHttpAuthenticationFactoryService.java b/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakHttpAuthenticationFactoryService.java new file mode 100644 index 0000000000..94fb8e566d --- /dev/null +++ b/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakHttpAuthenticationFactoryService.java @@ -0,0 +1,67 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2016 Red Hat, Inc., and individual contributors + * as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.subsystem.adapter.extension; + +import org.jboss.msc.service.Service; +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.keycloak.adapters.AdapterDeploymentContext; +import org.keycloak.adapters.KeycloakDeploymentBuilder; +import org.keycloak.adapters.elytron.KeycloakHttpServerAuthenticationMechanismFactory; +import org.wildfly.security.auth.server.SecurityDomain; +import org.wildfly.security.http.HttpServerAuthenticationMechanismFactory; +import org.wildfly.security.http.util.SetMechanismInformationMechanismFactory; + +import java.io.ByteArrayInputStream; + +/** + * @author Pedro Igor + */ +public class KeycloakHttpAuthenticationFactoryService implements Service { + + private final String factoryName; + private HttpServerAuthenticationMechanismFactory httpAuthenticationFactory; + + public KeycloakHttpAuthenticationFactoryService(String factoryName) { + this.factoryName = factoryName; + } + + @Override + public void start(StartContext context) throws StartException { + KeycloakAdapterConfigService adapterConfigService = KeycloakAdapterConfigService.getInstance(); + String config = adapterConfigService.getJSON(this.factoryName); + this.httpAuthenticationFactory = new KeycloakHttpServerAuthenticationMechanismFactory(createDeploymentContext(config.getBytes())); + } + + @Override + public void stop(StopContext context) { + this.httpAuthenticationFactory = null; + } + + @Override + public HttpServerAuthenticationMechanismFactory getValue() throws IllegalStateException, IllegalArgumentException { + return new SetMechanismInformationMechanismFactory(this.httpAuthenticationFactory); + } + + private AdapterDeploymentContext createDeploymentContext(byte[] config) { + return new AdapterDeploymentContext(KeycloakDeploymentBuilder.build(new ByteArrayInputStream(config))); + } +} diff --git a/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakHttpServerAuthenticationMechanismFactoryDefinition.java b/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakHttpServerAuthenticationMechanismFactoryDefinition.java new file mode 100644 index 0000000000..1e177a49f4 --- /dev/null +++ b/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakHttpServerAuthenticationMechanismFactoryDefinition.java @@ -0,0 +1,117 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2016 Red Hat, Inc., and individual contributors + * as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.subsystem.adapter.extension; + +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP_ADDR; +import static org.keycloak.subsystem.adapter.extension.KeycloakHttpServerAuthenticationMechanismFactoryDefinition.KeycloakHttpServerAuthenticationMechanismFactoryAddHandler.HTTP_SERVER_AUTHENTICATION_CAPABILITY; + +import org.jboss.as.controller.OperationContext; +import org.jboss.as.controller.OperationFailedException; +import org.jboss.as.controller.PathAddress; +import org.jboss.as.controller.SimpleResourceDefinition; +import org.jboss.as.controller.capability.RuntimeCapability; +import org.jboss.dmr.ModelNode; +import org.jboss.msc.service.ServiceController; +import org.jboss.msc.service.ServiceName; +import org.wildfly.security.http.HttpServerAuthenticationMechanismFactory; + +/** + * A {@link SimpleResourceDefinition} that can be used to configure a {@link org.keycloak.adapters.elytron.KeycloakHttpServerAuthenticationMechanismFactory} + * and expose it as a capability for other subsystems. + * + * @author Pedro Igor + */ +class KeycloakHttpServerAuthenticationMechanismFactoryDefinition extends AbstractAdapterConfigurationDefinition { + + static final String TAG_NAME = "http-server-mechanism-factory"; + + KeycloakHttpServerAuthenticationMechanismFactoryDefinition() { + this(TAG_NAME); + } + + KeycloakHttpServerAuthenticationMechanismFactoryDefinition(String tagName) { + super(tagName, ALL_ATTRIBUTES, new KeycloakHttpServerAuthenticationMechanismFactoryAddHandler(), new KeycloakHttpServerAuthenticationMechanismFactoryRemoveHandler(), new KeycloakHttpServerAuthenticationMechanismFactoryWriteHandler()); + } + + /** + * A {@link AbstractAdapterConfigurationAddHandler} that exposes a {@link KeycloakHttpServerAuthenticationMechanismFactoryDefinition} + * as a capability through the installation of a {@link KeycloakHttpAuthenticationFactoryService}. + * + * @author Pedro Igor + */ + static final class KeycloakHttpServerAuthenticationMechanismFactoryAddHandler extends AbstractAdapterConfigurationAddHandler { + + static final String HTTP_SERVER_AUTHENTICATION_CAPABILITY = "org.wildfly.security.http-server-mechanism-factory"; + static final RuntimeCapability HTTP_SERVER_AUTHENTICATION_RUNTIME_CAPABILITY = RuntimeCapability + .Builder.of(HTTP_SERVER_AUTHENTICATION_CAPABILITY, true, HttpServerAuthenticationMechanismFactory.class) + .build(); + + KeycloakHttpServerAuthenticationMechanismFactoryAddHandler() { + super(HTTP_SERVER_AUTHENTICATION_RUNTIME_CAPABILITY, ALL_ATTRIBUTES); + } + + @Override + protected void performRuntime(OperationContext context, ModelNode operation, ModelNode model) throws OperationFailedException { + super.performRuntime(context, operation, model); + installCapability(context, operation); + } + + static void installCapability(OperationContext context, ModelNode operation) { + PathAddress pathAddress = PathAddress.pathAddress(operation.get(OP_ADDR)); + String factoryName = pathAddress.getLastElement().getValue(); + ServiceName serviceName = context.getCapabilityServiceName(HTTP_SERVER_AUTHENTICATION_CAPABILITY, factoryName, HttpServerAuthenticationMechanismFactory.class); + KeycloakHttpAuthenticationFactoryService service = new KeycloakHttpAuthenticationFactoryService(factoryName); + context.getServiceTarget().addService(serviceName, service).setInitialMode(ServiceController.Mode.ACTIVE).install(); + } + } + + /** + * A {@link AbstractAdapterConfigurationRemoveHandler} that handles the removal of {@link KeycloakHttpServerAuthenticationMechanismFactoryDefinition}. + * + * @author Pedro Igor + */ + static final class KeycloakHttpServerAuthenticationMechanismFactoryRemoveHandler extends AbstractAdapterConfigurationRemoveHandler { + @Override + protected void performRuntime(OperationContext context, ModelNode operation, ModelNode model) throws OperationFailedException { + super.performRuntime(context, operation, model); + PathAddress pathAddress = PathAddress.pathAddress(operation.get(OP_ADDR)); + String factoryName = pathAddress.getLastElement().getValue(); + ServiceName serviceName = context.getCapabilityServiceName(HTTP_SERVER_AUTHENTICATION_CAPABILITY, factoryName, HttpServerAuthenticationMechanismFactory.class); + + context.removeService(serviceName); + } + + @Override + protected void recoverServices(OperationContext context, ModelNode operation, ModelNode model) throws OperationFailedException { + super.recoverServices(context, operation, model); + KeycloakHttpServerAuthenticationMechanismFactoryAddHandler.installCapability(context, operation); + } + } + + /** + * A {@link AbstractAdapterConfigurationWriteAttributeHandler} that updates attributes on a {@link KeycloakHttpServerAuthenticationMechanismFactoryDefinition}. + * + * @author Pedro Igor + */ + static final class KeycloakHttpServerAuthenticationMechanismFactoryWriteHandler extends AbstractAdapterConfigurationWriteAttributeHandler { + KeycloakHttpServerAuthenticationMechanismFactoryWriteHandler() { + super(ALL_ATTRIBUTES); + } + } +} diff --git a/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakSubsystemParser.java b/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakSubsystemParser.java index 79555e3b7b..a8d50aeb3d 100755 --- a/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakSubsystemParser.java +++ b/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakSubsystemParser.java @@ -62,6 +62,9 @@ class KeycloakSubsystemParser implements XMLStreamConstants, XMLElementReader
  • resourcesToAdd) throws XMLStreamException { + readSecureResource(KeycloakExtension.SECURE_DEPLOYMENT_DEFINITION.TAG_NAME, KeycloakExtension.SECURE_DEPLOYMENT_DEFINITION, reader, resourcesToAdd); + } + + private void readSecureServer(XMLExtendedStreamReader reader, List resourcesToAdd) throws XMLStreamException { + readSecureResource(KeycloakExtension.SECURE_SERVER_DEFINITION.TAG_NAME, KeycloakExtension.SECURE_SERVER_DEFINITION, reader, resourcesToAdd); + } + + private void readSecureResource(String tagName, AbstractAdapterConfigurationDefinition resource, XMLExtendedStreamReader reader, List resourcesToAdd) throws XMLStreamException { String name = readNameAttribute(reader); ModelNode addSecureDeployment = new ModelNode(); addSecureDeployment.get(ModelDescriptionConstants.OP).set(ModelDescriptionConstants.ADD); PathAddress addr = PathAddress.pathAddress(PathElement.pathElement(ModelDescriptionConstants.SUBSYSTEM, KeycloakExtension.SUBSYSTEM_NAME), - PathElement.pathElement(SecureDeploymentDefinition.TAG_NAME, name)); + PathElement.pathElement(tagName, name)); addSecureDeployment.get(ModelDescriptionConstants.OP_ADDR).set(addr.toModelNode()); List credentialsToAdd = new ArrayList(); List redirectRulesToAdd = new ArrayList(); while (reader.hasNext() && nextTag(reader) != END_ELEMENT) { - String tagName = reader.getLocalName(); - if (tagName.equals(CredentialDefinition.TAG_NAME)) { + String localName = reader.getLocalName(); + if (localName.equals(CredentialDefinition.TAG_NAME)) { readCredential(reader, addr, credentialsToAdd); continue; } - if (tagName.equals(RedirecRewritetRuleDefinition.TAG_NAME)) { + if (localName.equals(RedirecRewritetRuleDefinition.TAG_NAME)) { readRewriteRule(reader, addr, redirectRulesToAdd); continue; } - SimpleAttributeDefinition def = SecureDeploymentDefinition.lookup(tagName); - if (def == null) throw new XMLStreamException("Unknown secure-deployment tag " + tagName); + SimpleAttributeDefinition def = resource.lookup(localName); + if (def == null) throw new XMLStreamException("Unknown secure-deployment tag " + localName); def.parseAndSetParameter(reader.getElementText(), addSecureDeployment, reader); } @@ -236,6 +247,7 @@ class KeycloakSubsystemParser implements XMLStreamConstants, XMLElementReader
  • attributes, XMLExtendedStreamWriter writer, SubsystemMarshallingContext context) throws XMLStreamException { + if (!context.getModelNode().get(tagName).isDefined()) { return; } - for (Property deployment : context.getModelNode().get(SecureDeploymentDefinition.TAG_NAME).asPropertyList()) { - writer.writeStartElement(SecureDeploymentDefinition.TAG_NAME); + for (Property deployment : context.getModelNode().get(tagName).asPropertyList()) { + writer.writeStartElement(tagName); writer.writeAttribute("name", deployment.getName()); ModelNode deploymentElements = deployment.getValue(); - for (AttributeDefinition element : SecureDeploymentDefinition.ALL_ATTRIBUTES) { + for (AttributeDefinition element : attributes) { element.marshallAsElement(deploymentElements, writer); } @@ -271,7 +291,7 @@ class KeycloakSubsystemParser implements XMLStreamConstants, XMLElementReader
  • DEPLOYMENT_ONLY_ATTRIBUTES = new ArrayList(); - static { - DEPLOYMENT_ONLY_ATTRIBUTES.add(REALM); - DEPLOYMENT_ONLY_ATTRIBUTES.add(RESOURCE); - DEPLOYMENT_ONLY_ATTRIBUTES.add(USE_RESOURCE_ROLE_MAPPINGS); - DEPLOYMENT_ONLY_ATTRIBUTES.add(BEARER_ONLY); - DEPLOYMENT_ONLY_ATTRIBUTES.add(ENABLE_BASIC_AUTH); - DEPLOYMENT_ONLY_ATTRIBUTES.add(PUBLIC_CLIENT); - DEPLOYMENT_ONLY_ATTRIBUTES.add(TURN_OFF_CHANGE_SESSION); - DEPLOYMENT_ONLY_ATTRIBUTES.add(TOKEN_MINIMUM_TIME_TO_LIVE); - DEPLOYMENT_ONLY_ATTRIBUTES.add(MIN_TIME_BETWEEN_JWKS_REQUESTS); - } - - protected static final List ALL_ATTRIBUTES = new ArrayList(); - static { - ALL_ATTRIBUTES.addAll(DEPLOYMENT_ONLY_ATTRIBUTES); - ALL_ATTRIBUTES.addAll(SharedAttributeDefinitons.ATTRIBUTES); - } - - private static final Map DEFINITION_LOOKUP = new HashMap(); - static { - for (SimpleAttributeDefinition def : ALL_ATTRIBUTES) { - DEFINITION_LOOKUP.put(def.getXmlName(), def); - } - } - - private static SecureDeploymentWriteAttributeHandler attrHandler = new SecureDeploymentWriteAttributeHandler(ALL_ATTRIBUTES); + static final String TAG_NAME = "secure-deployment"; public SecureDeploymentDefinition() { - super(PathElement.pathElement(TAG_NAME), - KeycloakExtension.getResourceDescriptionResolver(TAG_NAME), - SecureDeploymentAddHandler.INSTANCE, - SecureDeploymentRemoveHandler.INSTANCE); + super(TAG_NAME, ALL_ATTRIBUTES, new SecureDeploymentAddHandler(), new SecureDeploymentRemoveHandler(), new SecureDeploymentWriteAttributeHandler()); } - @Override - public void registerOperations(ManagementResourceRegistration resourceRegistration) { - super.registerOperations(resourceRegistration); - resourceRegistration.registerOperationHandler(GenericSubsystemDescribeHandler.DEFINITION, GenericSubsystemDescribeHandler.INSTANCE); - } + /** + * Add a deployment to a realm. + * + * @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc. + */ + static final class SecureDeploymentAddHandler extends AbstractAdapterConfigurationAddHandler { - @Override - public void registerAttributes(ManagementResourceRegistration resourceRegistration) { - super.registerAttributes(resourceRegistration); - for (AttributeDefinition attrDef : ALL_ATTRIBUTES) { - resourceRegistration.registerReadWriteAttribute(attrDef, null, attrHandler); + static final String HTTP_SERVER_AUTHENTICATION_CAPABILITY = "org.wildfly.security.http-server-mechanism-factory"; + static RuntimeCapability HTTP_SERVER_AUTHENTICATION_RUNTIME_CAPABILITY; + + static { + try { + HTTP_SERVER_AUTHENTICATION_RUNTIME_CAPABILITY = RuntimeCapability + .Builder.of(HTTP_SERVER_AUTHENTICATION_CAPABILITY, true, HttpServerAuthenticationMechanismFactory.class) + .build(); + } catch (NoClassDefFoundError ncfe) { + // ignore, Elytron not present thus no capability will be published by this resource definition + } + } + + SecureDeploymentAddHandler() { + super(HTTP_SERVER_AUTHENTICATION_RUNTIME_CAPABILITY, ALL_ATTRIBUTES); + } + + @Override + protected void performRuntime(OperationContext context, ModelNode operation, ModelNode model) throws OperationFailedException { + super.performRuntime(context, operation, model); + if (HTTP_SERVER_AUTHENTICATION_RUNTIME_CAPABILITY != null) { + installCapability(context, operation); + } + } + + static void installCapability(OperationContext context, ModelNode operation) { + PathAddress pathAddress = PathAddress.pathAddress(operation.get(OP_ADDR)); + String factoryName = pathAddress.getLastElement().getValue(); + ServiceName serviceName = context.getCapabilityServiceName(HTTP_SERVER_AUTHENTICATION_CAPABILITY, factoryName, HttpServerAuthenticationMechanismFactory.class); + KeycloakHttpAuthenticationFactoryService service = new KeycloakHttpAuthenticationFactoryService(factoryName); + ServiceTarget serviceTarget = context.getServiceTarget(); + serviceTarget.addService(serviceName, service).setInitialMode(ServiceController.Mode.ACTIVE).install(); } } - public static SimpleAttributeDefinition lookup(String name) { - return DEFINITION_LOOKUP.get(name); + /** + * Remove a secure-deployment from a realm. + * + * @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc. + */ + static final class SecureDeploymentRemoveHandler extends AbstractAdapterConfigurationRemoveHandler {} + + /** + * Update an attribute on a secure-deployment. + * + * @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc. + */ + static final class SecureDeploymentWriteAttributeHandler extends AbstractAdapterConfigurationWriteAttributeHandler { + SecureDeploymentWriteAttributeHandler() { + super(ALL_ATTRIBUTES); + } } } diff --git a/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/SecureServerDefinition.java b/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/SecureServerDefinition.java new file mode 100755 index 0000000000..7d8fd05dfd --- /dev/null +++ b/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/SecureServerDefinition.java @@ -0,0 +1,258 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.subsystem.adapter.extension; + +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP_ADDR; +import static org.keycloak.subsystem.adapter.extension.KeycloakHttpServerAuthenticationMechanismFactoryDefinition.KeycloakHttpServerAuthenticationMechanismFactoryAddHandler.HTTP_SERVER_AUTHENTICATION_CAPABILITY; + +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.nio.file.Path; +import java.util.Collections; +import java.util.Date; +import java.util.List; + +import io.undertow.io.IoCallback; +import io.undertow.io.Sender; +import io.undertow.server.HttpServerExchange; +import io.undertow.server.handlers.resource.Resource; +import io.undertow.server.handlers.resource.ResourceChangeListener; +import io.undertow.server.handlers.resource.ResourceManager; +import io.undertow.util.ETag; +import io.undertow.util.MimeMappings; +import org.jboss.as.controller.OperationContext; +import org.jboss.as.controller.OperationFailedException; +import org.jboss.as.controller.PathAddress; +import org.jboss.as.controller.capability.RuntimeCapability; +import org.jboss.as.server.mgmt.domain.ExtensibleHttpManagement; +import org.jboss.dmr.ModelNode; +import org.jboss.msc.service.Service; +import org.jboss.msc.service.ServiceController.Mode; +import org.jboss.msc.service.ServiceName; +import org.jboss.msc.service.ServiceTarget; +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.security.http.HttpServerAuthenticationMechanismFactory; + +/** + * Defines attributes and operations for a secure-deployment. + * + * @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc. + */ +final class SecureServerDefinition extends AbstractAdapterConfigurationDefinition { + + public static final String TAG_NAME = "secure-server"; + + SecureServerDefinition() { + super(TAG_NAME, ALL_ATTRIBUTES, new SecureServerAddHandler(), new SecureServerRemoveHandler(), new SecureServerWriteHandler()); + } + + /** + * A {@link AbstractAdapterConfigurationAddHandler} that exposes a {@link SecureServerDefinition} + * as a capability through the installation of a {@link KeycloakHttpAuthenticationFactoryService}. + * + * @author Pedro Igor + */ + static final class SecureServerAddHandler extends AbstractAdapterConfigurationAddHandler { + + static final String HTTP_SERVER_AUTHENTICATION_CAPABILITY = "org.wildfly.security.http-server-mechanism-factory"; + static final String HTTP_MANAGEMENT_HTTP_EXTENSIBLE_CAPABILITY = "org.wildfly.management.http.extensible"; + static RuntimeCapability HTTP_SERVER_AUTHENTICATION_RUNTIME_CAPABILITY; + + static { + try { + HTTP_SERVER_AUTHENTICATION_RUNTIME_CAPABILITY = RuntimeCapability + .Builder.of(HTTP_SERVER_AUTHENTICATION_CAPABILITY, true, HttpServerAuthenticationMechanismFactory.class) + .build(); + } catch (NoClassDefFoundError ncfe) { + // ignore, Elytron not present thus no capability will be published by this resource definition + } + } + + SecureServerAddHandler() { + super(HTTP_SERVER_AUTHENTICATION_RUNTIME_CAPABILITY, ALL_ATTRIBUTES); + } + + @Override + protected void performRuntime(OperationContext context, ModelNode operation, ModelNode model) throws OperationFailedException { + super.performRuntime(context, operation, model); + if (HTTP_SERVER_AUTHENTICATION_RUNTIME_CAPABILITY != null) { + installCapability(context, operation); + } + } + + static void installCapability(OperationContext context, ModelNode operation) throws OperationFailedException { + PathAddress pathAddress = PathAddress.pathAddress(operation.get(OP_ADDR)); + String factoryName = pathAddress.getLastElement().getValue(); + ServiceName serviceName = context.getCapabilityServiceName(HTTP_SERVER_AUTHENTICATION_CAPABILITY, factoryName, HttpServerAuthenticationMechanismFactory.class); + boolean publicClient = SecureServerDefinition.PUBLIC_CLIENT.resolveModelAttribute(context, operation).asBoolean(false); + + if (!publicClient) { + throw new OperationFailedException("Only public clients are allowed to have their configuration exposed through the management interface"); + } + + KeycloakHttpAuthenticationFactoryService service = new KeycloakHttpAuthenticationFactoryService(factoryName); + ServiceTarget serviceTarget = context.getServiceTarget(); + InjectedValue injectedValue = new InjectedValue<>(); + serviceTarget.addService(serviceName.append("http-management-context"), createHttpManagementConfigContextService(factoryName, injectedValue)) + .addDependency(context.getCapabilityServiceName(HTTP_MANAGEMENT_HTTP_EXTENSIBLE_CAPABILITY, ExtensibleHttpManagement.class), ExtensibleHttpManagement.class, injectedValue).setInitialMode(Mode.ACTIVE).install(); + serviceTarget.addService(serviceName, service).setInitialMode(Mode.ACTIVE).install(); + } + } + + /** + * A {@link AbstractAdapterConfigurationRemoveHandler} that handles the removal of {@link SecureServerDefinition}. + * + * @author Pedro Igor + */ + static final class SecureServerRemoveHandler extends AbstractAdapterConfigurationRemoveHandler { + @Override + protected void performRuntime(OperationContext context, ModelNode operation, ModelNode model) throws OperationFailedException { + super.performRuntime(context, operation, model); + PathAddress pathAddress = PathAddress.pathAddress(operation.get(OP_ADDR)); + String factoryName = pathAddress.getLastElement().getValue(); + ServiceName serviceName = context.getCapabilityServiceName(HTTP_SERVER_AUTHENTICATION_CAPABILITY, factoryName, HttpServerAuthenticationMechanismFactory.class); + context.removeService(serviceName); + } + + @Override + protected void recoverServices(OperationContext context, ModelNode operation, ModelNode model) throws OperationFailedException { + super.recoverServices(context, operation, model); + SecureServerDefinition.SecureServerAddHandler.installCapability(context, operation); + } + } + + /** + * A {@link AbstractAdapterConfigurationWriteAttributeHandler} that updates attributes on a {@link SecureServerDefinition}. + * + * @author Pedro Igor + */ + static final class SecureServerWriteHandler extends AbstractAdapterConfigurationWriteAttributeHandler { + SecureServerWriteHandler() { + super(ALL_ATTRIBUTES); + } + } + + private static Service createHttpManagementConfigContextService(final String factoryName, final InjectedValue httpConfigContext) { + final String contextName = "/keycloak/adapter/" + factoryName + "/"; + return new Service() { + public void start(StartContext startContext) throws StartException { + ExtensibleHttpManagement extensibleHttpManagement = (ExtensibleHttpManagement)httpConfigContext.getValue(); + extensibleHttpManagement.addStaticContext(contextName, new ResourceManager() { + public Resource getResource(final String path) throws IOException { + KeycloakAdapterConfigService adapterConfigService = KeycloakAdapterConfigService.getInstance(); + final String config = adapterConfigService.getJSON(factoryName); + + if (config == null) { + return null; + } + + return new Resource() { + public String getPath() { + return null; + } + + public Date getLastModified() { + return null; + } + + public String getLastModifiedString() { + return null; + } + + public ETag getETag() { + return null; + } + + public String getName() { + return null; + } + + public boolean isDirectory() { + return false; + } + + public List list() { + return Collections.emptyList(); + } + + public String getContentType(MimeMappings mimeMappings) { + return "application/json"; + } + + public void serve(Sender sender, HttpServerExchange exchange, IoCallback completionCallback) { + sender.send(config); + } + + public Long getContentLength() { + return Long.valueOf((long)config.length()); + } + + public String getCacheKey() { + return null; + } + + public File getFile() { + return null; + } + + public Path getFilePath() { + return null; + } + + public File getResourceManagerRoot() { + return null; + } + + public Path getResourceManagerRootPath() { + return null; + } + + public URL getUrl() { + return null; + } + }; + } + + public boolean isResourceChangeListenerSupported() { + return false; + } + + public void registerResourceChangeListener(ResourceChangeListener listener) { + } + + public void removeResourceChangeListener(ResourceChangeListener listener) { + } + + public void close() throws IOException { + } + }); + } + + public void stop(StopContext stopContext) { + ((ExtensibleHttpManagement)httpConfigContext.getValue()).removeContext(contextName); + } + + public Void getValue() throws IllegalStateException, IllegalArgumentException { + return null; + } + }; + } +} diff --git a/adapters/oidc/wildfly/wildfly-subsystem/src/main/resources/org/keycloak/subsystem/adapter/extension/LocalDescriptions.properties b/adapters/oidc/wildfly/wildfly-subsystem/src/main/resources/org/keycloak/subsystem/adapter/extension/LocalDescriptions.properties index c9cea77787..f6097ae5cf 100755 --- a/adapters/oidc/wildfly/wildfly-subsystem/src/main/resources/org/keycloak/subsystem/adapter/extension/LocalDescriptions.properties +++ b/adapters/oidc/wildfly/wildfly-subsystem/src/main/resources/org/keycloak/subsystem/adapter/extension/LocalDescriptions.properties @@ -20,6 +20,8 @@ keycloak.subsystem.add=Operation Adds Keycloak adapter subsystem keycloak.subsystem.remove=Operation removes Keycloak adapter subsystem keycloak.subsystem.realm=A Keycloak realm. keycloak.subsystem.secure-deployment=A deployment secured by Keycloak. +keycloak.subsystem.secure-server=A configuration exposed to the server. +keycloak.subsystem.http-server-mechanism-factory=A http-server-mechanism-factory exposed to the server. keycloak.realm=A Keycloak realm. keycloak.realm.add=Add a realm definition to the subsystem. @@ -90,7 +92,48 @@ keycloak.secure-deployment.token-minimum-time-to-live=The adapter will refresh t keycloak.secure-deployment.min-time-between-jwks-requests=If adapter recognize token signed by unknown public key, it will try to download new public key from keycloak server. However it won't try to download if already tried it in less than 'min-time-between-jwks-requests' seconds keycloak.secure-deployment.ignore-oauth-query-parameter=disable query parameter parsing for access_token +keycloak.secure-server=A deployment secured by Keycloak +keycloak.secure-server.add=Add a deployment to be secured by Keycloak +keycloak.secure-server.realm=Keycloak realm +keycloak.secure-server.remove=Remove a deployment to be secured by Keycloak +keycloak.secure-server.realm-public-key=Public key of the realm +keycloak.secure-server.auth-server-url=Base URL of the Realm Auth Server +keycloak.secure-server.disable-trust-manager=Adapter will not use a trust manager when making adapter HTTPS requests +keycloak.secure-server.ssl-required=Specify if SSL is required (valid values are all, external and none) +keycloak.secure-server.allow-any-hostname=SSL Setting +keycloak.secure-server.truststore=Truststore used for adapter client HTTPS requests +keycloak.secure-server.truststore-password=Password of the Truststore +keycloak.secure-server.connection-pool-size=Connection pool size for the client used by the adapter +keycloak.secure-server.resource=Application name +keycloak.secure-server.use-resource-role-mappings=Use resource level permissions from token +keycloak.secure-server.credentials=Adapter credentials +keycloak.secure-server.redirect-rewrite-rule=Apply a rewrite rule for the redirect URI +keycloak.secure-server.bearer-only=Bearer Token Auth only +keycloak.secure-server.enable-basic-auth=Enable Basic Authentication +keycloak.secure-server.public-client=Public client +keycloak.secure-server.enable-cors=Enable Keycloak CORS support +keycloak.secure-server.autodetect-bearer-only=autodetect bearer-only requests +keycloak.secure-server.client-keystore=n/a +keycloak.secure-server.client-keystore-password=n/a +keycloak.secure-server.client-key-password=n/a +keycloak.secure-server.cors-max-age=CORS max-age header +keycloak.secure-server.cors-allowed-headers=CORS allowed headers +keycloak.secure-server.cors-allowed-methods=CORS allowed methods +keycloak.secure-server.cors-exposed-headers=CORS exposed headers +keycloak.secure-server.expose-token=Enable secure URL that exposes access token +keycloak.secure-server.auth-server-url-for-backend-requests=URL to use to make background calls to auth server +keycloak.secure-server.always-refresh-token=Refresh token on every single web request +keycloak.secure-server.register-node-at-startup=Cluster setting +keycloak.secure-server.register-node-period=how often to re-register node +keycloak.secure-server.token-store=cookie or session storage for auth session data +keycloak.secure-server.principal-attribute=token attribute to use to set Principal name +keycloak.secure-server.turn-off-change-session-id-on-login=The session id is changed by default on a successful login. Change this to true if you want to turn this off +keycloak.secure-server.token-minimum-time-to-live=The adapter will refresh the token if the current token is expired OR will expire in 'token-minimum-time-to-live' seconds or less +keycloak.secure-server.min-time-between-jwks-requests=If adapter recognize token signed by unknown public key, it will try to download new public key from keycloak server. However it won't try to download if already tried it in less than 'min-time-between-jwks-requests' seconds +keycloak.secure-server.ignore-oauth-query-parameter=disable query parameter parsing for access_token + keycloak.secure-deployment.credential=Credential value +keycloak.secure-server.credential=Credential value keycloak.credential=Credential keycloak.credential.value=Credential value diff --git a/adapters/oidc/wildfly/wildfly-subsystem/src/main/resources/schema/wildfly-keycloak_1_1.xsd b/adapters/oidc/wildfly/wildfly-subsystem/src/main/resources/schema/wildfly-keycloak_1_1.xsd index d8f5bc3d74..caa147d821 100755 --- a/adapters/oidc/wildfly/wildfly-subsystem/src/main/resources/schema/wildfly-keycloak_1_1.xsd +++ b/adapters/oidc/wildfly/wildfly-subsystem/src/main/resources/schema/wildfly-keycloak_1_1.xsd @@ -38,6 +38,7 @@ + @@ -84,7 +85,7 @@ - + @@ -99,9 +100,9 @@ - - - + + + diff --git a/adapters/oidc/wildfly/wildfly-subsystem/src/test/java/org/keycloak/subsystem/adapter/extension/SubsystemParsingTestCase.java b/adapters/oidc/wildfly/wildfly-subsystem/src/test/java/org/keycloak/subsystem/adapter/extension/SubsystemParsingTestCase.java index 9d5f87ab39..4adad9f21a 100755 --- a/adapters/oidc/wildfly/wildfly-subsystem/src/test/java/org/keycloak/subsystem/adapter/extension/SubsystemParsingTestCase.java +++ b/adapters/oidc/wildfly/wildfly-subsystem/src/test/java/org/keycloak/subsystem/adapter/extension/SubsystemParsingTestCase.java @@ -73,7 +73,7 @@ public class SubsystemParsingTestCase extends AbstractSubsystemBaseTest { ModelNode deployment = new ModelNode(); deployment.get("realm").set("demo"); deployment.get("resource").set("customer-portal"); - service.addSecureDeployment(deploymentOp, deployment); + service.addSecureDeployment(deploymentOp, deployment, false); addCredential(addr, service, "secret", "secret1"); addCredential(addr, service, "jwt.client-keystore-file", "/tmp/foo.jks"); diff --git a/adapters/oidc/wildfly/wildfly-subsystem/src/test/resources/org/keycloak/subsystem/adapter/extension/keycloak-1.1.xml b/adapters/oidc/wildfly/wildfly-subsystem/src/test/resources/org/keycloak/subsystem/adapter/extension/keycloak-1.1.xml index 246d76855f..fce5c41dae 100755 --- a/adapters/oidc/wildfly/wildfly-subsystem/src/test/resources/org/keycloak/subsystem/adapter/extension/keycloak-1.1.xml +++ b/adapters/oidc/wildfly/wildfly-subsystem/src/test/resources/org/keycloak/subsystem/adapter/extension/keycloak-1.1.xml @@ -40,6 +40,10 @@ session sub + + MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqKoq+a9MgXepmsPJDmo45qswuChW9pWjanX68oIBuI4hGvhQxFHryCow230A+sr7tFdMQMt8f1l/ysmV/fYAuW29WaoY4kI4Ou1yYPuwywKSsxT6PooTs83hKyZ1h4LZMj5DkLGDDDyVRHob2WmPaYg9RGVRw3iGGsD/p+Yb+L/gnBYQnZZ7lYqmN7h36p5CkzzlgXQA1Ha8sQxL+rJNH8+sZm0vBrKsoII3Of7TqHGsm1RwFV3XCuGJ7S61AbjJMXL5DQgJl9Z5scvxGAyoRLKC294UgMnQdzyBTMPw2GybxkRKmiK2KjQKmcopmrJp/Bt6fBR6ZkGSs9qUlxGHgwIDAQAB + http://localhost:8180/auth + master web-console @@ -69,4 +73,16 @@ /api/$1/ + + jboss-infra + wildfly-management + true + EXTERNAL + preferred_username + + + jboss-infra + wildfly-console + true + \ No newline at end of file diff --git a/distribution/feature-packs/adapter-feature-pack/src/main/resources/modules/system/add-ons/keycloak/org/keycloak/keycloak-wildfly-subsystem/main/module.xml b/distribution/feature-packs/adapter-feature-pack/src/main/resources/modules/system/add-ons/keycloak/org/keycloak/keycloak-wildfly-subsystem/main/module.xml index 025f15271b..32849fc6f1 100755 --- a/distribution/feature-packs/adapter-feature-pack/src/main/resources/modules/system/add-ons/keycloak/org/keycloak/keycloak-wildfly-subsystem/main/module.xml +++ b/distribution/feature-packs/adapter-feature-pack/src/main/resources/modules/system/add-ons/keycloak/org/keycloak/keycloak-wildfly-subsystem/main/module.xml @@ -38,5 +38,9 @@ + + + + diff --git a/testsuite/integration-arquillian/pom.xml b/testsuite/integration-arquillian/pom.xml index cb25e181fa..db78203044 100644 --- a/testsuite/integration-arquillian/pom.xml +++ b/testsuite/integration-arquillian/pom.xml @@ -45,9 +45,9 @@ 2.53.0 2.0.1.Final 2.1.0.Alpha3 - 2.1.0.Beta1 + 2.1.0.Alpha2 1.0.1.Final - 1.2.0.Beta2 + 1.2.0.Alpha2 2.2.2 1.0.0.Alpha2