Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Bill Burke 2014-11-12 15:38:05 -05:00
commit f552057b06
19 changed files with 306 additions and 67 deletions

View file

@ -1,27 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!--
~ JBoss, Home of Professional Open Source.
~ Copyright 2010, Red Hat, Inc., and individual contributors
~ as indicated by the @author tags. See the copyright.txt file in the
~ distribution for a full listing of individual contributors.
~
~ This is free software; you can redistribute it and/or modify it
~ under the terms of the GNU Lesser General Public License as
~ published by the Free Software Foundation; either version 2.1 of
~ the License, or (at your option) any later version.
~
~ This software is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
~ Lesser General Public License for more details.
~
~ You should have received a copy of the GNU Lesser General Public
~ License along with this software; if not, write to the Free
~ Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
~ 02110-1301 USA, or see the FSF site: http://www.fsf.org.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" <project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
@ -86,7 +64,6 @@
<dependency> <dependency>
<groupId>org.wildfly.core</groupId> <groupId>org.wildfly.core</groupId>
<artifactId>wildfly-cli</artifactId> <artifactId>wildfly-cli</artifactId>
<version>${wildfly.core.version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.picketlink</groupId> <groupId>org.picketlink</groupId>

View file

@ -28,7 +28,7 @@
</properties> </properties>
<resources> <resources>
<artifact name="org.jboss.aesh:aesh:0.33.12"/> <!-- Insert resources here -->
</resources> </resources>
<dependencies> <dependencies>

View file

@ -31,7 +31,7 @@
<main-class name="org.jboss.as.cli.CommandLineMain"/> <main-class name="org.jboss.as.cli.CommandLineMain"/>
<resources> <resources>
<resource-root path="wildfly-cli-1.0.0.Alpha11-SNAPSHOT.jar"/> <!-- Insert resources here -->
</resources> </resources>
<dependencies> <dependencies>

View file

@ -799,7 +799,7 @@ keycloak-war-dist-all-&project.version;/
<title>Using CLI and CLI GUI with the Keycloak Subsystem</title> <title>Using CLI and CLI GUI with the Keycloak Subsystem</title>
<para> <para>
Servers can also be added/removed or enabled/disabled at runtime using the <ulink url="https://developer.jboss.org/wiki/CommandLineInterface">CLI</ulink> or Servers can also be added/removed or enabled/disabled at runtime using the <ulink url="https://developer.jboss.org/wiki/CommandLineInterface">CLI</ulink> or
<ulink url="https://developer.jboss.org/wiki/AGUIForTheCommandLineInterface">CLI GUI</ulink> tool. These are tools that ship with WildFly and also with <ulink url="https://developer.jboss.org/wiki/AGUIForTheCommandLineInterface">CLI GUI</ulink> tool. These are tools that ship with WildFly/EAP and also with
the Keycloak Appliance installation. See <ulink url="https://developer.jboss.org/wiki/CommandLineInterface">CLI</ulink> or the Keycloak Appliance installation. See <ulink url="https://developer.jboss.org/wiki/CommandLineInterface">CLI</ulink> or
<ulink url="https://developer.jboss.org/wiki/AGUIForTheCommandLineInterface">CLI GUI</ulink> documentation to learn more about how to start the tools, <ulink url="https://developer.jboss.org/wiki/AGUIForTheCommandLineInterface">CLI GUI</ulink> documentation to learn more about how to start the tools,
issue commands, and create CLI scripts. issue commands, and create CLI scripts.
@ -812,7 +812,7 @@ cd <APPLIANCE_INSTALL_DIR>/keycloak/bin
or or
./jboss.cli.bat --gui]]> ./jboss.cli.bat --gui]]>
</programlisting> </programlisting>
<note>Your Keycloak server must be running to start in --gui mode.</note> <note>Your server must be running to start in --gui mode.</note>
</para> </para>
<section> <section>
<title>Basic CLI Commands</title> <title>Basic CLI Commands</title>
@ -842,7 +842,7 @@ The Keycloak server will be immediately deployed or undeployed, but not deleted.
<title>Uploading extra configuration using CLI</title> <title>Uploading extra configuration using CLI</title>
<para> <para>
The WildFly Keycloak subsystem allows you to upload keycloak-server.json, provider jars, and theme jars to a Keycloak server instance. The The WildFly Keycloak subsystem allows you to upload keycloak-server.json, provider jars, and theme jars to a Keycloak server instance. The
CLI operations for this are "update-server-config" and "add-provider". You may use plain or CLI scripts for these operations. The following CLI operations for this are "update-server-config" and "add-provider". You may use CLI, CLI GUI, or CLI scripts for these operations. The following
examples are shown using <ulink url="https://developer.jboss.org/wiki/AGUIForTheCommandLineInterface">CLI GUI</ulink> for clarity. examples are shown using <ulink url="https://developer.jboss.org/wiki/AGUIForTheCommandLineInterface">CLI GUI</ulink> for clarity.
</para> </para>
<para> <para>
@ -874,6 +874,39 @@ The Keycloak server will be immediately deployed or undeployed, but not deleted.
<imagedata fileref="images/add-provider-dialog.png"/> <imagedata fileref="images/add-provider-dialog.png"/>
</para> </para>
</section> </section>
<section>
<title>Working with overlays</title>
<para>
When you upload a provider jar, theme jar, or keycloak-server.json file, you are creating an overlay. That is, the file is "overlayed"
onto the Keycloak server at deploy time. There are two additional operations that help you manage these overlays. They are "list-overlays" and
"remove-overlay". Here are CLI examples of these operations.
</para>
<para>
<programlisting>
/subsystem=keycloak/auth-server=my-auth-server/:list-overlays
{
"outcome" => "success",
"result" => [
"/WEB-INF/classes/META-INF/keycloak-server.json",
"/WEB-INF/lib/federation-properties-example.jar"
],
}</programlisting>
<programlisting>
/subsystem=keycloak/auth-server=my-auth-server/:remove-overlay(overlay-file-path=/WEB-INF/lib/federation-properties-example.jar,redeploy=true)
{
"outcome" => "success",
}</programlisting>
</para>
<para>
<note>
Notice in the "list-overlays" operation, the full path to the server config is
/WEB-INF/classes/META-INF/keycloak-server.json. This is always the uploaded path for an "update-server-config" operation.
If you remove this overlay, the Keycloak server will revert to its default keycloak-server.json. If you have a
keycloak-server.json file in your &lt;WILDFLY_HOME&gt;/standalone/configuration directory, it will always take precedence
over both the default and the overlay.
</note>
</para>
</section>
</section> </section>
<section> <section>
<title>Adding a Keycloak server in Domain Mode</title> <title>Adding a Keycloak server in Domain Mode</title>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 90 KiB

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 91 KiB

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 73 KiB

