diff --git a/core/src/main/java/org/keycloak/adapters/AdapterConstants.java b/core/src/main/java/org/keycloak/adapters/AdapterConstants.java
index 7ad2d85631..73f221ca8a 100755
--- a/core/src/main/java/org/keycloak/adapters/AdapterConstants.java
+++ b/core/src/main/java/org/keycloak/adapters/AdapterConstants.java
@@ -9,4 +9,9 @@ public interface AdapterConstants {
// URL endpoints
public static final String K_LOGOUT = "k_logout";
public static final String K_QUERY_BEARER_TOKEN = "k_query_bearer_token";
+
+ // This param name is defined again in Keycloak Subsystem class
+ // org.keycloak.subsystem.extensionKeycloakAdapterConfigDeploymentProcessor. We have this value in
+ // two places to avoid dependency between Keycloak Subsystem and Keyclaok Undertow Integration.
+ String AUTH_DATA_PARAM_NAME = "org.keycloak.json.adapterConfig";
}
diff --git a/core/src/test/java/org/keycloak/SkeletonKeyTokenTest.java b/core/src/test/java/org/keycloak/SkeletonKeyTokenTest.java
index e4df1240ba..f016b4855f 100755
--- a/core/src/test/java/org/keycloak/SkeletonKeyTokenTest.java
+++ b/core/src/test/java/org/keycloak/SkeletonKeyTokenTest.java
@@ -5,10 +5,11 @@ import org.junit.Test;
import org.keycloak.jose.jws.JWSBuilder;
import org.keycloak.jose.jws.JWSInput;
import org.keycloak.jose.jws.crypto.RSAProvider;
-import org.keycloak.util.JsonSerialization;
import org.keycloak.representations.SkeletonKeyScope;
import org.keycloak.representations.SkeletonKeyToken;
+import org.keycloak.util.JsonSerialization;
+import java.io.IOException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
@@ -16,61 +17,93 @@ import java.security.KeyPairGenerator;
* @author Bill Burke
* @version $Revision: 1 $
*/
-public class SkeletonKeyTokenTest
-{
- @Test
- public void testScope() throws Exception
- {
- SkeletonKeyScope scope2 = new SkeletonKeyScope();
+public class SkeletonKeyTokenTest {
+ private static class Parser implements Runnable {
+ private String json;
- scope2.add("one", "admin");
- scope2.add("one", "buyer");
- scope2.add("two", "seller");
- String json = JsonSerialization.writeValueAsString(scope2);
- System.out.println(json);
+ private Parser(String json) {
+ this.json = json;
+ }
+
+ @Override
+ public void run() {
+ for (int i = 0; i < 10000; i++) {
+ try {
+ SkeletonKeyScope scope = JsonSerialization.readValue(json.getBytes(), SkeletonKeyScope.class);
+ } catch (IOException e) {
+
+ }
+ }
+ }
+ }
+
+ @Test
+ public void testScope() throws Exception {
+ SkeletonKeyScope scope2 = new SkeletonKeyScope();
+
+ scope2.add("one", "admin");
+ scope2.add("one", "buyer");
+ scope2.add("two", "seller");
+ String json = JsonSerialization.writeValueAsString(scope2);
+ System.out.println(json);
+
+ /*
+
+ Thread[] threads = new Thread[1000];
+ for (int i = 0; i < 1000; i++) {
+ threads[i] = new Thread(new Parser(json));
+ }
+ long start = System.currentTimeMillis();
+ for (Thread thread : threads) {
+ thread.start();
+ }
+ for (Thread thread : threads) {
+ thread.join();
+ }
+ long end = System.currentTimeMillis() - start;
+ System.out.println("Time took: " + end);
+ */
- }
+ }
- @Test
- public void testToken() throws Exception
- {
- SkeletonKeyToken token = new SkeletonKeyToken();
- token.id("111");
- token.addAccess("foo").addRole("admin");
- token.addAccess("bar").addRole("user");
+ @Test
+ public void testToken() throws Exception {
+ SkeletonKeyToken token = new SkeletonKeyToken();
+ token.id("111");
+ token.addAccess("foo").addRole("admin");
+ token.addAccess("bar").addRole("user");
- String json = JsonSerialization.writeValueAsString(token);
- System.out.println(json);
+ String json = JsonSerialization.writeValueAsString(token);
+ System.out.println(json);
- token = JsonSerialization.readValue(json, SkeletonKeyToken.class);
- Assert.assertEquals("111", token.getId());
- SkeletonKeyToken.Access foo = token.getResourceAccess("foo");
- Assert.assertNotNull(foo);
- Assert.assertTrue(foo.isUserInRole("admin"));
+ token = JsonSerialization.readValue(json, SkeletonKeyToken.class);
+ Assert.assertEquals("111", token.getId());
+ SkeletonKeyToken.Access foo = token.getResourceAccess("foo");
+ Assert.assertNotNull(foo);
+ Assert.assertTrue(foo.isUserInRole("admin"));
- }
+ }
- @Test
- public void testRSA() throws Exception
- {
- SkeletonKeyToken token = new SkeletonKeyToken();
- token.id("111");
- token.addAccess("foo").addRole("admin");
- token.addAccess("bar").addRole("user");
+ @Test
+ public void testRSA() throws Exception {
+ SkeletonKeyToken token = new SkeletonKeyToken();
+ token.id("111");
+ token.addAccess("foo").addRole("admin");
+ token.addAccess("bar").addRole("user");
- KeyPair keyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair();
+ KeyPair keyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair();
- String encoded = new JWSBuilder()
- .jsonContent(token)
- .rsa256(keyPair.getPrivate());
+ String encoded = new JWSBuilder()
+ .jsonContent(token)
+ .rsa256(keyPair.getPrivate());
- System.out.println(encoded);
+ System.out.println(encoded);
- JWSInput input = new JWSInput(encoded);
+ JWSInput input = new JWSInput(encoded);
- token = input.readJsonContent(SkeletonKeyToken.class);
- Assert.assertEquals("111", token.getId());
- Assert.assertTrue(RSAProvider.verify(input, keyPair.getPublic()));
- }
+ token = input.readJsonContent(SkeletonKeyToken.class);
+ Assert.assertEquals("111", token.getId());
+ Assert.assertTrue(RSAProvider.verify(input, keyPair.getPublic()));
+ }
}
diff --git a/distribution/eap6-adapter-zip/assembly.xml b/distribution/eap6-adapter-zip/assembly.xml
index 6fe463bec2..d3d334ac02 100755
--- a/distribution/eap6-adapter-zip/assembly.xml
+++ b/distribution/eap6-adapter-zip/assembly.xml
@@ -11,6 +11,7 @@
${project.build.directory}/unpackedorg/keycloak/keycloak-undertow-adapter/**
+ org/keycloak/keycloak-wildfly-subsystem/**modules/system/layers/base
diff --git a/distribution/modules/build.xml b/distribution/modules/build.xml
index 8f3b099d40..16694d4fb2 100755
--- a/distribution/modules/build.xml
+++ b/distribution/modules/build.xml
@@ -70,6 +70,9 @@
+
+
+
diff --git a/distribution/modules/pom.xml b/distribution/modules/pom.xml
index af567d238e..1e29d5209f 100755
--- a/distribution/modules/pom.xml
+++ b/distribution/modules/pom.xml
@@ -64,6 +64,11 @@
keycloak-wildfly-subsystem${project.version}
+
+ org.keycloak
+ keycloak-as7-subsystem
+ ${project.version}
+ org.bouncycastlebcprov-jdk16
diff --git a/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-as7-subsystem/main/module.xml b/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-as7-subsystem/main/module.xml
new file mode 100755
index 0000000000..37603763e7
--- /dev/null
+++ b/distribution/modules/src/main/resources/modules/org/keycloak/keycloak-as7-subsystem/main/module.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/distribution/wildfly-adapter-zip/assembly.xml b/distribution/wildfly-adapter-zip/assembly.xml
index b824efe3f5..76d1f183e4 100755
--- a/distribution/wildfly-adapter-zip/assembly.xml
+++ b/distribution/wildfly-adapter-zip/assembly.xml
@@ -11,6 +11,7 @@
${project.build.directory}/unpackedorg/keycloak/keycloak-as7-adapter/**
+ org/keycloak/keycloak-as7-subsystem/**org/bouncycastle/**modules/system/layers/base
diff --git a/integration/as7-eap-subsystem/pom.xml b/integration/as7-eap-subsystem/pom.xml
new file mode 100755
index 0000000000..db43e456db
--- /dev/null
+++ b/integration/as7-eap-subsystem/pom.xml
@@ -0,0 +1,151 @@
+
+
+
+ 4.0.0
+
+
+ org.keycloak
+ keycloak-parent
+ 1.0-alpha-2-SNAPSHOT
+
+
+ org.keycloak
+ keycloak-as7-subsystem
+ 1.0-alpha-2-SNAPSHOT
+
+ Keycloak Wildfly Subsystem
+
+ jar
+
+
+
+
+ maven-compiler-plugin
+ 2.3.1
+
+
+ 1.6
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 2.8.1
+
+ false
+ true
+ -Xmx512m
+
+
+ jboss.home
+ ${jboss.home}
+
+
+
+ **/*TestCase.java
+
+ once
+
+
+
+
+
+
+
+
+
+ org.keycloak
+ keycloak-as7-adapter
+ ${project.version}
+
+
+
+ org.jboss.as
+ jboss-as-naming
+ 7.1.1.Final
+
+
+
+ org.jboss.as
+ jboss-as-server
+ 7.1.1.Final
+
+
+
+ org.jboss.as
+ jboss-as-ee
+ 7.1.1.Final
+
+
+
+ org.jboss.as
+ jboss-as-web
+ 7.1.1.Final
+
+
+
+ org.jboss.logging
+ jboss-logging
+ 3.1.0.GA
+
+
+
+ org.jboss.logging
+ jboss-logging-processor
+
+ provided
+ true
+ 1.0.0.Final
+
+
+
+ org.jboss.msc
+ jboss-msc
+ 1.0.2.GA
+
+
+
+ junit
+ junit
+ test
+
+
+
diff --git a/integration/as7-eap-subsystem/src/main/java/org/keycloak/subsystem/extension/AbstractAddStepHandlerWithAttributes.java b/integration/as7-eap-subsystem/src/main/java/org/keycloak/subsystem/extension/AbstractAddStepHandlerWithAttributes.java
new file mode 100755
index 0000000000..db5d1e5fc2
--- /dev/null
+++ b/integration/as7-eap-subsystem/src/main/java/org/keycloak/subsystem/extension/AbstractAddStepHandlerWithAttributes.java
@@ -0,0 +1,57 @@
+package org.keycloak.subsystem.extension;
+
+import org.jboss.as.controller.AbstractAddStepHandler;
+import org.jboss.as.controller.AttributeDefinition;
+import org.jboss.as.controller.OperationFailedException;
+import org.jboss.dmr.ModelNode;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+
+/**
+ * @author Bill Burke
+ * @version $Revision: 1 $
+ */
+public class AbstractAddStepHandlerWithAttributes extends AbstractAddStepHandler {
+ protected Collection extends AttributeDefinition> attributes;
+
+ public AbstractAddStepHandlerWithAttributes(){ //default constructor to preserve backward compatibility
+
+ }
+
+ public AbstractAddStepHandlerWithAttributes(Collection extends AttributeDefinition> attributes) {
+ this.attributes = attributes;
+ }
+
+ /**
+ * Constructs add handler
+ *
+ * @param attributes for which model will be populated
+ */
+ public AbstractAddStepHandlerWithAttributes(AttributeDefinition... attributes) {
+ if (attributes.length > 0) {
+ this.attributes = Arrays.asList(attributes);
+ } else {
+ this.attributes = Collections.emptySet();
+ }
+ }
+
+ /**
+ * Populate the given node in the persistent configuration model based on the values in the given operation.
+ *
+ * @param operation the operation
+ * @param model persistent configuration model node that corresponds to the address of {@code operation}
+ *
+ * @throws org.jboss.as.controller.OperationFailedException if {@code operation} is invalid or populating the model otherwise fails
+ */
+ protected void populateModel(final ModelNode operation, final ModelNode model) throws OperationFailedException {
+ if (attributes != null) {
+ for (AttributeDefinition attr : attributes) {
+ attr.validateAndSet(operation, model);
+ }
+ }
+ }
+
+
+}
diff --git a/integration/as7-eap-subsystem/src/main/java/org/keycloak/subsystem/extension/CredentialAddHandler.java b/integration/as7-eap-subsystem/src/main/java/org/keycloak/subsystem/extension/CredentialAddHandler.java
new file mode 100755
index 0000000000..9b8a28bd5d
--- /dev/null
+++ b/integration/as7-eap-subsystem/src/main/java/org/keycloak/subsystem/extension/CredentialAddHandler.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2014 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * 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.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.ServiceVerificationHandler;
+import org.jboss.dmr.ModelNode;
+import org.jboss.msc.service.ServiceController;
+
+/**
+ * Add a credential to a deployment.
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2014 Red Hat Inc.
+ */
+public class CredentialAddHandler extends AbstractAddStepHandlerWithAttributes {
+
+ public CredentialAddHandler(AttributeDefinition... attributes) {
+ super(attributes);
+ }
+
+ @Override
+ protected void performRuntime(OperationContext context, ModelNode operation, ModelNode model, ServiceVerificationHandler verificationHandler, List> newControllers) throws OperationFailedException {
+ KeycloakAdapterConfigService ckService = KeycloakAdapterConfigService.find(context);
+ ckService.addCredential(operation, model);
+ }
+
+}
diff --git a/integration/as7-eap-subsystem/src/main/java/org/keycloak/subsystem/extension/CredentialDefinition.java b/integration/as7-eap-subsystem/src/main/java/org/keycloak/subsystem/extension/CredentialDefinition.java
new file mode 100755
index 0000000000..681d4d9caa
--- /dev/null
+++ b/integration/as7-eap-subsystem/src/main/java/org/keycloak/subsystem/extension/CredentialDefinition.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * 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.extension;
+
+import org.jboss.as.controller.AttributeDefinition;
+import org.jboss.as.controller.PathElement;
+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.StringLengthValidator;
+import org.jboss.as.controller.registry.ManagementResourceRegistration;
+import org.jboss.as.controller.registry.OperationEntry;
+import org.jboss.dmr.ModelType;
+
+import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.DESCRIBE;
+
+/**
+ * Defines attributes and operations for a credential.
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc.
+ */
+public class CredentialDefinition extends SimpleResourceDefinition {
+
+ public static final String TAG_NAME = "credential";
+
+ protected static final AttributeDefinition VALUE =
+ new SimpleAttributeDefinitionBuilder("value", ModelType.STRING, false)
+ .setXmlName("value")
+ .setAllowExpression(true)
+ .setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, false, true))
+ .build();
+
+ public CredentialDefinition() {
+ super(PathElement.pathElement(TAG_NAME),
+ KeycloakExtension.getResourceDescriptionResolver(TAG_NAME),
+ new CredentialAddHandler(VALUE),
+ CredentialRemoveHandler.INSTANCE);
+ }
+
+ @Override
+ public void registerOperations(ManagementResourceRegistration resourceRegistration) {
+ super.registerOperations(resourceRegistration);
+ resourceRegistration.registerOperationHandler(DESCRIBE, GenericSubsystemDescribeHandler.INSTANCE, GenericSubsystemDescribeHandler.INSTANCE, false, OperationEntry.EntryType.PRIVATE);
+ //resourceRegistration.registerOperationHandler(GenericSubsystemDescribeHandler.DEFINITION, GenericSubsystemDescribeHandler.INSTANCE);
+ }
+
+ @Override
+ public void registerAttributes(ManagementResourceRegistration resourceRegistration) {
+ super.registerAttributes(resourceRegistration);
+ resourceRegistration.registerReadWriteAttribute(VALUE, null, new CredentialReadWriteAttributeHandler());
+ }
+}
diff --git a/integration/as7-eap-subsystem/src/main/java/org/keycloak/subsystem/extension/CredentialReadWriteAttributeHandler.java b/integration/as7-eap-subsystem/src/main/java/org/keycloak/subsystem/extension/CredentialReadWriteAttributeHandler.java
new file mode 100755
index 0000000000..6289ff4539
--- /dev/null
+++ b/integration/as7-eap-subsystem/src/main/java/org/keycloak/subsystem/extension/CredentialReadWriteAttributeHandler.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2014 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * 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.extension;
+
+import org.jboss.as.controller.AbstractWriteAttributeHandler;
+import org.jboss.as.controller.OperationContext;
+import org.jboss.as.controller.OperationFailedException;
+import org.jboss.dmr.ModelNode;
+
+/**
+ * Update a credential value.
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2014 Red Hat Inc.
+ */
+public class CredentialReadWriteAttributeHandler extends AbstractWriteAttributeHandler {
+
+ @Override
+ protected boolean applyUpdateToRuntime(OperationContext context, ModelNode operation, String attributeName,
+ ModelNode resolvedValue, ModelNode currentValue, AbstractWriteAttributeHandler.HandbackHolder hh) throws OperationFailedException {
+
+ KeycloakAdapterConfigService ckService = KeycloakAdapterConfigService.find(context);
+ ckService.updateCredential(operation, attributeName, resolvedValue);
+
+ hh.setHandback(ckService);
+
+ return false;
+ }
+
+ @Override
+ protected void revertUpdateToRuntime(OperationContext context, ModelNode operation, String attributeName,
+ ModelNode valueToRestore, ModelNode valueToRevert, KeycloakAdapterConfigService ckService) throws OperationFailedException {
+ ckService.updateCredential(operation, attributeName, valueToRestore);
+ }
+
+}
diff --git a/integration/as7-eap-subsystem/src/main/java/org/keycloak/subsystem/extension/CredentialRemoveHandler.java b/integration/as7-eap-subsystem/src/main/java/org/keycloak/subsystem/extension/CredentialRemoveHandler.java
new file mode 100755
index 0000000000..1cad10bfb0
--- /dev/null
+++ b/integration/as7-eap-subsystem/src/main/java/org/keycloak/subsystem/extension/CredentialRemoveHandler.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2014 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * 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.extension;
+
+import org.jboss.as.controller.AbstractRemoveStepHandler;
+import org.jboss.as.controller.OperationContext;
+import org.jboss.as.controller.OperationFailedException;
+import org.jboss.dmr.ModelNode;
+
+/**
+ * Remove a credential from a deployment.
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2014 Red Hat Inc.
+ */
+public final class CredentialRemoveHandler extends AbstractRemoveStepHandler {
+
+ public static CredentialRemoveHandler INSTANCE = new CredentialRemoveHandler();
+
+ private CredentialRemoveHandler() {}
+
+ @Override
+ protected void performRuntime(OperationContext context, ModelNode operation, ModelNode model) throws OperationFailedException {
+ KeycloakAdapterConfigService ckService = KeycloakAdapterConfigService.find(context);
+ ckService.removeCredential(operation);
+ }
+
+}
diff --git a/integration/as7-eap-subsystem/src/main/java/org/keycloak/subsystem/extension/KeycloakAdapterConfigDeploymentProcessor.java b/integration/as7-eap-subsystem/src/main/java/org/keycloak/subsystem/extension/KeycloakAdapterConfigDeploymentProcessor.java
new file mode 100755
index 0000000000..c60769eaf4
--- /dev/null
+++ b/integration/as7-eap-subsystem/src/main/java/org/keycloak/subsystem/extension/KeycloakAdapterConfigDeploymentProcessor.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2014 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * 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.extension;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.jboss.as.server.deployment.DeploymentPhaseContext;
+import org.jboss.as.server.deployment.DeploymentUnit;
+import org.jboss.as.server.deployment.DeploymentUnitProcessingException;
+import org.jboss.as.server.deployment.DeploymentUnitProcessor;
+import org.jboss.as.server.deployment.Phase;
+import org.jboss.as.web.deployment.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.jboss.ValveMetaData;
+import org.jboss.metadata.web.spec.LoginConfigMetaData;
+import org.keycloak.adapters.as7.KeycloakAuthenticatorValve;
+
+/**
+ * Pass authentication data (keycloak.json) as a servlet context param so it can be read by the KeycloakServletExtension.
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2014 Red Hat Inc.
+ */
+public class KeycloakAdapterConfigDeploymentProcessor implements DeploymentUnitProcessor {
+ protected Logger log = Logger.getLogger(KeycloakAdapterConfigDeploymentProcessor.class);
+
+ // This param name is defined again in Keycloak Undertow Integration class
+ // org.keycloak.adapters.undertow.KeycloakServletExtension. We have this value in
+ // two places to avoid dependency between Keycloak Subsystem and Keyclaok Undertow Integration.
+ public static final String AUTH_DATA_PARAM_NAME = "org.keycloak.json.adapterConfig";
+
+ public static final Phase PHASE = Phase.INSTALL;
+ // needs to run before INSTALL_WAR_DEPLOYMENT so that valves are added.
+ public static final int PRIORITY = Phase.INSTALL_WAR_DEPLOYMENT - 1;
+
+ @Override
+ public void deploy(DeploymentPhaseContext phaseContext) throws DeploymentUnitProcessingException {
+ DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit();
+ String deploymentName = deploymentUnit.getName();
+
+ KeycloakAdapterConfigService service = KeycloakAdapterConfigService.find(phaseContext.getServiceRegistry());
+ //log.info("********* CHECK KEYCLOAK DEPLOYMENT: " + deploymentName);
+ if (service.isKeycloakDeployment(deploymentName)) {
+
+ addKeycloakAuthData(phaseContext, deploymentName, service);
+ }
+ }
+
+ private void addKeycloakAuthData(DeploymentPhaseContext phaseContext, String deploymentName, KeycloakAdapterConfigService service) {
+ DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit();
+ WarMetaData warMetaData = deploymentUnit.getAttachment(WarMetaData.ATTACHMENT_KEY);
+
+ addJSONData(service.getJSON(deploymentName), warMetaData);
+ JBossWebMetaData webMetaData = warMetaData.getMergedJBossWebMetaData();
+ if (webMetaData == null) {
+ webMetaData = new JBossWebMetaData();
+ warMetaData.setMergedJBossWebMetaData(webMetaData);
+ }
+ List valves = webMetaData.getValves();
+ if (valves == null) {
+ valves = new ArrayList(1);
+ webMetaData.setValves(valves);
+ }
+ ValveMetaData valve = new ValveMetaData();
+ valve.setValveClass(KeycloakAuthenticatorValve.class.getName());
+ valve.setModule("org.keycloak.keycloak-as7-adapter");
+ log.info("******* adding Keycloak valve to: " + deploymentName);
+ valves.add(valve);
+
+ /*
+ LoginConfigMetaData loginConfig = webMetaData.getLoginConfig();
+ if (loginConfig == null) {
+ loginConfig = new LoginConfigMetaData();
+ webMetaData.setLoginConfig(loginConfig);
+ }
+ loginConfig.setAuthMethod("KEYCLOAK");
+ loginConfig.setRealmName(service.getRealmName(deploymentName));
+ */
+
+ }
+
+ private void addJSONData(String json, WarMetaData warMetaData) {
+ JBossWebMetaData webMetaData = warMetaData.getMergedJBossWebMetaData();
+ if (webMetaData == null) {
+ webMetaData = new JBossWebMetaData();
+ warMetaData.setMergedJBossWebMetaData(webMetaData);
+ }
+
+ List contextParams = webMetaData.getContextParams();
+ if (contextParams == null) {
+ contextParams = new ArrayList();
+ }
+
+ ParamValueMetaData param = new ParamValueMetaData();
+ param.setParamName(AUTH_DATA_PARAM_NAME);
+ param.setParamValue(json);
+ contextParams.add(param);
+
+ webMetaData.setContextParams(contextParams);
+ }
+
+ @Override
+ public void undeploy(DeploymentUnit du) {
+
+ }
+
+}
diff --git a/integration/as7-eap-subsystem/src/main/java/org/keycloak/subsystem/extension/KeycloakAdapterConfigService.java b/integration/as7-eap-subsystem/src/main/java/org/keycloak/subsystem/extension/KeycloakAdapterConfigService.java
new file mode 100755
index 0000000000..ad31d60a54
--- /dev/null
+++ b/integration/as7-eap-subsystem/src/main/java/org/keycloak/subsystem/extension/KeycloakAdapterConfigService.java
@@ -0,0 +1,214 @@
+/*
+ * Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * 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.extension;
+
+import java.util.HashMap;
+import java.util.Map;
+import org.jboss.as.controller.OperationContext;
+import org.jboss.dmr.ModelNode;
+import org.jboss.dmr.Property;
+import org.jboss.logging.Logger;
+import org.jboss.msc.service.Service;
+import org.jboss.msc.service.ServiceController;
+import org.jboss.msc.service.ServiceName;
+import org.jboss.msc.service.ServiceRegistry;
+import org.jboss.msc.service.StartContext;
+import org.jboss.msc.service.StartException;
+import org.jboss.msc.service.StopContext;
+
+import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ADDRESS;
+
+/**
+ * This service keeps track of the entire Keycloak management model so as to provide
+ * adapter configuration to each deployment at deploy time.
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc.
+ */
+public final class KeycloakAdapterConfigService implements Service {
+ protected Logger log = Logger.getLogger(KeycloakAdapterConfigService.class);
+ private static final String CREDENTIALS_JSON_NAME = "credentials";
+
+ // Right now this is used as a service, but I'm not sure it really needs to be implemented that way.
+ // It's also a singleton serving the entire subsystem, but the INSTANCE variable is currently only
+ // used during initialization of the subsystem.
+ public static final ServiceName SERVICE_NAME = ServiceName.JBOSS.append("KeycloakAdapterConfigService");
+ public static final KeycloakAdapterConfigService INSTANCE = new KeycloakAdapterConfigService();
+
+ private Map realms = new HashMap();
+ private Map deployments = new HashMap();
+
+ private KeycloakAdapterConfigService() {
+
+ }
+
+ @Override
+ public void start(StartContext sc) throws StartException {
+
+ }
+
+ @Override
+ public void stop(StopContext sc) {
+
+ }
+
+ @Override
+ public KeycloakAdapterConfigService getValue() throws IllegalStateException, IllegalArgumentException {
+ return this;
+ }
+
+ public void addRealm(ModelNode operation, ModelNode model) {
+ this.realms.put(realmNameFromOp(operation), model.clone());
+ }
+
+ public void updateRealm(ModelNode operation, String attrName, ModelNode resolvedValue) {
+ ModelNode realm = this.realms.get(realmNameFromOp(operation));
+ realm.get(attrName).set(resolvedValue);
+ }
+
+ public void removeRealm(ModelNode operation) {
+ this.realms.remove(realmNameFromOp(operation));
+ }
+
+ public void addSecureDeployment(ModelNode operation, ModelNode model) {
+ ModelNode deployment = model.clone();
+ deployment.get(RealmDefinition.TAG_NAME).set(realmNameFromOp(operation));
+ this.deployments.put(deploymentNameFromOp(operation), deployment);
+ }
+
+ public void updateSecureDeployment(ModelNode operation, String attrName, ModelNode resolvedValue) {
+ ModelNode deployment = this.deployments.get(deploymentNameFromOp(operation));
+ deployment.get(attrName).set(resolvedValue);
+ }
+
+ public void removeSecureDeployment(ModelNode operation) {
+ this.deployments.remove(deploymentNameFromOp(operation));
+ }
+
+ public void addCredential(ModelNode operation, ModelNode model) {
+ ModelNode credentials = credentialsFromOp(operation);
+ if (!credentials.isDefined()) {
+ credentials = new ModelNode();
+ }
+
+ String credentialName = credentialNameFromOp(operation);
+ credentials.get(credentialName).set(model.get("value").asString());
+
+ ModelNode deployment = this.deployments.get(deploymentNameFromOp(operation));
+ deployment.get(CREDENTIALS_JSON_NAME).set(credentials);
+ }
+
+ public void removeCredential(ModelNode operation) {
+ ModelNode credentials = credentialsFromOp(operation);
+ if (!credentials.isDefined()) {
+ throw new RuntimeException("Can not remove credential. No credential defined for deployment in op " + operation.toString());
+ }
+
+ String credentialName = credentialNameFromOp(operation);
+ credentials.remove(credentialName);
+ }
+
+ public void updateCredential(ModelNode operation, String attrName, ModelNode resolvedValue) {
+ ModelNode credentials = credentialsFromOp(operation);
+ if (!credentials.isDefined()) {
+ throw new RuntimeException("Can not update credential. No credential defined for deployment in op " + operation.toString());
+ }
+
+ String credentialName = credentialNameFromOp(operation);
+ credentials.get(credentialName).set(resolvedValue);
+ }
+
+ private ModelNode credentialsFromOp(ModelNode operation) {
+ ModelNode deployment = this.deployments.get(deploymentNameFromOp(operation));
+ return deployment.get(CREDENTIALS_JSON_NAME);
+ }
+
+ private String realmNameFromOp(ModelNode operation) {
+ return valueFromOpAddress(RealmDefinition.TAG_NAME, operation);
+ }
+
+ private String deploymentNameFromOp(ModelNode operation) {
+ return valueFromOpAddress(SecureDeploymentDefinition.TAG_NAME, operation);
+ }
+
+ private String credentialNameFromOp(ModelNode operation) {
+ return valueFromOpAddress(CredentialDefinition.TAG_NAME, operation);
+ }
+
+ 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;
+ }
+
+ private String getValueOfAddrElement(ModelNode address, String elementName) {
+ for (ModelNode element : address.asList()) {
+ if (element.has(elementName)) return element.get(elementName).asString();
+ }
+
+ return null;
+ }
+
+ public String getRealmName(String deploymentName) {
+ ModelNode deployment = this.deployments.get(deploymentName);
+ return deployment.get(RealmDefinition.TAG_NAME).asString();
+
+ }
+
+ public String getJSON(String deploymentName) {
+ ModelNode deployment = this.deployments.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.
+ setJSONValues(json, realm);
+ setJSONValues(json, deployment);
+ return json.toJSONString(true);
+ }
+
+ private void setJSONValues(ModelNode json, ModelNode values) {
+ for (Property prop : values.asPropertyList()) {
+ String name = prop.getName();
+ ModelNode value = prop.getValue();
+ if (value.isDefined()) {
+ json.get(name).set(value);
+ }
+ }
+ }
+
+ public boolean isKeycloakDeployment(String deploymentName) {
+ //log.info("********* CHECK KEYCLOAK DEPLOYMENT: deployments.size()" + deployments.size());
+
+ return this.deployments.containsKey(deploymentName);
+ }
+
+ static KeycloakAdapterConfigService find(ServiceRegistry registry) {
+ ServiceController> container = registry.getService(KeycloakAdapterConfigService.SERVICE_NAME);
+ if (container != null) {
+ KeycloakAdapterConfigService service = (KeycloakAdapterConfigService)container.getValue();
+ return service;
+ }
+ return null;
+ }
+
+ static KeycloakAdapterConfigService find(OperationContext context) {
+ return find(context.getServiceRegistry(true));
+ }
+}
diff --git a/integration/as7-eap-subsystem/src/main/java/org/keycloak/subsystem/extension/KeycloakDependencyProcessor.java b/integration/as7-eap-subsystem/src/main/java/org/keycloak/subsystem/extension/KeycloakDependencyProcessor.java
new file mode 100755
index 0000000000..889d8fa936
--- /dev/null
+++ b/integration/as7-eap-subsystem/src/main/java/org/keycloak/subsystem/extension/KeycloakDependencyProcessor.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * 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.extension;
+
+import org.jboss.as.server.deployment.Attachments;
+import org.jboss.as.server.deployment.DeploymentPhaseContext;
+import org.jboss.as.server.deployment.DeploymentUnit;
+import org.jboss.as.server.deployment.DeploymentUnitProcessingException;
+import org.jboss.as.server.deployment.DeploymentUnitProcessor;
+import org.jboss.as.server.deployment.module.ModuleDependency;
+import org.jboss.as.server.deployment.module.ModuleSpecification;
+import org.jboss.modules.Module;
+import org.jboss.modules.ModuleIdentifier;
+import org.jboss.modules.ModuleLoader;
+
+/**
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc.
+ */
+public class KeycloakDependencyProcessor implements DeploymentUnitProcessor {
+
+ private static final ModuleIdentifier KEYCLOAK_AS7_ADAPTER = ModuleIdentifier.create("org.keycloak.keycloak-as7-adapter");
+ private static final ModuleIdentifier KEYCLOAK_CORE_ADAPTER = ModuleIdentifier.create("org.keycloak.keycloak-adapter-core");
+ private static final ModuleIdentifier APACHE_HTTPCOMPONENTS = ModuleIdentifier.create("org.apache.httpcomponents");
+
+ @Override
+ public void deploy(DeploymentPhaseContext phaseContext) throws DeploymentUnitProcessingException {
+ final DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit();
+
+ KeycloakAdapterConfigService service = KeycloakAdapterConfigService.find(phaseContext.getServiceRegistry());
+ if (service.isKeycloakDeployment(deploymentUnit.getName())) {
+ addModules(deploymentUnit);
+ }
+ }
+
+ private void addModules(DeploymentUnit deploymentUnit) {
+ final ModuleSpecification moduleSpecification = deploymentUnit.getAttachment(Attachments.MODULE_SPECIFICATION);
+ final ModuleLoader moduleLoader = Module.getBootModuleLoader();
+
+ moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_AS7_ADAPTER, false, false, true, false));
+ moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_CORE_ADAPTER, false, false, false, false));
+ moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, APACHE_HTTPCOMPONENTS, false, false, true, false));
+ }
+
+ @Override
+ public void undeploy(DeploymentUnit du) {
+
+ }
+
+}
diff --git a/integration/as7-eap-subsystem/src/main/java/org/keycloak/subsystem/extension/KeycloakExtension.java b/integration/as7-eap-subsystem/src/main/java/org/keycloak/subsystem/extension/KeycloakExtension.java
new file mode 100755
index 0000000000..e1f59afc84
--- /dev/null
+++ b/integration/as7-eap-subsystem/src/main/java/org/keycloak/subsystem/extension/KeycloakExtension.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * 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.extension;
+
+import org.jboss.as.controller.Extension;
+import org.jboss.as.controller.ExtensionContext;
+import org.jboss.as.controller.PathElement;
+import org.jboss.as.controller.SubsystemRegistration;
+import org.jboss.as.controller.descriptions.StandardResourceDescriptionResolver;
+import org.jboss.as.controller.parsing.ExtensionParsingContext;
+import org.jboss.as.controller.registry.ManagementResourceRegistration;
+import org.jboss.as.controller.ResourceDefinition;
+import org.keycloak.subsystem.logging.KeycloakLogger;
+
+import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SUBSYSTEM;
+
+
+/**
+ * Main Extension class for the subsystem.
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc.
+ */
+public class KeycloakExtension implements Extension {
+
+ public static final String SUBSYSTEM_NAME = "keycloak";
+ public static final String NAMESPACE = "urn:jboss:domain:keycloak:1.0";
+ private static final KeycloakSubsystemParser PARSER = new KeycloakSubsystemParser();
+ static final PathElement PATH_SUBSYSTEM = PathElement.pathElement(SUBSYSTEM, SUBSYSTEM_NAME);
+ private static final String RESOURCE_NAME = KeycloakExtension.class.getPackage().getName() + ".LocalDescriptions";
+ private static final int MANAGEMENT_API_MAJOR_VERSION = 1;
+ private static final int MANAGEMENT_API_MINOR_VERSION = 0;
+ private static final int MANAGEMENT_API_MICRO_VERSION = 0;
+ protected static final PathElement SUBSYSTEM_PATH = PathElement.pathElement(SUBSYSTEM, SUBSYSTEM_NAME);
+ 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 CredentialDefinition CREDENTIAL_DEFINITION = new CredentialDefinition();
+
+ static StandardResourceDescriptionResolver getResourceDescriptionResolver(final String... keyPrefix) {
+ StringBuilder prefix = new StringBuilder(SUBSYSTEM_NAME);
+ for (String kp : keyPrefix) {
+ prefix.append('.').append(kp);
+ }
+ return new StandardResourceDescriptionResolver(prefix.toString(), RESOURCE_NAME, KeycloakExtension.class.getClassLoader(), true, false);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void initializeParsers(final ExtensionParsingContext context) {
+ context.setSubsystemXmlMapping(SUBSYSTEM_NAME, KeycloakExtension.NAMESPACE, PARSER);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void initialize(final ExtensionContext context) {
+ KeycloakLogger.ROOT_LOGGER.debug("Activating Keycloak Extension");
+ final SubsystemRegistration subsystem = context.registerSubsystem(SUBSYSTEM_NAME, MANAGEMENT_API_MAJOR_VERSION,
+ MANAGEMENT_API_MINOR_VERSION);
+
+ ManagementResourceRegistration registration = subsystem.registerSubsystemModel(KEYCLOAK_SUBSYSTEM_RESOURCE);
+ ManagementResourceRegistration realmRegistration = registration.registerSubModel(REALM_DEFINITION);
+ ManagementResourceRegistration secureDeploymentRegistration = realmRegistration.registerSubModel(SECURE_DEPLOYMENT_DEFINITION);
+ secureDeploymentRegistration.registerSubModel(CREDENTIAL_DEFINITION);
+
+ subsystem.registerXMLElementWriter(PARSER);
+ }
+}
diff --git a/integration/as7-eap-subsystem/src/main/java/org/keycloak/subsystem/extension/KeycloakSubsystemAdd.java b/integration/as7-eap-subsystem/src/main/java/org/keycloak/subsystem/extension/KeycloakSubsystemAdd.java
new file mode 100755
index 0000000000..7bb610b5f1
--- /dev/null
+++ b/integration/as7-eap-subsystem/src/main/java/org/keycloak/subsystem/extension/KeycloakSubsystemAdd.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * 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.extension;
+
+import java.util.List;
+
+import org.jboss.as.controller.AbstractBoottimeAddStepHandler;
+import org.jboss.as.controller.OperationContext;
+import org.jboss.as.controller.OperationFailedException;
+import org.jboss.as.controller.ServiceVerificationHandler;
+import org.jboss.as.server.AbstractDeploymentChainStep;
+import org.jboss.as.server.DeploymentProcessorTarget;
+import org.jboss.as.server.deployment.Phase;
+import org.jboss.dmr.ModelNode;
+import org.jboss.msc.service.ServiceController;
+
+/**
+ * The Keycloak subsystem add update handler.
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc.
+ */
+class KeycloakSubsystemAdd extends AbstractBoottimeAddStepHandler {
+
+ static final KeycloakSubsystemAdd INSTANCE = new KeycloakSubsystemAdd();
+
+ @Override
+ protected void populateModel(ModelNode operation, ModelNode model) throws OperationFailedException {
+ model.setEmptyObject();
+ }
+
+ @Override
+ protected void performBoottime(final OperationContext context, ModelNode operation, final ModelNode model, ServiceVerificationHandler verificationHandler, List> newControllers) {
+ context.addStep(new AbstractDeploymentChainStep() {
+ @Override
+ protected void execute(DeploymentProcessorTarget processorTarget) {
+ processorTarget.addDeploymentProcessor(Phase.DEPENDENCIES, 0, new KeycloakDependencyProcessor());
+
+
+ processorTarget.addDeploymentProcessor(KeycloakAdapterConfigDeploymentProcessor.PHASE,
+ KeycloakAdapterConfigDeploymentProcessor.PRIORITY,
+ new KeycloakAdapterConfigDeploymentProcessor());
+ }
+ }, OperationContext.Stage.RUNTIME);
+ }
+
+ @Override
+ protected void performRuntime(OperationContext context, ModelNode operation, ModelNode model, ServiceVerificationHandler verificationHandler, List> newControllers) throws OperationFailedException {
+ super.performRuntime(context, operation, model, verificationHandler, newControllers);
+
+ ServiceController controller = context.getServiceTarget()
+ .addService(KeycloakAdapterConfigService.SERVICE_NAME, KeycloakAdapterConfigService.INSTANCE)
+ .addListener(verificationHandler)
+ .setInitialMode(ServiceController.Mode.ACTIVE)
+ .install();
+ newControllers.add(controller);
+ }
+
+ @Override
+ protected boolean requiresRuntimeVerification() {
+ return false;
+ }
+}
diff --git a/integration/as7-eap-subsystem/src/main/java/org/keycloak/subsystem/extension/KeycloakSubsystemDefinition.java b/integration/as7-eap-subsystem/src/main/java/org/keycloak/subsystem/extension/KeycloakSubsystemDefinition.java
new file mode 100755
index 0000000000..d3b3f69af7
--- /dev/null
+++ b/integration/as7-eap-subsystem/src/main/java/org/keycloak/subsystem/extension/KeycloakSubsystemDefinition.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2014 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * 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.extension;
+
+import org.jboss.as.controller.PathElement;
+import org.jboss.as.controller.ReloadRequiredRemoveStepHandler;
+import org.jboss.as.controller.SimpleResourceDefinition;
+import org.jboss.as.controller.descriptions.ModelDescriptionConstants;
+import org.jboss.as.controller.operations.common.GenericSubsystemDescribeHandler;
+import org.jboss.as.controller.registry.ManagementResourceRegistration;
+import org.jboss.as.controller.registry.OperationEntry;
+
+import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.DESCRIBE;
+
+/**
+ * Definition of subsystem=keycloak.
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc.
+ */
+public class KeycloakSubsystemDefinition extends SimpleResourceDefinition {
+ protected KeycloakSubsystemDefinition() {
+ super(PathElement.pathElement(ModelDescriptionConstants.SUBSYSTEM, KeycloakExtension.SUBSYSTEM_NAME),
+ KeycloakExtension.getResourceDescriptionResolver("subsystem"),
+ KeycloakSubsystemAdd.INSTANCE,
+ ReloadRequiredRemoveStepHandler.INSTANCE
+ );
+ }
+
+ @Override
+ public void registerOperations(ManagementResourceRegistration resourceRegistration) {
+ super.registerOperations(resourceRegistration);
+ resourceRegistration.registerOperationHandler(DESCRIBE, GenericSubsystemDescribeHandler.INSTANCE, GenericSubsystemDescribeHandler.INSTANCE, false, OperationEntry.EntryType.PRIVATE);
+ //resourceRegistration.registerOperationHandler(GenericSubsystemDescribeHandler.DEFINITION, GenericSubsystemDescribeHandler.INSTANCE);
+ }
+
+}
diff --git a/integration/as7-eap-subsystem/src/main/java/org/keycloak/subsystem/extension/KeycloakSubsystemParser.java b/integration/as7-eap-subsystem/src/main/java/org/keycloak/subsystem/extension/KeycloakSubsystemParser.java
new file mode 100755
index 0000000000..0cb196bc6e
--- /dev/null
+++ b/integration/as7-eap-subsystem/src/main/java/org/keycloak/subsystem/extension/KeycloakSubsystemParser.java
@@ -0,0 +1,226 @@
+/*
+ * Copyright 2014 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * 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.extension;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import javax.xml.stream.XMLStreamConstants;
+import javax.xml.stream.XMLStreamException;
+import org.jboss.as.controller.AttributeDefinition;
+import org.jboss.as.controller.PathAddress;
+import org.jboss.as.controller.PathElement;
+import org.jboss.as.controller.SimpleAttributeDefinition;
+import org.jboss.as.controller.descriptions.ModelDescriptionConstants;
+import org.jboss.as.controller.parsing.ParseUtils;
+import org.jboss.as.controller.persistence.SubsystemMarshallingContext;
+import org.jboss.dmr.ModelNode;
+import org.jboss.dmr.Property;
+import org.jboss.staxmapper.XMLElementReader;
+import org.jboss.staxmapper.XMLElementWriter;
+import org.jboss.staxmapper.XMLExtendedStreamReader;
+import org.jboss.staxmapper.XMLExtendedStreamWriter;
+
+/**
+ * The subsystem parser, which uses stax to read and write to and from xml
+ */
+class KeycloakSubsystemParser implements XMLStreamConstants, XMLElementReader>, XMLElementWriter {
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void readElement(final XMLExtendedStreamReader reader, final List list) throws XMLStreamException {
+ // Require no attributes
+ ParseUtils.requireNoAttributes(reader);
+ ModelNode addKeycloakSub = Util.createAddOperation(PathAddress.pathAddress(KeycloakExtension.PATH_SUBSYSTEM));
+ list.add(addKeycloakSub);
+
+ while (reader.hasNext() && nextTag(reader) != END_ELEMENT) {
+ if (!reader.getLocalName().equals("realm")) {
+ throw ParseUtils.unexpectedElement(reader);
+ }
+ readRealm(reader, list);
+ }
+ }
+
+ // used for debugging
+ private int nextTag(XMLExtendedStreamReader reader) throws XMLStreamException {
+ return reader.nextTag();
+ }
+
+ private void readRealm(XMLExtendedStreamReader reader, List list) throws XMLStreamException {
+ String realmName = readNameAttribute(reader);
+ ModelNode composite = new ModelNode();
+ composite.get(ModelDescriptionConstants.OP_ADDR).setEmptyList();
+ composite.get(ModelDescriptionConstants.OP).set(ModelDescriptionConstants.COMPOSITE);
+ ModelNode addRealm = new ModelNode();
+ addRealm.get(ModelDescriptionConstants.OP).set(ModelDescriptionConstants.ADD);
+ PathAddress addr = PathAddress.pathAddress(PathElement.pathElement(ModelDescriptionConstants.SUBSYSTEM, KeycloakExtension.SUBSYSTEM_NAME),
+ PathElement.pathElement(RealmDefinition.TAG_NAME, realmName));
+ addRealm.get(ModelDescriptionConstants.OP_ADDR).set(addr.toModelNode());
+
+ List resourcesToAdd = new ArrayList();
+ while (reader.hasNext() && nextTag(reader) != END_ELEMENT) {
+ String tagName = reader.getLocalName();
+ if (tagName.equals(SecureDeploymentDefinition.TAG_NAME)) {
+ readDeployment(reader, addr, resourcesToAdd);
+ continue;
+ }
+
+ SimpleAttributeDefinition def = RealmDefinition.lookup(tagName);
+ if (def == null) throw new XMLStreamException("Unknown realm tag " + tagName);
+ def.parseAndSetParameter(reader.getElementText(), addRealm, reader);
+ }
+
+ if (!RealmDefinition.validateTruststoreSetIfRequired(addRealm)) {
+ //TODO: externalize the message
+ throw new XMLStreamException("truststore and truststore-password must be set if both ssl-not-required and disable-trust-maanger are false.");
+ }
+
+ ModelNode steps = new ModelNode();
+ steps.add(addRealm);
+ for (ModelNode resource : resourcesToAdd) {
+ steps.add(resource);
+ }
+ composite.get(ModelDescriptionConstants.STEPS).set(steps);
+
+ list.add(composite);
+ }
+
+ private void readDeployment(XMLExtendedStreamReader reader, PathAddress parent, List resourcesToAdd) throws XMLStreamException {
+ String name = readNameAttribute(reader);
+ ModelNode addSecureDeployment = new ModelNode();
+ addSecureDeployment.get(ModelDescriptionConstants.OP).set(ModelDescriptionConstants.ADD);
+ PathAddress addr = PathAddress.pathAddress(parent, PathElement.pathElement(SecureDeploymentDefinition.TAG_NAME, name));
+ addSecureDeployment.get(ModelDescriptionConstants.OP_ADDR).set(addr.toModelNode());
+ List credentialsToAdd = new ArrayList();
+ while (reader.hasNext() && nextTag(reader) != END_ELEMENT) {
+ String tagName = reader.getLocalName();
+ if (tagName.equals(CredentialDefinition.TAG_NAME)) {
+ readCredential(reader, addr, credentialsToAdd);
+ continue;
+ }
+
+ SimpleAttributeDefinition def = SecureDeploymentDefinition.lookup(tagName);
+ if (def == null) throw new XMLStreamException("Unknown secure-deployment tag " + tagName);
+ def.parseAndSetParameter(reader.getElementText(), addSecureDeployment, reader);
+ }
+ // Must add credentials after the deployment is added.
+ resourcesToAdd.add(addSecureDeployment);
+ resourcesToAdd.addAll(credentialsToAdd);
+ }
+
+ public void readCredential(XMLExtendedStreamReader reader, PathAddress parent, List credentialsToAdd) throws XMLStreamException {
+ String name = readNameAttribute(reader);
+ ModelNode addCredential = new ModelNode();
+ addCredential.get(ModelDescriptionConstants.OP).set(ModelDescriptionConstants.ADD);
+ PathAddress addr = PathAddress.pathAddress(parent, PathElement.pathElement(CredentialDefinition.TAG_NAME, name));
+ addCredential.get(ModelDescriptionConstants.OP_ADDR).set(addr.toModelNode());
+ addCredential.get(CredentialDefinition.VALUE.getName()).set(reader.getElementText());
+ credentialsToAdd.add(addCredential);
+ }
+
+ // expects that the current tag will have one single attribute called "name"
+ private String readNameAttribute(XMLExtendedStreamReader reader) throws XMLStreamException {
+ String name = null;
+ for (int i = 0; i < reader.getAttributeCount(); i++) {
+ String attr = reader.getAttributeLocalName(i);
+ if (attr.equals("name")) {
+ name = reader.getAttributeValue(i);
+ continue;
+ }
+ throw ParseUtils.unexpectedAttribute(reader, i);
+ }
+ if (name == null) {
+ throw ParseUtils.missingRequired(reader, Collections.singleton("name"));
+ }
+ return name;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void writeContent(final XMLExtendedStreamWriter writer, final SubsystemMarshallingContext context) throws XMLStreamException {
+ context.startSubsystemElement(KeycloakExtension.NAMESPACE, false);
+ writeRealms(writer, context);
+ writer.writeEndElement();
+ }
+
+ private void writeRealms(XMLExtendedStreamWriter writer, SubsystemMarshallingContext context) throws XMLStreamException {
+ if (!context.getModelNode().get(RealmDefinition.TAG_NAME).isDefined()) {
+ return;
+ }
+ for (Property realm : context.getModelNode().get(RealmDefinition.TAG_NAME).asPropertyList()) {
+ writer.writeStartElement(RealmDefinition.TAG_NAME);
+ writer.writeAttribute("name", realm.getName());
+ ModelNode realmElements = realm.getValue();
+ for (AttributeDefinition element : RealmDefinition.ALL_ATTRIBUTES) {
+ element.marshallAsElement(realmElements, writer);
+ }
+
+ ModelNode deployments = realmElements.get(SecureDeploymentDefinition.TAG_NAME);
+ if (deployments.isDefined()) {
+ writeSecureDeployments(writer, deployments);
+ }
+
+ writer.writeEndElement();
+ }
+ }
+
+ private void writeSecureDeployments(XMLExtendedStreamWriter writer, ModelNode deployments) throws XMLStreamException {
+ for (Property deployment : deployments.asPropertyList()) {
+ writer.writeStartElement(SecureDeploymentDefinition.TAG_NAME);
+ writer.writeAttribute("name", deployment.getName());
+ ModelNode deploymentElements = deployment.getValue();
+ for (AttributeDefinition element : SecureDeploymentDefinition.ALL_ATTRIBUTES) {
+ element.marshallAsElement(deploymentElements, writer);
+ }
+
+ ModelNode credentials = deploymentElements.get(CredentialDefinition.TAG_NAME);
+ if (credentials.isDefined()) {
+ writeCredentials(writer, credentials);
+ }
+
+ writer.writeEndElement();
+ }
+ }
+
+ private void writeCredentials(XMLExtendedStreamWriter writer, ModelNode credentials) throws XMLStreamException {
+ for (Property credential : credentials.asPropertyList()) {
+ writer.writeStartElement(CredentialDefinition.TAG_NAME);
+ writer.writeAttribute("name", credential.getName());
+ String credentialValue = credential.getValue().get(CredentialDefinition.VALUE.getName()).asString();
+ writeCharacters(writer, credentialValue);
+ writer.writeEndElement();
+ }
+ }
+
+ // code taken from org.jboss.as.controller.AttributeMarshaller
+ private void writeCharacters(XMLExtendedStreamWriter writer, String content) throws XMLStreamException {
+ if (content.indexOf('\n') > -1) {
+ // Multiline content. Use the overloaded variant that staxmapper will format
+ writer.writeCharacters(content);
+ } else {
+ // Staxmapper will just output the chars without adding newlines if this is used
+ char[] chars = content.toCharArray();
+ writer.writeCharacters(chars, 0, chars.length);
+ }
+ }
+
+}
diff --git a/integration/as7-eap-subsystem/src/main/java/org/keycloak/subsystem/extension/RealmAddHandler.java b/integration/as7-eap-subsystem/src/main/java/org/keycloak/subsystem/extension/RealmAddHandler.java
new file mode 100755
index 0000000000..ea080aed3a
--- /dev/null
+++ b/integration/as7-eap-subsystem/src/main/java/org/keycloak/subsystem/extension/RealmAddHandler.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * 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.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.ServiceVerificationHandler;
+import org.jboss.dmr.ModelNode;
+
+import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ADD;
+import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP;
+import org.jboss.msc.service.ServiceController;
+
+/**
+ * Add a new realm.
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc.
+ */
+public final class RealmAddHandler extends AbstractAddStepHandler {
+
+ public static RealmAddHandler INSTANCE = new RealmAddHandler();
+
+ private RealmAddHandler() {}
+
+ @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 realm. operation=" + operation.toString());
+ }
+
+ for (AttributeDefinition attrib : RealmDefinition.ALL_ATTRIBUTES) {
+ attrib.validateAndSet(operation, model);
+ }
+
+ if (!RealmDefinition.validateTruststoreSetIfRequired(model.clone())) {
+ //TODO: externalize message
+ throw new OperationFailedException("truststore and truststore-password must be set if both ssl-not-required and disable-trust-maanger are false.");
+ }
+ }
+
+ @Override
+ protected void performRuntime(OperationContext context, ModelNode operation, ModelNode model, ServiceVerificationHandler verificationHandler, List> newControllers) throws OperationFailedException {
+ KeycloakAdapterConfigService ckService = KeycloakAdapterConfigService.find(context);
+ ckService.addRealm(operation, model);
+ }
+}
diff --git a/integration/as7-eap-subsystem/src/main/java/org/keycloak/subsystem/extension/RealmDefinition.java b/integration/as7-eap-subsystem/src/main/java/org/keycloak/subsystem/extension/RealmDefinition.java
new file mode 100755
index 0000000000..295951d443
--- /dev/null
+++ b/integration/as7-eap-subsystem/src/main/java/org/keycloak/subsystem/extension/RealmDefinition.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * 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.extension;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+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.as.controller.registry.OperationEntry;
+import org.jboss.dmr.ModelNode;
+import org.jboss.dmr.ModelType;
+
+import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.DESCRIBE;
+
+/**
+ * Defines attributes and operations for the Realm
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc.
+ */
+public class RealmDefinition extends SimpleResourceDefinition {
+
+ public static final String TAG_NAME = "realm";
+
+ protected static final SimpleAttributeDefinition REALM_PUBLIC_KEY =
+ new SimpleAttributeDefinitionBuilder("realm-public-key", ModelType.STRING, false)
+ .setXmlName("realm-public-key")
+ .setAllowExpression(true)
+ .setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, false, true))
+ .build();
+ protected static final SimpleAttributeDefinition AUTH_SERVER_URL =
+ new SimpleAttributeDefinitionBuilder("auth-server-url", ModelType.STRING, false)
+ .setXmlName("auth-server-url")
+ .setAllowExpression(true)
+ .setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, false, true))
+ .build();
+ protected static final SimpleAttributeDefinition SSL_NOT_REQUIRED =
+ new SimpleAttributeDefinitionBuilder("ssl-not-required", ModelType.BOOLEAN, true)
+ .setXmlName("ssl-not-required")
+ .setAllowExpression(true)
+ .setDefaultValue(new ModelNode(false))
+ .build();
+ protected static final SimpleAttributeDefinition ALLOW_ANY_HOSTNAME =
+ new SimpleAttributeDefinitionBuilder("allow-any-hostname", ModelType.BOOLEAN, true)
+ .setXmlName("allow-any-hostname")
+ .setAllowExpression(true)
+ .setDefaultValue(new ModelNode(false))
+ .build();
+ protected static final SimpleAttributeDefinition DISABLE_TRUST_MANAGER =
+ new SimpleAttributeDefinitionBuilder("disable-trust-manager", ModelType.BOOLEAN, true)
+ .setXmlName("disable-trust-manager")
+ .setAllowExpression(true)
+ .setDefaultValue(new ModelNode(false))
+ .build();
+ protected static final SimpleAttributeDefinition TRUSTSTORE =
+ new SimpleAttributeDefinitionBuilder("truststore", ModelType.STRING, true)
+ .setXmlName("truststore")
+ .setAllowExpression(true)
+ .setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, true, true))
+ .build();
+ protected static final SimpleAttributeDefinition TRUSTSTORE_PASSWORD =
+ new SimpleAttributeDefinitionBuilder("truststore-password", ModelType.STRING, true)
+ .setXmlName("truststore-password")
+ .setAllowExpression(true)
+ .setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, true, true))
+ .build();
+ protected static final SimpleAttributeDefinition CONNECTION_POOL_SIZE =
+ new SimpleAttributeDefinitionBuilder("connection-pool-size", ModelType.INT, true)
+ .setXmlName("connection-pool-size")
+ .setAllowExpression(true)
+ .setValidator(new IntRangeValidator(0, true))
+ .build();
+
+ protected static final List REALM_ONLY_ATTRIBUTES = new ArrayList();
+ static {
+ REALM_ONLY_ATTRIBUTES.add(REALM_PUBLIC_KEY);
+ REALM_ONLY_ATTRIBUTES.add(AUTH_SERVER_URL);
+ REALM_ONLY_ATTRIBUTES.add(TRUSTSTORE);
+ REALM_ONLY_ATTRIBUTES.add(TRUSTSTORE_PASSWORD);
+ REALM_ONLY_ATTRIBUTES.add(SSL_NOT_REQUIRED);
+ REALM_ONLY_ATTRIBUTES.add(ALLOW_ANY_HOSTNAME);
+ REALM_ONLY_ATTRIBUTES.add(DISABLE_TRUST_MANAGER);
+ REALM_ONLY_ATTRIBUTES.add(CONNECTION_POOL_SIZE);
+ }
+
+ protected static final List ALL_ATTRIBUTES = new ArrayList();
+ static {
+ ALL_ATTRIBUTES.addAll(REALM_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 final RealmWriteAttributeHandler realmAttrHandler = new RealmWriteAttributeHandler(ALL_ATTRIBUTES.toArray(new SimpleAttributeDefinition[0]));
+
+ public RealmDefinition() {
+ super(PathElement.pathElement("realm"),
+ KeycloakExtension.getResourceDescriptionResolver("realm"),
+ RealmAddHandler.INSTANCE,
+ RealmRemoveHandler.INSTANCE);
+ }
+
+ @Override
+ public void registerOperations(ManagementResourceRegistration resourceRegistration) {
+ super.registerOperations(resourceRegistration);
+ resourceRegistration.registerOperationHandler(DESCRIBE, GenericSubsystemDescribeHandler.INSTANCE, GenericSubsystemDescribeHandler.INSTANCE, false, OperationEntry.EntryType.PRIVATE);
+ //resourceRegistration.registerOperationHandler(GenericSubsystemDescribeHandler.DEFINITION, GenericSubsystemDescribeHandler.INSTANCE);
+ }
+
+ @Override
+ public void registerAttributes(ManagementResourceRegistration resourceRegistration) {
+ super.registerAttributes(resourceRegistration);
+
+ for (AttributeDefinition attrDef : ALL_ATTRIBUTES) {
+ //TODO: use subclass of realmAttrHandler that can call RealmDefinition.validateTruststoreSetIfRequired
+ resourceRegistration.registerReadWriteAttribute(attrDef, null, realmAttrHandler);
+ }
+ }
+
+ /**
+ * truststore and truststore-password must be set if ssl-not-required and disable-trust-manager are both false.
+ *
+ * @param attributes The full set of attributes.
+ *
+ * @return true if the attributes are valid, false otherwise.
+ */
+ public static boolean validateTruststoreSetIfRequired(ModelNode attributes) {
+ if (!isSet(attributes, SSL_NOT_REQUIRED) && !isSet(attributes, DISABLE_TRUST_MANAGER)) {
+ if (!(isSet(attributes, TRUSTSTORE) && isSet(attributes, TRUSTSTORE_PASSWORD))) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private static boolean isSet(ModelNode attributes, SimpleAttributeDefinition def) {
+ ModelNode attribute = attributes.get(def.getName());
+
+ if (def.getType() == ModelType.BOOLEAN) {
+ return attribute.isDefined() && attribute.asBoolean();
+ }
+
+ return attribute.isDefined() && !attribute.asString().isEmpty();
+ }
+
+ public static SimpleAttributeDefinition lookup(String name) {
+ return DEFINITION_LOOKUP.get(name);
+ }
+}
diff --git a/integration/as7-eap-subsystem/src/main/java/org/keycloak/subsystem/extension/RealmRemoveHandler.java b/integration/as7-eap-subsystem/src/main/java/org/keycloak/subsystem/extension/RealmRemoveHandler.java
new file mode 100755
index 0000000000..a5c5441554
--- /dev/null
+++ b/integration/as7-eap-subsystem/src/main/java/org/keycloak/subsystem/extension/RealmRemoveHandler.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * 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.extension;
+
+import org.jboss.as.controller.AbstractRemoveStepHandler;
+import org.jboss.as.controller.OperationContext;
+import org.jboss.as.controller.OperationFailedException;
+import org.jboss.dmr.ModelNode;
+
+/**
+ * Remove a realm.
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc.
+ */
+public final class RealmRemoveHandler extends AbstractRemoveStepHandler {
+
+ public static RealmRemoveHandler INSTANCE = new RealmRemoveHandler();
+
+ private RealmRemoveHandler() {}
+
+ @Override
+ protected void performRuntime(OperationContext context, ModelNode operation, ModelNode model) throws OperationFailedException {
+ KeycloakAdapterConfigService ckService = KeycloakAdapterConfigService.find(context);
+ ckService.removeRealm(operation);
+ }
+}
diff --git a/integration/as7-eap-subsystem/src/main/java/org/keycloak/subsystem/extension/RealmWriteAttributeHandler.java b/integration/as7-eap-subsystem/src/main/java/org/keycloak/subsystem/extension/RealmWriteAttributeHandler.java
new file mode 100755
index 0000000000..f6847b5d5d
--- /dev/null
+++ b/integration/as7-eap-subsystem/src/main/java/org/keycloak/subsystem/extension/RealmWriteAttributeHandler.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * 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.extension;
+
+import java.util.List;
+import org.jboss.as.controller.AbstractWriteAttributeHandler;
+import org.jboss.as.controller.AttributeDefinition;
+import org.jboss.as.controller.OperationContext;
+import org.jboss.as.controller.OperationFailedException;
+import org.jboss.dmr.ModelNode;
+
+/**
+ * Update an attribute on a realm.
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc.
+ */
+public class RealmWriteAttributeHandler extends AbstractWriteAttributeHandler {
+
+ public RealmWriteAttributeHandler(List definitions) {
+ this(definitions.toArray(new AttributeDefinition[definitions.size()]));
+ }
+
+ public RealmWriteAttributeHandler(AttributeDefinition... definitions) {
+ super(definitions);
+ }
+
+ @Override
+ protected boolean applyUpdateToRuntime(OperationContext context, ModelNode operation, String attributeName,
+ ModelNode resolvedValue, ModelNode currentValue, HandbackHolder hh) throws OperationFailedException {
+ KeycloakAdapterConfigService ckService = KeycloakAdapterConfigService.find(context);
+ ckService.updateRealm(operation, attributeName, resolvedValue);
+
+ hh.setHandback(ckService);
+
+ return false;
+ }
+
+ @Override
+ protected void revertUpdateToRuntime(OperationContext context, ModelNode operation, String attributeName,
+ ModelNode valueToRestore, ModelNode valueToRevert, KeycloakAdapterConfigService ckService) throws OperationFailedException {
+ ckService.updateRealm(operation, attributeName, valueToRestore);
+ }
+
+}
diff --git a/integration/as7-eap-subsystem/src/main/java/org/keycloak/subsystem/extension/SecureDeploymentAddHandler.java b/integration/as7-eap-subsystem/src/main/java/org/keycloak/subsystem/extension/SecureDeploymentAddHandler.java
new file mode 100755
index 0000000000..c5f795c4e1
--- /dev/null
+++ b/integration/as7-eap-subsystem/src/main/java/org/keycloak/subsystem/extension/SecureDeploymentAddHandler.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * 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.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.ServiceVerificationHandler;
+import org.jboss.dmr.ModelNode;
+
+import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ADD;
+import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP;
+import org.jboss.msc.service.ServiceController;
+
+/**
+ * Add a deployment to a realm.
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc.
+ */
+public final class SecureDeploymentAddHandler extends AbstractAddStepHandler {
+
+ public static SecureDeploymentAddHandler INSTANCE = new SecureDeploymentAddHandler();
+
+ 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);
+ }
+ }
+
+ @Override
+ protected void performRuntime(OperationContext context, ModelNode operation, ModelNode model, ServiceVerificationHandler verificationHandler, List> newControllers) throws OperationFailedException {
+ KeycloakAdapterConfigService ckService = KeycloakAdapterConfigService.find(context);
+ ckService.addSecureDeployment(operation, model);
+ }
+}
diff --git a/integration/as7-eap-subsystem/src/main/java/org/keycloak/subsystem/extension/SecureDeploymentDefinition.java b/integration/as7-eap-subsystem/src/main/java/org/keycloak/subsystem/extension/SecureDeploymentDefinition.java
new file mode 100755
index 0000000000..91fab8d429
--- /dev/null
+++ b/integration/as7-eap-subsystem/src/main/java/org/keycloak/subsystem/extension/SecureDeploymentDefinition.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * 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.extension;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+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.StringLengthValidator;
+import org.jboss.as.controller.registry.ManagementResourceRegistration;
+import org.jboss.as.controller.registry.OperationEntry;
+import org.jboss.dmr.ModelNode;
+import org.jboss.dmr.ModelType;
+
+import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.DESCRIBE;
+
+/**
+ * Defines attributes and operations for a secure-deployment.
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc.
+ */
+public class SecureDeploymentDefinition extends SimpleResourceDefinition {
+
+ public static final String TAG_NAME = "secure-deployment";
+
+ 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 List DEPLOYMENT_ONLY_ATTRIBUTES = new ArrayList();
+ static {
+ DEPLOYMENT_ONLY_ATTRIBUTES.add(RESOURCE);
+ DEPLOYMENT_ONLY_ATTRIBUTES.add(USE_RESOURCE_ROLE_MAPPINGS);
+ DEPLOYMENT_ONLY_ATTRIBUTES.add(BEARER_ONLY);
+ }
+
+ 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);
+
+ public SecureDeploymentDefinition() {
+ super(PathElement.pathElement(TAG_NAME),
+ KeycloakExtension.getResourceDescriptionResolver(TAG_NAME),
+ SecureDeploymentAddHandler.INSTANCE,
+ SecureDeploymentRemoveHandler.INSTANCE);
+ }
+
+ @Override
+ public void registerOperations(ManagementResourceRegistration resourceRegistration) {
+ super.registerOperations(resourceRegistration);
+ resourceRegistration.registerOperationHandler(DESCRIBE, GenericSubsystemDescribeHandler.INSTANCE, GenericSubsystemDescribeHandler.INSTANCE, false, OperationEntry.EntryType.PRIVATE);
+ //resourceRegistration.registerOperationHandler(GenericSubsystemDescribeHandler.DEFINITION, GenericSubsystemDescribeHandler.INSTANCE);
+ }
+
+ @Override
+ public void registerAttributes(ManagementResourceRegistration resourceRegistration) {
+ super.registerAttributes(resourceRegistration);
+ for (AttributeDefinition attrDef : ALL_ATTRIBUTES) {
+ resourceRegistration.registerReadWriteAttribute(attrDef, null, attrHandler);
+ }
+ }
+
+ public static SimpleAttributeDefinition lookup(String name) {
+ return DEFINITION_LOOKUP.get(name);
+ }
+}
diff --git a/integration/as7-eap-subsystem/src/main/java/org/keycloak/subsystem/extension/SecureDeploymentRemoveHandler.java b/integration/as7-eap-subsystem/src/main/java/org/keycloak/subsystem/extension/SecureDeploymentRemoveHandler.java
new file mode 100755
index 0000000000..da0ca7e19d
--- /dev/null
+++ b/integration/as7-eap-subsystem/src/main/java/org/keycloak/subsystem/extension/SecureDeploymentRemoveHandler.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * 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.extension;
+
+import org.jboss.as.controller.AbstractRemoveStepHandler;
+import org.jboss.as.controller.OperationContext;
+import org.jboss.as.controller.OperationFailedException;
+import org.jboss.dmr.ModelNode;
+
+/**
+ * Remove a secure-deployment from a realm.
+ *
+ * @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() {}
+
+ @Override
+ protected void performRuntime(OperationContext context, ModelNode operation, ModelNode model) throws OperationFailedException {
+ KeycloakAdapterConfigService ckService = KeycloakAdapterConfigService.find(context);
+ ckService.removeSecureDeployment(operation);
+ }
+}
diff --git a/integration/as7-eap-subsystem/src/main/java/org/keycloak/subsystem/extension/SecureDeploymentWriteAttributeHandler.java b/integration/as7-eap-subsystem/src/main/java/org/keycloak/subsystem/extension/SecureDeploymentWriteAttributeHandler.java
new file mode 100755
index 0000000000..8cc1f604d8
--- /dev/null
+++ b/integration/as7-eap-subsystem/src/main/java/org/keycloak/subsystem/extension/SecureDeploymentWriteAttributeHandler.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * 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.extension;
+
+import java.util.List;
+import org.jboss.as.controller.AbstractWriteAttributeHandler;
+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.dmr.ModelNode;
+
+/**
+ * Update an attribute on a secure-deployment.
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc.
+ */
+public class SecureDeploymentWriteAttributeHandler extends AbstractWriteAttributeHandler {
+
+ public SecureDeploymentWriteAttributeHandler(List definitions) {
+ this(definitions.toArray(new AttributeDefinition[definitions.size()]));
+ }
+
+ public SecureDeploymentWriteAttributeHandler(AttributeDefinition... definitions) {
+ super(definitions);
+ }
+
+ @Override
+ protected boolean applyUpdateToRuntime(OperationContext context, ModelNode operation, String attributeName,
+ ModelNode resolvedValue, ModelNode currentValue, HandbackHolder hh) throws OperationFailedException {
+ KeycloakAdapterConfigService ckService = KeycloakAdapterConfigService.find(context);
+ hh.setHandback(ckService);
+ ckService.updateSecureDeployment(operation, attributeName, resolvedValue);
+ return false;
+ }
+
+ @Override
+ protected void revertUpdateToRuntime(OperationContext context, ModelNode operation, String attributeName,
+ ModelNode valueToRestore, ModelNode valueToRevert, KeycloakAdapterConfigService ckService) throws OperationFailedException {
+ ckService.updateSecureDeployment(operation, attributeName, valueToRestore);
+ }
+
+}
diff --git a/integration/as7-eap-subsystem/src/main/java/org/keycloak/subsystem/extension/SharedAttributeDefinitons.java b/integration/as7-eap-subsystem/src/main/java/org/keycloak/subsystem/extension/SharedAttributeDefinitons.java
new file mode 100755
index 0000000000..e95bb5da4f
--- /dev/null
+++ b/integration/as7-eap-subsystem/src/main/java/org/keycloak/subsystem/extension/SharedAttributeDefinitons.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * 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.extension;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.jboss.as.controller.SimpleAttributeDefinition;
+import org.jboss.as.controller.SimpleAttributeDefinitionBuilder;
+import org.jboss.as.controller.operations.validation.IntRangeValidator;
+import org.jboss.as.controller.operations.validation.StringLengthValidator;
+import org.jboss.dmr.ModelNode;
+import org.jboss.dmr.ModelType;
+
+/**
+ * Defines attributes that can be present in both a realm and an application (secure-deployment).
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc.
+ */
+public class SharedAttributeDefinitons {
+
+ protected static final SimpleAttributeDefinition ENABLE_CORS =
+ new SimpleAttributeDefinitionBuilder("enable-cors", ModelType.BOOLEAN, true)
+ .setXmlName("enable-cors")
+ .setAllowExpression(true)
+ .setDefaultValue(new ModelNode(false))
+ .build();
+ protected static final SimpleAttributeDefinition CLIENT_KEYSTORE =
+ new SimpleAttributeDefinitionBuilder("client-keystore", ModelType.STRING, true)
+ .setXmlName("client-keystore")
+ .setAllowExpression(true)
+ .setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, true, true))
+ .build();
+ protected static final SimpleAttributeDefinition CLIENT_KEYSTORE_PASSWORD =
+ new SimpleAttributeDefinitionBuilder("client-keystore-password", ModelType.STRING, true)
+ .setXmlName("client-keystore-password")
+ .setAllowExpression(true)
+ .setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, true, true))
+ .build();
+ protected static final SimpleAttributeDefinition CLIENT_KEY_PASSWORD =
+ new SimpleAttributeDefinitionBuilder("client-key-password", ModelType.STRING, true)
+ .setXmlName("client-key-password")
+ .setAllowExpression(true)
+ .setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, true, true))
+ .build();
+ protected static final SimpleAttributeDefinition CORS_MAX_AGE =
+ new SimpleAttributeDefinitionBuilder("cors-max-age", ModelType.INT, true)
+ .setXmlName("cors-max-age")
+ .setAllowExpression(true)
+ .setValidator(new IntRangeValidator(-1, true))
+ .build();
+ protected static final SimpleAttributeDefinition CORS_ALLOWED_HEADERS =
+ new SimpleAttributeDefinitionBuilder("cors-allowed-headers", ModelType.STRING, true)
+ .setXmlName("cors-allowed-headers")
+ .setAllowExpression(true)
+ .setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, true, true))
+ .build();
+ protected static final SimpleAttributeDefinition CORS_ALLOWED_METHODS =
+ new SimpleAttributeDefinitionBuilder("cors-allowed-methods", ModelType.STRING, true)
+ .setXmlName("cors-allowed-methods")
+ .setAllowExpression(true)
+ .setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, true, true))
+ .build();
+ protected static final SimpleAttributeDefinition EXPOSE_TOKEN =
+ new SimpleAttributeDefinitionBuilder("expose-token", ModelType.BOOLEAN, true)
+ .setXmlName("expose-token")
+ .setAllowExpression(true)
+ .setDefaultValue(new ModelNode(false))
+ .build();
+
+
+ protected static final List ATTRIBUTES = new ArrayList();
+ static {
+ ATTRIBUTES.add(ENABLE_CORS);
+ ATTRIBUTES.add(CLIENT_KEYSTORE);
+ ATTRIBUTES.add(CLIENT_KEYSTORE_PASSWORD);
+ ATTRIBUTES.add(CLIENT_KEY_PASSWORD);
+ ATTRIBUTES.add(CORS_MAX_AGE);
+ ATTRIBUTES.add(CORS_ALLOWED_HEADERS);
+ ATTRIBUTES.add(CORS_ALLOWED_METHODS);
+ ATTRIBUTES.add(EXPOSE_TOKEN);
+ }
+
+}
diff --git a/integration/as7-eap-subsystem/src/main/java/org/keycloak/subsystem/extension/Util.java b/integration/as7-eap-subsystem/src/main/java/org/keycloak/subsystem/extension/Util.java
new file mode 100755
index 0000000000..9d376e274d
--- /dev/null
+++ b/integration/as7-eap-subsystem/src/main/java/org/keycloak/subsystem/extension/Util.java
@@ -0,0 +1,42 @@
+package org.keycloak.subsystem.extension;
+
+import org.jboss.as.controller.PathAddress;
+import org.jboss.as.controller.descriptions.ModelDescriptionConstants;
+import org.jboss.dmr.ModelNode;
+
+import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP;
+import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP_ADDR;
+
+/**
+ * @author Bill Burke
+ * @version $Revision: 1 $
+ */
+public class Util {
+ public static ModelNode createAddOperation(final PathAddress address) {
+ return createOperation(ModelDescriptionConstants.ADD, address);
+ }
+
+ public static ModelNode createAddOperation() {
+ return createEmptyOperation(ModelDescriptionConstants.ADD, null);
+ }
+
+ public static ModelNode createRemoveOperation(final PathAddress address) {
+ return createOperation(ModelDescriptionConstants.REMOVE, address);
+ }
+
+ public static ModelNode createOperation(final String operationName, final PathAddress address) {
+ return createEmptyOperation(operationName, address);
+ }
+
+ public static ModelNode createEmptyOperation(String operationName, final PathAddress address) {
+ ModelNode op = new ModelNode();
+ op.get(OP).set(operationName);
+ if (address != null) {
+ op.get(OP_ADDR).set(address.toModelNode());
+ } else {
+ // Just establish the standard structure; caller can fill in address later
+ op.get(OP_ADDR);
+ }
+ return op;
+ }
+}
diff --git a/integration/as7-eap-subsystem/src/main/java/org/keycloak/subsystem/logging/KeycloakLogger.java b/integration/as7-eap-subsystem/src/main/java/org/keycloak/subsystem/logging/KeycloakLogger.java
new file mode 100755
index 0000000000..fb45f69913
--- /dev/null
+++ b/integration/as7-eap-subsystem/src/main/java/org/keycloak/subsystem/logging/KeycloakLogger.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * 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.logging;
+
+import org.jboss.logging.BasicLogger;
+import org.jboss.logging.Logger;
+import org.jboss.logging.MessageLogger;
+
+/**
+ * This interface to be fleshed out later when error messages are fully externalized.
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc.
+ */
+@MessageLogger(projectCode = "KEYCLOAK")
+public interface KeycloakLogger extends BasicLogger {
+
+ /**
+ * A logger with a category of the package name.
+ */
+ KeycloakLogger ROOT_LOGGER = Logger.getMessageLogger(KeycloakLogger.class, "org.jboss.keycloak");
+
+}
diff --git a/integration/as7-eap-subsystem/src/main/java/org/keycloak/subsystem/logging/KeycloakMessages.java b/integration/as7-eap-subsystem/src/main/java/org/keycloak/subsystem/logging/KeycloakMessages.java
new file mode 100755
index 0000000000..06a6678b26
--- /dev/null
+++ b/integration/as7-eap-subsystem/src/main/java/org/keycloak/subsystem/logging/KeycloakMessages.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * 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.logging;
+
+import org.jboss.logging.MessageBundle;
+import org.jboss.logging.Messages;
+
+/**
+ * This interface to be fleshed out later when error messages are fully externalized.
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2012 Red Hat Inc.
+ */
+@MessageBundle(projectCode = "TLIP")
+public interface KeycloakMessages {
+
+ /**
+ * The messages
+ */
+ KeycloakMessages MESSAGES = Messages.getBundle(KeycloakMessages.class);
+}
diff --git a/integration/as7-eap-subsystem/src/main/resources/META-INF/services/org.jboss.as.controller.Extension b/integration/as7-eap-subsystem/src/main/resources/META-INF/services/org.jboss.as.controller.Extension
new file mode 100755
index 0000000000..6a7d631da0
--- /dev/null
+++ b/integration/as7-eap-subsystem/src/main/resources/META-INF/services/org.jboss.as.controller.Extension
@@ -0,0 +1 @@
+org.keycloak.subsystem.extension.KeycloakExtension
diff --git a/integration/as7-eap-subsystem/src/main/resources/org/keycloak/subsystem/extension/LocalDescriptions.properties b/integration/as7-eap-subsystem/src/main/resources/org/keycloak/subsystem/extension/LocalDescriptions.properties
new file mode 100755
index 0000000000..5234a327e3
--- /dev/null
+++ b/integration/as7-eap-subsystem/src/main/resources/org/keycloak/subsystem/extension/LocalDescriptions.properties
@@ -0,0 +1,49 @@
+keycloak.subsystem=Keycloak subsystem
+keycloak.subsystem.add=Operation Adds Keycloak subsystem
+keycloak.subsystem.remove=Operation removes Keycloak subsystem
+keycloak.subsystem.realm=A Keycloak realm.
+
+keycloak.realm=A Keycloak realm.
+keycloak.realm.add=Add a realm definition to the subsystem.
+keycloak.realm.remove=Remove a realm from the subsystem.
+keycloak.realm.realm-public-key=TODO: fill in help text
+keycloak.realm.auth-server-url=TODO: fill in help text
+keycloak.realm.disable-trust-manager=TODO: fill in help text
+keycloak.realm.ssl-not-required=TODO: fill in help text
+keycloak.realm.allow-any-hostname=TODO: fill in help text
+keycloak.realm.truststore=TODO: fill in help text
+keycloak.realm.truststore-password=TODO: fill in help text
+keycloak.realm.connection-pool-size=TODO: fill in help text
+keycloak.realm.enable-cors=TODO: fill in help text
+keycloak.realm.client-keystore=TODO: fill in help text
+keycloak.realm.client-keystore-password=TODO: fill in help text
+keycloak.realm.client-key-password=TODO: fill in help text
+keycloak.realm.cors-max-age=TODO: fill in help text
+keycloak.realm.cors-allowed-headers=TODO: fill in help text
+keycloak.realm.cors-allowed-methods=TODO: fill in help text
+keycloak.realm.expose-token=TODO: fill in help text
+
+keycloak.realm.secure-deployment=A deployment secured by Keycloak
+
+keycloak.secure-deployment=A deployment secured by Keycloak
+keycloak.secure-deployment.add=Add a deployment to be secured by Keycloak
+keycloak.secure-deployment.remove=Remove a deployment to be secured by Keycloak
+keycloak.secure-deployment.resource=TODO: fill in help text
+keycloak.secure-deployment.use-resource-role-mappings=TODO: fill in help text
+keycloak.secure-deployment.credentials=TODO: fill in help text
+keycloak.secure-deployment.bearer-only=TODO: fill in help text
+keycloak.secure-deployment.enable-cors=TODO: fill in help text
+keycloak.secure-deployment.client-keystore=TODO: fill in help text
+keycloak.secure-deployment.client-keystore-password=TODO: fill in help text
+keycloak.secure-deployment.client-key-password=TODO: fill in help text
+keycloak.secure-deployment.cors-max-age=TODO: fill in help text
+keycloak.secure-deployment.cors-allowed-headers=TODO: fill in help text
+keycloak.secure-deployment.cors-allowed-methods=TODO: fill in help text
+keycloak.secure-deployment.expose-token=TODO: fill in help text
+
+keycloak.secure-deployment.credential=TODO: fill in help text
+
+keycloak.credential=TODO: fill in help text
+keycloak.credential.value=TODO: fill in help text
+keycloak.credential.add=TODO: fill in help text
+keycloak.credential.remove=TODO: fill in help text
\ No newline at end of file
diff --git a/integration/as7-eap-subsystem/src/main/resources/schema/keycloak_1_0.xsd b/integration/as7-eap-subsystem/src/main/resources/schema/keycloak_1_0.xsd
new file mode 100755
index 0000000000..9cdbbba980
--- /dev/null
+++ b/integration/as7-eap-subsystem/src/main/resources/schema/keycloak_1_0.xsd
@@ -0,0 +1,89 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ The name of the realm.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ The name of the deployment.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ The name of the credential.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/integration/as7-eap-subsystem/src/test/java/org/keycloak/subsystem/extension/RealmDefinitionTestCase.java b/integration/as7-eap-subsystem/src/test/java/org/keycloak/subsystem/extension/RealmDefinitionTestCase.java
new file mode 100755
index 0000000000..5cff75e2c5
--- /dev/null
+++ b/integration/as7-eap-subsystem/src/test/java/org/keycloak/subsystem/extension/RealmDefinitionTestCase.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * 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.extension;
+
+
+import org.jboss.dmr.ModelNode;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+
+
+/**
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc.
+ */
+public class RealmDefinitionTestCase {
+
+ private ModelNode model;
+
+ @Before
+ public void setUp() {
+ model = new ModelNode();
+ model.get("realm").set("demo");
+ model.get("resource").set("customer-portal");
+ model.get("realm-public-key").set("MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB");
+ model.get("auth-url").set("http://localhost:8080/auth-server/rest/realms/demo/tokens/login");
+ model.get("code-url").set("http://localhost:8080/auth-server/rest/realms/demo/tokens/access/codes");
+ model.get("expose-token").set(true);
+ ModelNode credential = new ModelNode();
+ credential.get("password").set("password");
+ model.get("credentials").set(credential);
+ }
+
+ @Test
+ public void testIsTruststoreSetIfRequired() throws Exception {
+ model.get("ssl-not-required").set(true);
+ model.get("disable-trust-manager").set(true);
+ Assert.assertTrue(RealmDefinition.validateTruststoreSetIfRequired(model));
+
+ model.get("ssl-not-required").set(true);
+ model.get("disable-trust-manager").set(false);
+ Assert.assertTrue(RealmDefinition.validateTruststoreSetIfRequired(model));
+
+ model.get("ssl-not-required").set(false);
+ model.get("disable-trust-manager").set(true);
+ Assert.assertTrue(RealmDefinition.validateTruststoreSetIfRequired(model));
+
+ model.get("ssl-not-required").set(false);
+ model.get("disable-trust-manager").set(false);
+ Assert.assertFalse(RealmDefinition.validateTruststoreSetIfRequired(model));
+
+ model.get("ssl-not-required").set(false);
+ model.get("disable-trust-manager").set(false);
+ model.get("truststore").set("foo");
+ Assert.assertFalse(RealmDefinition.validateTruststoreSetIfRequired(model));
+
+ model.get("ssl-not-required").set(false);
+ model.get("disable-trust-manager").set(false);
+ model.get("truststore").set("foo");
+ model.get("truststore-password").set("password");
+ Assert.assertTrue(RealmDefinition.validateTruststoreSetIfRequired(model));
+ }
+
+}
diff --git a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/config/CatalinaAdapterConfigLoader.java b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/config/CatalinaAdapterConfigLoader.java
index 604a2ed741..f1abb9ca54 100755
--- a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/config/CatalinaAdapterConfigLoader.java
+++ b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/config/CatalinaAdapterConfigLoader.java
@@ -1,26 +1,46 @@
package org.keycloak.adapters.as7.config;
import org.apache.catalina.Context;
+import org.jboss.logging.Logger;
+import org.keycloak.adapters.AdapterConstants;
import org.keycloak.adapters.config.RealmConfigurationLoader;
+import javax.servlet.ServletContext;
+import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
public class CatalinaAdapterConfigLoader extends RealmConfigurationLoader {
+ private static final Logger log = Logger.getLogger(CatalinaAdapterConfigLoader.class);
+
+ private InputStream getJSONFromServletContext(ServletContext servletContext) {
+ String json = servletContext.getInitParameter(AdapterConstants.AUTH_DATA_PARAM_NAME);
+ if (json == null) {
+ return null;
+ }
+ log.info("**** using " + AdapterConstants.AUTH_DATA_PARAM_NAME);
+ log.info(json);
+ return new ByteArrayInputStream(json.getBytes());
+ }
public CatalinaAdapterConfigLoader(Context context) {
- InputStream is = null;
- String path = context.getServletContext().getInitParameter("keycloak.config.file");
- if (path == null) {
- is = context.getServletContext().getResourceAsStream("/WEB-INF/keycloak.json");
- } else {
- try {
- is = new FileInputStream(path);
- } catch (FileNotFoundException e) {
- throw new RuntimeException(e);
+ log.info("******* Loading adapter config.");
+ InputStream is = getJSONFromServletContext(context.getServletContext());
+ if (is == null) {
+ String path = context.getServletContext().getInitParameter("keycloak.config.file");
+ if (path == null) {
+ log.info("**** using /WEB-INF/keycloak.json");
+ is = context.getServletContext().getResourceAsStream("/WEB-INF/keycloak.json");
+ } else {
+ try {
+ is = new FileInputStream(path);
+ } catch (FileNotFoundException e) {
+ throw new RuntimeException(e);
+ }
}
}
+ if (is == null) throw new RuntimeException("Could not find keycloak config.");
loadConfig(is);
}
diff --git a/integration/pom.xml b/integration/pom.xml
index b0e25d9fdc..8a81e9db53 100755
--- a/integration/pom.xml
+++ b/integration/pom.xml
@@ -21,6 +21,7 @@
as7-eap6/adapterundertowwildfly-subsystem
+ as7-eap-subsystemjs
diff --git a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/KeycloakServletExtension.java b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/KeycloakServletExtension.java
index 6be2346f59..04e0d64130 100755
--- a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/KeycloakServletExtension.java
+++ b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/KeycloakServletExtension.java
@@ -13,6 +13,7 @@ import io.undertow.servlet.api.LoginConfig;
import io.undertow.servlet.api.ServletSessionConfig;
import java.io.ByteArrayInputStream;
import org.jboss.logging.Logger;
+import org.keycloak.adapters.AdapterConstants;
import org.keycloak.adapters.config.RealmConfiguration;
import org.keycloak.representations.adapters.config.AdapterConfig;
import org.keycloak.adapters.config.RealmConfigurationLoader;
@@ -26,10 +27,6 @@ import java.util.Map;
* @version $Revision: 1 $
*/
public class KeycloakServletExtension implements ServletExtension {
- // This param name is defined again in Keycloak Subsystem class
- // org.keycloak.subsystem.extensionKeycloakAdapterConfigDeploymentProcessor. We have this value in
- // two places to avoid dependency between Keycloak Subsystem and Keyclaok Undertow Integration.
- public static final String AUTH_DATA_PARAM_NAME = "org.keycloak.json.adapterConfig";
protected Logger log = Logger.getLogger(KeycloakServletExtension.class);
@@ -47,7 +44,7 @@ public class KeycloakServletExtension implements ServletExtension {
}
private InputStream getJSONFromServletContext(ServletContext servletContext) {
- String json = servletContext.getInitParameter(AUTH_DATA_PARAM_NAME);
+ String json = servletContext.getInitParameter(AdapterConstants.AUTH_DATA_PARAM_NAME);
if (json == null) {
return null;
}
@@ -61,9 +58,9 @@ public class KeycloakServletExtension implements ServletExtension {
return;
}
log.info("KeycloakServletException initialization");
- InputStream is = servletContext.getResourceAsStream("/WEB-INF/keycloak.json");
+ InputStream is = getJSONFromServletContext(servletContext);
if (is == null) {
- is = getJSONFromServletContext(servletContext);
+ is = servletContext.getResourceAsStream("/WEB-INF/keycloak.json");
}
if (is == null) throw new RuntimeException("Unable to find realm config in /WEB-INF/keycloak.json or in keycloak subsystem.");
RealmConfigurationLoader loader = new RealmConfigurationLoader(is);
diff --git a/integration/wildfly-subsystem/src/main/java/org/keycloak/subsystem/extension/KeycloakAdapterConfigDeploymentProcessor.java b/integration/wildfly-subsystem/src/main/java/org/keycloak/subsystem/extension/KeycloakAdapterConfigDeploymentProcessor.java
index 40b3ec8fb3..9599158266 100755
--- a/integration/wildfly-subsystem/src/main/java/org/keycloak/subsystem/extension/KeycloakAdapterConfigDeploymentProcessor.java
+++ b/integration/wildfly-subsystem/src/main/java/org/keycloak/subsystem/extension/KeycloakAdapterConfigDeploymentProcessor.java
@@ -45,7 +45,7 @@ public class KeycloakAdapterConfigDeploymentProcessor implements DeploymentUnitP
public static final Phase PHASE = Phase.INSTALL;
// Seems wise to have this run after INSTALL_WAR_DEPLOYMENT
- public static final int PRIORITY = Phase.INSTALL_WAR_DEPLOYMENT + 1;
+ public static final int PRIORITY = Phase.INSTALL_WAR_DEPLOYMENT - 1;
@Override
public void deploy(DeploymentPhaseContext phaseContext) throws DeploymentUnitProcessingException {
diff --git a/integration/wildfly-subsystem/src/main/java/org/keycloak/subsystem/extension/RealmDefinition.java b/integration/wildfly-subsystem/src/main/java/org/keycloak/subsystem/extension/RealmDefinition.java
old mode 100644
new mode 100755
index 7ecdcb3fc5..d2fdbd2145
--- a/integration/wildfly-subsystem/src/main/java/org/keycloak/subsystem/extension/RealmDefinition.java
+++ b/integration/wildfly-subsystem/src/main/java/org/keycloak/subsystem/extension/RealmDefinition.java
@@ -87,7 +87,7 @@ public class RealmDefinition extends SimpleResourceDefinition {
new SimpleAttributeDefinitionBuilder("connection-pool-size", ModelType.INT, true)
.setXmlName("connection-pool-size")
.setAllowExpression(true)
- .setValidator(new IntRangeValidator(0))
+ .setValidator(new IntRangeValidator(0, true))
.build();
protected static final List REALM_ONLY_ATTRIBUTES = new ArrayList();
diff --git a/integration/wildfly-subsystem/src/main/java/org/keycloak/subsystem/extension/SharedAttributeDefinitons.java b/integration/wildfly-subsystem/src/main/java/org/keycloak/subsystem/extension/SharedAttributeDefinitons.java
old mode 100644
new mode 100755
index db0ccf8986..e95bb5da4f
--- a/integration/wildfly-subsystem/src/main/java/org/keycloak/subsystem/extension/SharedAttributeDefinitons.java
+++ b/integration/wildfly-subsystem/src/main/java/org/keycloak/subsystem/extension/SharedAttributeDefinitons.java
@@ -60,7 +60,7 @@ public class SharedAttributeDefinitons {
new SimpleAttributeDefinitionBuilder("cors-max-age", ModelType.INT, true)
.setXmlName("cors-max-age")
.setAllowExpression(true)
- .setValidator(new IntRangeValidator(-1))
+ .setValidator(new IntRangeValidator(-1, true))
.build();
protected static final SimpleAttributeDefinition CORS_ALLOWED_HEADERS =
new SimpleAttributeDefinitionBuilder("cors-allowed-headers", ModelType.STRING, true)