After

Width:  |  Height:  |  Size: 80 KiB

View file

@ -21,12 +21,11 @@ import org.jboss.as.controller.OperationContext;
import org.jboss.as.controller.OperationFailedException; import org.jboss.as.controller.OperationFailedException;
import org.jboss.as.controller.OperationStepHandler; import org.jboss.as.controller.OperationStepHandler;
import org.jboss.as.controller.PathAddress; import org.jboss.as.controller.PathAddress;
import org.jboss.as.controller.PathElement;
import org.jboss.as.controller.ProcessType; import org.jboss.as.controller.ProcessType;
import org.jboss.as.controller.SimpleAttributeDefinition; import org.jboss.as.controller.SimpleAttributeDefinition;
import org.jboss.as.controller.SimpleAttributeDefinitionBuilder; import org.jboss.as.controller.SimpleAttributeDefinitionBuilder;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ADD; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ADD;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.DEPLOYMENT_OVERLAY; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.REMOVE;
import org.jboss.as.controller.operations.common.Util; import org.jboss.as.controller.operations.common.Util;
import org.jboss.as.controller.registry.Resource; import org.jboss.as.controller.registry.Resource;
import org.jboss.dmr.ModelNode; import org.jboss.dmr.ModelNode;
@ -48,21 +47,39 @@ public abstract class AbstractAddOverlayHandler implements OperationStepHandler
.setAllowNull(false) .setAllowNull(false)
.build(); .build();
static final SimpleAttributeDefinition REDEPLOY_SERVER =
new SimpleAttributeDefinitionBuilder("redeploy", ModelType.BOOLEAN, true)
.setXmlName("redeploy")
.setAllowExpression(true)
.setDefaultValue(new ModelNode(false))
.build();
protected static final SimpleAttributeDefinition OVERWRITE =
new SimpleAttributeDefinitionBuilder("overwrite", ModelType.BOOLEAN, true)
.setXmlName("overwrite")
.setAllowExpression(true)
.setDefaultValue(new ModelNode(false))
.build();
@Override @Override
public void execute(final OperationContext context, final ModelNode operation) throws OperationFailedException { public void execute(final OperationContext context, final ModelNode operation) throws OperationFailedException {
//System.out.println("*** execute operation ***"); //System.out.println("*** execute operation ***");
//System.out.println(scrub(operation)); //System.out.println(scrub(operation));
String uploadFileName = operation.get(UPLOADED_FILE_OP_NAME).asString(); String uploadFileName = operation.get(UPLOADED_FILE_OP_NAME).asString();
String overlayPath = getOverlayPath(uploadFileName); boolean isRedeploy = isRedeploy(context, operation);
String overlayName = AuthServerUtil.getAuthServerName(operation) + "-keycloak-overlay"; boolean isOverwrite = getBooleanFromOperation(operation, OVERWRITE);
PathAddress overlayAddress = PathAddress.pathAddress(PathElement.pathElement(DEPLOYMENT_OVERLAY, overlayName));
boolean isOverlayExists = isOverlayExists(context, overlayName, PathAddress.EMPTY_ADDRESS); String overlayPath = getOverlayPath(uploadFileName);
String overlayName = AuthServerUtil.getOverlayName(operation);
PathAddress overlayAddress = AuthServerUtil.getOverlayAddress(overlayName);
String deploymentName = AuthServerUtil.getDeploymentName(operation);
boolean isOverlayExists = AuthServerUtil.isOverlayExists(context, overlayName, PathAddress.EMPTY_ADDRESS);
if (!isOverlayExists) { if (!isOverlayExists) {
addOverlay(context, overlayAddress); addOverlay(context, overlayAddress);
if (!isHostController(context)) { if (!isHostController(context)) {
addDeploymentToOverlay(context, overlayAddress, AuthServerUtil.getDeploymentName(operation)); addDeploymentToOverlay(context, overlayAddress, deploymentName);
} }
} }
@ -70,36 +87,42 @@ public abstract class AbstractAddOverlayHandler implements OperationStepHandler
addOverlayToServerGroups(context, overlayAddress, operation, overlayName); addOverlayToServerGroups(context, overlayAddress, operation, overlayName);
} }
// There is no way to do an overwrite of content from here because it involves
// removing the overlay service in the runtime phase. You have to remove
// the content in a seperate operation.
if (isOverlayExists && isContentExists(context, overlayAddress, overlayPath)) { if (isOverlayExists && isContentExists(context, overlayAddress, overlayPath)) {
throw new OperationFailedException(pathExistsMessage(overlayAddress, overlayPath)); if (isOverwrite) {
removeContent(context, overlayAddress, overlayPath);
} else {
throw new OperationFailedException(pathExistsMessage(overlayAddress, overlayPath));
}
} }
addContent(context, overlayAddress, operation.get(BYTES_TO_UPLOAD.getName()).asBytes(), overlayPath); addContent(context, overlayAddress, operation.get(BYTES_TO_UPLOAD.getName()).asBytes(), overlayPath);
context.restartRequired(); if (isRedeploy) AuthServerUtil.addStepToRedeployAuthServer(context, deploymentName);
if (!isRedeploy) context.restartRequired();
context.completeStep(OperationContext.ResultHandler.NOOP_RESULT_HANDLER); context.completeStep(OperationContext.ResultHandler.NOOP_RESULT_HANDLER);
} }
static void removeContent(OperationContext context, PathAddress overlayAddress, String overlayPath) {
PathAddress contentAddress = overlayAddress.append("content", overlayPath);
ModelNode operation = Util.createRemoveOperation(contentAddress);
context.addStep(operation, getHandler(context, contentAddress, REMOVE), OperationContext.Stage.MODEL);
}
static boolean isRedeploy(OperationContext context, ModelNode operation) {
return isAuthServerEnabled(context) && getBooleanFromOperation(operation, REDEPLOY_SERVER);
}
private boolean isHostController(OperationContext context) { private boolean isHostController(OperationContext context) {
return context.getProcessType() == ProcessType.HOST_CONTROLLER; return context.getProcessType() == ProcessType.HOST_CONTROLLER;
} }
private String pathExistsMessage(PathAddress overlayAddress, String overlayPath) { private String pathExistsMessage(PathAddress overlayAddress, String overlayPath) {
PathAddress contentAddress = overlayAddress.append("content", overlayPath); PathAddress contentAddress = overlayAddress.append("content", overlayPath);
String msg = "Can not update overlay. "; String msg = "Can not update overlay at " + contentAddress.toCLIStyleString();
msg += "First remove the overlay with CLI using the following command with the content path in double quotes: "; msg += " You may try your request again using the " + OVERWRITE.getName() + " attribute.";
msg += contentAddress.toCLIStyleString() + ":remove";
return msg; return msg;
} }
private boolean isOverlayExists(OperationContext context, String overlayName, PathAddress address) {
Resource resource = context.readResourceFromRoot(address);
return resource.getChildrenNames("deployment-overlay").contains(overlayName);
}
private boolean isContentExists(OperationContext context, PathAddress overlayAddress, String overlayPath) { private boolean isContentExists(OperationContext context, PathAddress overlayAddress, String overlayPath) {
Resource resource = context.readResourceFromRoot(overlayAddress); Resource resource = context.readResourceFromRoot(overlayAddress);
return resource.getChildrenNames("content").contains(overlayPath); return resource.getChildrenNames("content").contains(overlayPath);
@ -124,7 +147,7 @@ public abstract class AbstractAddOverlayHandler implements OperationStepHandler
ModelNode serverGroupModel = context.readResourceFromRoot(address).getModel(); ModelNode serverGroupModel = context.readResourceFromRoot(address).getModel();
if (serverGroupModel.get("profile").asString().equals(myProfile)) { if (serverGroupModel.get("profile").asString().equals(myProfile)) {
PathAddress serverGroupOverlayAddress = address.append(overlayAddress); PathAddress serverGroupOverlayAddress = address.append(overlayAddress);
boolean isOverlayExists = isOverlayExists(context, overlayName, address); boolean isOverlayExists = AuthServerUtil.isOverlayExists(context, overlayName, address);
if (!isOverlayExists) { if (!isOverlayExists) {
addOverlay(context, serverGroupOverlayAddress); addOverlay(context, serverGroupOverlayAddress);
addDeploymentToOverlay(context, serverGroupOverlayAddress, AuthServerUtil.getDeploymentName(operation)); addDeploymentToOverlay(context, serverGroupOverlayAddress, AuthServerUtil.getDeploymentName(operation));
@ -160,6 +183,23 @@ public abstract class AbstractAddOverlayHandler implements OperationStepHandler
context.addStep(operation, getHandler(context, address, ADD), OperationContext.Stage.MODEL); context.addStep(operation, getHandler(context, address, ADD), OperationContext.Stage.MODEL);
} }
private static boolean isAuthServerEnabled(OperationContext context) {
boolean defaultValue = AuthServerDefinition.ENABLED.getDefaultValue().asBoolean();
ModelNode authServerModel = context.readResource(PathAddress.EMPTY_ADDRESS).getModel().clone();
String attrName = AuthServerDefinition.ENABLED.getName();
if (!authServerModel.get(attrName).isDefined()) return defaultValue;
return authServerModel.get(attrName).asBoolean();
}
private static boolean getBooleanFromOperation(ModelNode operation, SimpleAttributeDefinition definition) {
boolean defaultValue = definition.getDefaultValue().asBoolean();
if (!operation.get(definition.getName()).isDefined()) {
return defaultValue;
} else {
return operation.get(definition.getName()).asBoolean();
}
}
// used for debugging // used for debugging
private ModelNode scrub(ModelNode op) { private ModelNode scrub(ModelNode op) {
ModelNode scrubbed = op.clone(); ModelNode scrubbed = op.clone();

View file

@ -17,7 +17,6 @@
package org.keycloak.subsystem.extension.authserver; package org.keycloak.subsystem.extension.authserver;
import java.io.File;
import org.jboss.as.controller.OperationDefinition; import org.jboss.as.controller.OperationDefinition;
import org.jboss.as.controller.SimpleAttributeDefinition; import org.jboss.as.controller.SimpleAttributeDefinition;
import org.jboss.as.controller.SimpleAttributeDefinitionBuilder; import org.jboss.as.controller.SimpleAttributeDefinitionBuilder;
@ -46,8 +45,12 @@ public class AddProviderHandler extends AbstractAddOverlayHandler {
public static OperationDefinition DEFINITION = new SimpleOperationDefinitionBuilder(OP, AuthServerDefinition.rscDescriptionResolver) public static OperationDefinition DEFINITION = new SimpleOperationDefinitionBuilder(OP, AuthServerDefinition.rscDescriptionResolver)
.addParameter(BYTES_TO_UPLOAD) .addParameter(BYTES_TO_UPLOAD)
.addParameter(UPLOADED_FILE_NAME) .addParameter(UPLOADED_FILE_NAME)
.addParameter(REDEPLOY_SERVER)
.addParameter(OVERWRITE)
.build(); .build();
private AddProviderHandler() {}
@Override @Override
String getOverlayPath(String fileName) { String getOverlayPath(String fileName) {
if (!fileName.toLowerCase().endsWith(".jar")) { if (!fileName.toLowerCase().endsWith(".jar")) {

View file

@ -95,6 +95,8 @@ public class AuthServerDefinition extends SimpleResourceDefinition {
resourceRegistration.registerOperationHandler(GenericSubsystemDescribeHandler.DEFINITION, GenericSubsystemDescribeHandler.INSTANCE); resourceRegistration.registerOperationHandler(GenericSubsystemDescribeHandler.DEFINITION, GenericSubsystemDescribeHandler.INSTANCE);
resourceRegistration.registerOperationHandler(AddProviderHandler.DEFINITION, AddProviderHandler.INSTANCE); resourceRegistration.registerOperationHandler(AddProviderHandler.DEFINITION, AddProviderHandler.INSTANCE);
resourceRegistration.registerOperationHandler(OverlayKeycloakServerJsonHandler.DEFINITION, OverlayKeycloakServerJsonHandler.INSTANCE); resourceRegistration.registerOperationHandler(OverlayKeycloakServerJsonHandler.DEFINITION, OverlayKeycloakServerJsonHandler.INSTANCE);
resourceRegistration.registerOperationHandler(ListOverlaysHandler.DEFINITION, ListOverlaysHandler.INSTANCE);
resourceRegistration.registerOperationHandler(RemoveOverlayHandler.DEFINITION, RemoveOverlayHandler.INSTANCE);
} }
@Override @Override

View file

@ -31,6 +31,7 @@ import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ARC
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.CONTENT; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.CONTENT;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.DEPLOY; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.DEPLOY;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.DEPLOYMENT; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.DEPLOYMENT;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.DEPLOYMENT_OVERLAY;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ENABLED; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ENABLED;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.PERSISTENT; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.PERSISTENT;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.PATH; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.PATH;
@ -40,6 +41,7 @@ import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.UND
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.URL; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.URL;
import org.jboss.as.controller.operations.common.Util; import org.jboss.as.controller.operations.common.Util;
import org.jboss.as.controller.registry.ImmutableManagementResourceRegistration; import org.jboss.as.controller.registry.ImmutableManagementResourceRegistration;
import org.jboss.as.controller.registry.Resource;
import org.jboss.dmr.ModelNode; import org.jboss.dmr.ModelNode;
import org.jboss.modules.Module; import org.jboss.modules.Module;
import org.jboss.modules.ModuleIdentifier; import org.jboss.modules.ModuleIdentifier;
@ -108,7 +110,7 @@ public class AuthServerUtil {
} }
void addStepToUploadAuthServer(OperationContext context, boolean isEnabled) throws OperationFailedException { void addStepToUploadAuthServer(OperationContext context, boolean isEnabled) throws OperationFailedException {
PathAddress deploymentAddress = deploymentAddress(); PathAddress deploymentAddress = deploymentAddress(deploymentName);
ModelNode op = Util.createOperation(ADD, deploymentAddress); ModelNode op = Util.createOperation(ADD, deploymentAddress);
op.get(ENABLED).set(isEnabled); op.get(ENABLED).set(isEnabled);
op.get(PERSISTENT).set(false); // prevents writing this deployment out to standalone.xml op.get(PERSISTENT).set(false); // prevents writing this deployment out to standalone.xml
@ -137,26 +139,27 @@ public class AuthServerUtil {
return contentItem; return contentItem;
} }
void addStepToRedeployAuthServer(OperationContext context) { static void addStepToRedeployAuthServer(OperationContext context, String deploymentName) {
addDeploymentAction(context, REDEPLOY); addDeploymentAction(context, REDEPLOY, deploymentName);
} }
void addStepToUndeployAuthServer(OperationContext context) { static void addStepToUndeployAuthServer(OperationContext context, String deploymentName) {
addDeploymentAction(context, UNDEPLOY); addDeploymentAction(context, UNDEPLOY, deploymentName);
} }
void addStepToDeployAuthServer(OperationContext context) { static void addStepToDeployAuthServer(OperationContext context, String deploymentName) {
addDeploymentAction(context, DEPLOY); addDeploymentAction(context, DEPLOY, deploymentName);
} }
private void addDeploymentAction(OperationContext context, String operation) { private static void addDeploymentAction(OperationContext context, String operation, String deploymentName) {
PathAddress deploymentAddress = deploymentAddress(); if (!context.isNormalServer()) return;
PathAddress deploymentAddress = deploymentAddress(deploymentName);
ModelNode op = Util.createOperation(operation, deploymentAddress); ModelNode op = Util.createOperation(operation, deploymentAddress);
op.get(RUNTIME_NAME).set(deploymentName); op.get(RUNTIME_NAME).set(deploymentName);
context.addStep(op, getHandler(context, deploymentAddress, operation), OperationContext.Stage.MODEL); context.addStep(op, getHandler(context, deploymentAddress, operation), OperationContext.Stage.MODEL);
} }
private PathAddress deploymentAddress() { private static PathAddress deploymentAddress(String deploymentName) {
return PathAddress.pathAddress(PathElement.pathElement(DEPLOYMENT, deploymentName)); return PathAddress.pathAddress(PathElement.pathElement(DEPLOYMENT, deploymentName));
} }
@ -183,4 +186,17 @@ public class AuthServerUtil {
return PathAddress.pathAddress(operation.get(ADDRESS)); return PathAddress.pathAddress(operation.get(ADDRESS));
} }
static PathAddress getOverlayAddress(String overlayName) {
return PathAddress.pathAddress(PathElement.pathElement(DEPLOYMENT_OVERLAY, overlayName));
}
static String getOverlayName(ModelNode operation) {
return AuthServerUtil.getAuthServerName(operation) + "-keycloak-overlay";
}
static boolean isOverlayExists(OperationContext context, String overlayName, PathAddress address) {
Resource resource = context.readResourceFromRoot(address);
return resource.getChildrenNames(DEPLOYMENT_OVERLAY).contains(overlayName);
}
} }

View file

@ -50,23 +50,23 @@ public class AuthServerWriteAttributeHandler extends ModelOnlyWriteAttributeHand
return; return;
} }
AuthServerUtil authServerUtil = new AuthServerUtil(operation);
boolean isEnabled = isEnabled(model); // is server currently enabled? boolean isEnabled = isEnabled(model); // is server currently enabled?
String deploymentName = AuthServerUtil.getDeploymentName(operation);
if (attributeName.equals(AuthServerDefinition.WEB_CONTEXT.getName())) { if (attributeName.equals(AuthServerDefinition.WEB_CONTEXT.getName())) {
String deploymentName = AuthServerUtil.getDeploymentName(operation);
KeycloakAdapterConfigService.INSTANCE.removeServerDeployment(deploymentName); KeycloakAdapterConfigService.INSTANCE.removeServerDeployment(deploymentName);
KeycloakAdapterConfigService.INSTANCE.addServerDeployment(deploymentName, newValue.asString()); KeycloakAdapterConfigService.INSTANCE.addServerDeployment(deploymentName, newValue.asString());
if (isEnabled) { if (isEnabled) {
authServerUtil.addStepToRedeployAuthServer(context); AuthServerUtil.addStepToRedeployAuthServer(context, deploymentName);
} }
} }
if (attributeName.equals(AuthServerDefinition.ENABLED.getName())) { if (attributeName.equals(AuthServerDefinition.ENABLED.getName())) {
if (!isEnabled) { // we are disabling if (!isEnabled) { // we are disabling
authServerUtil.addStepToUndeployAuthServer(context); AuthServerUtil.addStepToUndeployAuthServer(context, deploymentName);
} else { // we are enabling } else { // we are enabling
authServerUtil.addStepToDeployAuthServer(context); AuthServerUtil.addStepToDeployAuthServer(context, deploymentName);
} }
} }

View file

@ -0,0 +1,76 @@
/*
* 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.authserver;
import java.util.Set;
import java.util.TreeSet;
import org.jboss.as.controller.OperationContext;
import org.jboss.as.controller.OperationDefinition;
import org.jboss.as.controller.OperationFailedException;
import org.jboss.as.controller.OperationStepHandler;
import org.jboss.as.controller.PathAddress;
import org.jboss.as.controller.SimpleOperationDefinitionBuilder;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.CONTENT;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.DEPLOYMENT_OVERLAY;
import org.jboss.as.controller.registry.Resource;
import org.jboss.dmr.ModelNode;
import org.jboss.dmr.ModelType;
/**
* Operation to list all of the provider jars, theme jars, and keycloak-server.json that
* have been uploaded to the auth server.
*
* @author Stan Silvert ssilvert@redhat.com (C) 2014 Red Hat Inc.
*/
public class ListOverlaysHandler implements OperationStepHandler {
static final String LIST_OVERLAYS_OPERATION = "list-overlays";
static final OperationDefinition DEFINITION = new SimpleOperationDefinitionBuilder(LIST_OVERLAYS_OPERATION, AuthServerDefinition.rscDescriptionResolver)
.setReadOnly()
.setRuntimeOnly()
.setReplyType(ModelType.LIST)
.setReplyValueType(ModelType.STRING)
.build();
static final OperationStepHandler INSTANCE = new ListOverlaysHandler();
private ListOverlaysHandler() {}
@Override
public void execute(OperationContext context, ModelNode operation) throws OperationFailedException {
final ModelNode result = context.getResult();
result.setEmptyList();
String overlayName = AuthServerUtil.getOverlayName(operation);
boolean isOverlayExists = AuthServerUtil.isOverlayExists(context, overlayName, PathAddress.EMPTY_ADDRESS);
if (isOverlayExists) {
Set<String> overlays = new TreeSet<String>(getOverlayNames(context, overlayName));
for (final String key : overlays) {
result.add(key);
}
}
context.stepCompleted();
}
private Set<String> getOverlayNames(OperationContext context, String overlayName) {
PathAddress overlayAddr = PathAddress.pathAddress(DEPLOYMENT_OVERLAY, overlayName);
Resource resource = context.readResourceFromRoot(overlayAddr);
return resource.getChildrenNames(CONTENT);
}
}

View file

@ -33,8 +33,12 @@ public class OverlayKeycloakServerJsonHandler extends AbstractAddOverlayHandler
public static OperationDefinition DEFINITION = new SimpleOperationDefinitionBuilder(OP, AuthServerDefinition.rscDescriptionResolver) public static OperationDefinition DEFINITION = new SimpleOperationDefinitionBuilder(OP, AuthServerDefinition.rscDescriptionResolver)
.addParameter(BYTES_TO_UPLOAD) .addParameter(BYTES_TO_UPLOAD)
.addParameter(REDEPLOY_SERVER)
.addParameter(OVERWRITE)
.build(); .build();
private OverlayKeycloakServerJsonHandler() {}
@Override @Override
String getOverlayPath(String fileName) { String getOverlayPath(String fileName) {
return "/WEB-INF/classes/META-INF/keycloak-server.json"; return "/WEB-INF/classes/META-INF/keycloak-server.json";

View file

@ -0,0 +1,76 @@
/*
* 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.authserver;
import org.jboss.as.controller.OperationContext;
import org.jboss.as.controller.OperationDefinition;
import org.jboss.as.controller.OperationFailedException;
import org.jboss.as.controller.OperationStepHandler;
import org.jboss.as.controller.PathAddress;
import org.jboss.as.controller.SimpleAttributeDefinition;
import org.jboss.as.controller.SimpleAttributeDefinitionBuilder;
import org.jboss.as.controller.SimpleOperationDefinitionBuilder;
import org.jboss.dmr.ModelNode;
import org.jboss.dmr.ModelType;
/**
* Operation to remove a provider jars, theme jars, or keycloak-server.json that
* has been uploaded to the auth server.
*
* @author Stan Silvert ssilvert@redhat.com (C) 2014 Red Hat Inc.
*/
public class RemoveOverlayHandler implements OperationStepHandler {
static final String REMOVE_OVERLAY_OPERATION = "remove-overlay";
protected static final SimpleAttributeDefinition OVERLAY_FILE_PATH =
new SimpleAttributeDefinitionBuilder("overlay-file-path", ModelType.STRING, false)
.setAllowExpression(true)
.setAllowNull(false)
.setDefaultValue(new ModelNode().set("/WEB-INF/lib/myprovider.jar"))
.build();
static final OperationDefinition DEFINITION = new SimpleOperationDefinitionBuilder(REMOVE_OVERLAY_OPERATION, AuthServerDefinition.rscDescriptionResolver)
.addParameter(OVERLAY_FILE_PATH)
.addParameter(AbstractAddOverlayHandler.REDEPLOY_SERVER)
.build();
static final OperationStepHandler INSTANCE = new RemoveOverlayHandler();
private RemoveOverlayHandler() {}
@Override
public void execute(OperationContext context, ModelNode operation) throws OperationFailedException {
String overlayName = AuthServerUtil.getOverlayName(operation);
boolean isOverlayExists = AuthServerUtil.isOverlayExists(context, overlayName, PathAddress.EMPTY_ADDRESS);
String overlayPath = operation.get(OVERLAY_FILE_PATH.getName()).asString();
if (isOverlayExists) {
PathAddress overlayAddress = AuthServerUtil.getOverlayAddress(overlayName);
AbstractAddOverlayHandler.removeContent(context, overlayAddress, overlayPath);
} else {
context.setRollbackOnly();
throw new OperationFailedException("Overlay path " + overlayPath + " not found.");
}
boolean isRedeploy = AbstractAddOverlayHandler.isRedeploy(context, operation);
String deploymentName = AuthServerUtil.getDeploymentName(operation);
if (isRedeploy) AuthServerUtil.addStepToRedeployAuthServer(context, deploymentName);
if (!isRedeploy) context.restartRequired();
context.stepCompleted();
}
}

View file

@ -12,9 +12,16 @@ keycloak.auth-server.remove=Remove an Auth Server from the subsystem.
keycloak.auth-server.add-provider=Add a provider service jar to the Keycloak auth server. keycloak.auth-server.add-provider=Add a provider service jar to the Keycloak auth server.
keycloak.auth-server.add-provider.uploaded-file-name=The file name of the provider service jar to be added or updated. keycloak.auth-server.add-provider.uploaded-file-name=The file name of the provider service jar to be added or updated.
keycloak.auth-server.add-provider.bytes-to-upload=The bytes of the provider service jar to be added or updated. keycloak.auth-server.add-provider.bytes-to-upload=The bytes of the provider service jar to be added or updated.
keycloak.auth-server.add-provider.redeploy=Redeploy the auth server after adding the provider. Ignored if auth server is disabled.
keycloak.auth-server.add-provider.overwrite=Overwrite even if the uploaded-file-name already exists as an overlay.
keycloak.auth-server.list-overlays=List the overlays uploaded for this auth server.
keycloak.auth-server.remove-overlay=Remove a provider jar, theme jar, or keycloak-server.json that has been uploaded to the auth server.
keycloak.auth-server.remove-overlay.overlay-file-path=The uploaded path and file name of the overlay to be removed.
keycloak.auth-server.remove-overlay.redeploy=Redeploy the auth server after removing the overlay.
keycloak.auth-server.update-server-config=Upload a new keycloak-server.json configuration file for the Keycloak auth server. keycloak.auth-server.update-server-config=Upload a new keycloak-server.json configuration file for the Keycloak auth server.
keycloak.auth-server.update-server-config.uploaded-file-name=Should be the name keycloak-server.json.
keycloak.auth-server.update-server-config.bytes-to-upload=The bytes of the keycloak-server.json file to be added or updated. keycloak.auth-server.update-server-config.bytes-to-upload=The bytes of the keycloak-server.json file to be added or updated.
keycloak.auth-server.update-server-config.redeploy=Redeploy the auth server after updating the server config.
keycloak.auth-server.update-server-config.overwrite=Overwrite even if keycloak-server.json already exitss as an overlay.
keycloak.auth-server.enabled=Enable or disable the Auth Server. keycloak.auth-server.enabled=Enable or disable the Auth Server.
keycloak.auth-server.web-context=Web context the auth-server will use. Also, the module name of the auth-server deployment. keycloak.auth-server.web-context=Web context the auth-server will use. Also, the module name of the auth-server deployment.

View file

@ -36,7 +36,7 @@
<slf4j.version>1.5.10</slf4j.version> <slf4j.version>1.5.10</slf4j.version>
<jboss.version>7.1.1.Final</jboss.version> <jboss.version>7.1.1.Final</jboss.version>
<wildfly.version>8.1.0.Final</wildfly.version> <wildfly.version>8.1.0.Final</wildfly.version>
<wildfly.core.version>1.0.0.Alpha9</wildfly.core.version> <wildfly.core.version>1.0.0.Alpha12</wildfly.core.version>
<servlet.api.30.version>1.0.2.Final</servlet.api.30.version> <servlet.api.30.version>1.0.2.Final</servlet.api.30.version>
<google.zxing.version>2.2</google.zxing.version> <google.zxing.version>2.2</google.zxing.version>
<google.client.version>1.14.1-beta</google.client.version> <google.client.version>1.14.1-beta</google.client.version>
@ -470,6 +470,11 @@
<type>pom</type> <type>pom</type>
<version>${wildfly.core.version}</version> <version>${wildfly.core.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.wildfly.core</groupId>
<artifactId>wildfly-cli</artifactId>
<version>${wildfly.core.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.wildfly.core</groupId> <groupId>org.wildfly.core</groupId>
<artifactId>wildfly-core-feature-pack</artifactId> <artifactId>wildfly-core-feature-pack</artifactId>