Merge pull request #1340 from mstruk/srv-subsystem

Cleanup, and simplify keycloak-server-subsystem
This commit is contained in:
Stian Thorgersen 2015-06-09 07:05:07 +01:00
commit 966d939c12
23 changed files with 185 additions and 927 deletions

View file

@ -126,7 +126,7 @@
<version>${project.version}</version>
<type>war</type>
<overWrite>true</overWrite>
<outputDirectory>${project.build.directory}/${project.build.finalName}/modules/system/layers/base/org/keycloak/keycloak-server-subsystem/main/auth-server</outputDirectory>
<outputDirectory>${project.build.directory}/${project.build.finalName}/modules/system/layers/base/org/keycloak/keycloak-server-subsystem/main/server-war</outputDirectory>
</artifactItem>
</artifactItems>
</configuration>

View file

@ -25,7 +25,7 @@
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-server-subsystem">
<properties>
<property name="keycloak-version" value="${project.version}"/>
<property name="auth-server-exploded" value="false"/>
<property name="server-war-exploded" value="false"/>
</properties>
<resources>

View file

@ -14,14 +14,8 @@
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.keycloak.subsystem.server.extension;
import java.util.HashMap;
import java.util.Map;
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.
@ -30,37 +24,25 @@ import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ADD
*/
public final class KeycloakAdapterConfigService {
private static final KeycloakAdapterConfigService INSTANCE = new KeycloakAdapterConfigService();
static final KeycloakAdapterConfigService INSTANCE = new KeycloakAdapterConfigService();
public static KeycloakAdapterConfigService getInstance() {
return INSTANCE;
}
// key=auth-server deployment name; value=web-context
private final Map<String, String> webContexts = new HashMap<String, String>();
static final String DEPLOYMENT_NAME = "keycloak-server";
private String webContext;
private KeycloakAdapterConfigService() {
}
public void addServerDeployment(String deploymentName, String webContext) {
this.webContexts.put(deploymentName, webContext);
void setWebContext(String webContext) {
this.webContext = webContext;
}
public String getWebContext(String deploymentName) {
return webContexts.get(deploymentName);
String getWebContext() {
return webContext;
}
public void removeServerDeployment(String deploymentName) {
this.webContexts.remove(deploymentName);
}
public boolean isWebContextUsed(String webContext) {
return webContexts.containsValue(webContext);
}
public boolean isKeycloakServerDeployment(String deploymentName) {
return this.webContexts.containsKey(deploymentName);
boolean isKeycloakServerDeployment(String deploymentName) {
return DEPLOYMENT_NAME.equals(deploymentName);
}
}

View file

@ -24,11 +24,9 @@ import org.jboss.as.controller.ResourceDefinition;
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.keycloak.subsystem.server.extension.authserver.AuthServerDefinition;
import org.keycloak.subsystem.server.logging.KeycloakLogger;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SUBSYSTEM;
import static org.keycloak.subsystem.server.logging.KeycloakLogger.ROOT_LOGGER;
/**
@ -38,17 +36,16 @@ import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SUB
*/
public class KeycloakExtension implements Extension {
public static final String SUBSYSTEM_NAME = "keycloak-server";
public static final String NAMESPACE = "urn:jboss:domain:keycloak-server:1.1";
private static final KeycloakSubsystemParser PARSER = new KeycloakSubsystemParser();
static final String SUBSYSTEM_NAME = "keycloak-server";
static final String NAMESPACE = "urn:jboss:domain:keycloak-server:1.1";
static final PathElement PATH_SUBSYSTEM = PathElement.pathElement(SUBSYSTEM, SUBSYSTEM_NAME);
private static final String RESOURCE_NAME = KeycloakExtension.class.getPackage().getName() + ".LocalDescriptions";
private static final ModelVersion MGMT_API_VERSION = ModelVersion.create(1,1,0);
static final PathElement SUBSYSTEM_PATH = PathElement.pathElement(SUBSYSTEM, SUBSYSTEM_NAME);
private static final ResourceDefinition KEYCLOAK_SUBSYSTEM_RESOURCE = new KeycloakSubsystemDefinition();
static final AuthServerDefinition AUTH_SERVER_DEFINITION = new AuthServerDefinition();
public static StandardResourceDescriptionResolver getResourceDescriptionResolver(final String... keyPrefix) {
private static final String RESOURCE_NAME = KeycloakExtension.class.getPackage().getName() + ".LocalDescriptions";
private static final ResourceDefinition KEYCLOAK_SUBSYSTEM_RESOURCE = new KeycloakSubsystemDefinition();
private static final KeycloakSubsystemParser PARSER = new KeycloakSubsystemParser();
private static final ModelVersion MGMT_API_VERSION = ModelVersion.create(1,1,0);
static StandardResourceDescriptionResolver getResourceDescriptionResolver(final String... keyPrefix) {
StringBuilder prefix = new StringBuilder(SUBSYSTEM_NAME);
for (String kp : keyPrefix) {
prefix.append('.').append(kp);
@ -61,7 +58,7 @@ public class KeycloakExtension implements Extension {
*/
@Override
public void initializeParsers(final ExtensionParsingContext context) {
context.setSubsystemXmlMapping(SUBSYSTEM_NAME, KeycloakExtension.NAMESPACE, PARSER);
context.setSubsystemXmlMapping(SUBSYSTEM_NAME, NAMESPACE, PARSER);
}
/**
@ -69,11 +66,10 @@ public class KeycloakExtension implements Extension {
*/
@Override
public void initialize(final ExtensionContext context) {
KeycloakLogger.ROOT_LOGGER.debug("Activating Keycloak Extension");
ROOT_LOGGER.debug("Activating Keycloak Extension");
final SubsystemRegistration subsystem = context.registerSubsystem(SUBSYSTEM_NAME, MGMT_API_VERSION);
ManagementResourceRegistration registration = subsystem.registerSubsystemModel(KEYCLOAK_SUBSYSTEM_RESOURCE);
registration.registerSubModel(AUTH_SERVER_DEFINITION);
subsystem.registerSubsystemModel(KEYCLOAK_SUBSYSTEM_RESOURCE);
subsystem.registerXMLElementWriter(PARSER);
}
}

View file

@ -14,14 +14,13 @@
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.keycloak.subsystem.server.extension.authserver;
package org.keycloak.subsystem.server.extension;
import org.jboss.as.ee.component.EEModuleDescription;
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.keycloak.subsystem.server.extension.KeycloakAdapterConfigService;
/**
* DUP responsible for setting the web context of a Keycloak auth server.
@ -33,22 +32,22 @@ public class KeycloakServerDeploymentProcessor implements DeploymentUnitProcesso
@Override
public void deploy(DeploymentPhaseContext phaseContext) throws DeploymentUnitProcessingException {
DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit();
KeycloakAdapterConfigService config = KeycloakAdapterConfigService.INSTANCE;
String deploymentName = deploymentUnit.getName();
KeycloakAdapterConfigService service = KeycloakAdapterConfigService.getInstance();
if (!service.isKeycloakServerDeployment(deploymentName)) {
if (!config.isKeycloakServerDeployment(deploymentName)) {
return;
}
final EEModuleDescription description = deploymentUnit.getAttachment(org.jboss.as.ee.component.Attachments.EE_MODULE_DESCRIPTION);
String webContext = service.getWebContext(deploymentName);
String webContext = config.getWebContext();
if (webContext == null) {
throw new DeploymentUnitProcessingException("Can't determine web context/module for Keycloak Auth Server");
throw new DeploymentUnitProcessingException("Can't determine web context/module for Keycloak Server");
}
description.setModuleName(webContext);
}
@Override
public void undeploy(DeploymentUnit du) {
}
}

View file

@ -17,13 +17,18 @@
package org.keycloak.subsystem.server.extension;
import org.jboss.as.controller.AbstractBoottimeAddStepHandler;
import org.jboss.as.controller.AttributeDefinition;
import org.jboss.as.controller.OperationContext;
import org.jboss.as.controller.OperationFailedException;
import org.jboss.as.controller.registry.Resource;
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.keycloak.subsystem.server.extension.authserver.KeycloakServerDeploymentProcessor;
import static org.keycloak.subsystem.server.extension.KeycloakExtension.SUBSYSTEM_NAME;
import static org.keycloak.subsystem.server.extension.KeycloakSubsystemDefinition.ALL_ATTRIBUTES;
import static org.keycloak.subsystem.server.extension.KeycloakSubsystemDefinition.WEB_CONTEXT;
/**
* The Keycloak subsystem add update handler.
@ -35,15 +40,44 @@ class KeycloakSubsystemAdd extends AbstractBoottimeAddStepHandler {
static final KeycloakSubsystemAdd INSTANCE = new KeycloakSubsystemAdd();
@Override
protected void performBoottime(final OperationContext context, ModelNode operation, final ModelNode model) {
protected void performBoottime(final OperationContext context, final ModelNode operation, final ModelNode model) {
context.addStep(new AbstractDeploymentChainStep() {
@Override
protected void execute(DeploymentProcessorTarget processorTarget) {
processorTarget.addDeploymentProcessor(KeycloakExtension.SUBSYSTEM_NAME,
processorTarget.addDeploymentProcessor(SUBSYSTEM_NAME,
Phase.POST_MODULE, // PHASE
Phase.POST_MODULE_VALIDATOR_FACTORY - 1, // PRIORITY
new KeycloakServerDeploymentProcessor());
}
}, OperationContext.Stage.RUNTIME);
}
protected void populateModel(final OperationContext context, final ModelNode operation, final Resource resource) throws OperationFailedException {
ModelNode model = resource.getModel();
// set attribute values from parsed model
for (AttributeDefinition attrDef : ALL_ATTRIBUTES) {
attrDef.validateAndSet(operation, model);
}
// returns early if on domain controller
if (!requiresRuntime(context)) {
return;
}
// don't want to try to start server on host controller
if (!context.isNormalServer()) {
return;
}
ModelNode webContextNode = resource.getModel().get(WEB_CONTEXT.getName());
if (!webContextNode.isDefined()) {
webContextNode = WEB_CONTEXT.getDefaultValue();
}
String webContext = webContextNode.asString();
ServerUtil serverUtil = new ServerUtil(operation);
serverUtil.addStepToUploadServerWar(context);
KeycloakAdapterConfigService.INSTANCE.setWebContext(webContext);
}
}

View file

@ -14,25 +14,56 @@
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.keycloak.subsystem.server.extension;
import org.jboss.as.controller.ReloadRequiredRemoveStepHandler;
import org.jboss.as.controller.AttributeDefinition;
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.registry.ManagementResourceRegistration;
import org.jboss.dmr.ModelNode;
import org.jboss.dmr.ModelType;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Definition of subsystem=keycloak.
* Definition of subsystem=keycloak-server.
*
* @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc.
*/
public class KeycloakSubsystemDefinition extends SimpleResourceDefinition {
static final SimpleAttributeDefinition WEB_CONTEXT =
new SimpleAttributeDefinitionBuilder("web-context", ModelType.STRING, true)
.setAllowExpression(true)
.setDefaultValue(new ModelNode("auth"))
.setRestartAllServices()
.build();
static final List<SimpleAttributeDefinition> ALL_ATTRIBUTES = new ArrayList<SimpleAttributeDefinition>();
static {
ALL_ATTRIBUTES.add(WEB_CONTEXT);
}
private static final Map<String, SimpleAttributeDefinition> DEFINITION_LOOKUP = new HashMap<String, SimpleAttributeDefinition>();
static {
for (SimpleAttributeDefinition def : ALL_ATTRIBUTES) {
DEFINITION_LOOKUP.put(def.getXmlName(), def);
}
}
private static KeycloakSubsystemWriteAttributeHandler attrHandler = new KeycloakSubsystemWriteAttributeHandler(ALL_ATTRIBUTES);
protected KeycloakSubsystemDefinition() {
super(KeycloakExtension.SUBSYSTEM_PATH,
KeycloakExtension.getResourceDescriptionResolver("subsystem"),
KeycloakSubsystemAdd.INSTANCE,
ReloadRequiredRemoveStepHandler.INSTANCE
super(KeycloakExtension.PATH_SUBSYSTEM,
KeycloakExtension.getResourceDescriptionResolver("subsystem"),
KeycloakSubsystemAdd.INSTANCE,
KeycloakSubsystemRemoveHandler.INSTANCE
);
}
@ -42,4 +73,15 @@ public class KeycloakSubsystemDefinition extends SimpleResourceDefinition {
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);
}
}

View file

@ -16,17 +16,11 @@
*/
package org.keycloak.subsystem.server.extension;
import org.keycloak.subsystem.server.extension.authserver.AuthServerDefinition;
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.operations.common.Util;
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;
@ -34,9 +28,11 @@ import org.jboss.staxmapper.XMLExtendedStreamWriter;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import java.util.Collections;
import java.util.List;
import static org.keycloak.subsystem.server.extension.KeycloakExtension.PATH_SUBSYSTEM;
import static org.keycloak.subsystem.server.extension.KeycloakSubsystemDefinition.WEB_CONTEXT;
/**
* The subsystem parser, which uses stax to read and write to and from xml
*/
@ -49,12 +45,14 @@ class KeycloakSubsystemParser implements XMLStreamConstants, XMLElementReader<Li
public void readElement(final XMLExtendedStreamReader reader, final List<ModelNode> list) throws XMLStreamException {
// Require no attributes
ParseUtils.requireNoAttributes(reader);
ModelNode addKeycloakSub = Util.createAddOperation(PathAddress.pathAddress(KeycloakExtension.PATH_SUBSYSTEM));
ModelNode addKeycloakSub = Util.createAddOperation(PathAddress.pathAddress(PATH_SUBSYSTEM));
list.add(addKeycloakSub);
while (reader.hasNext() && nextTag(reader) != END_ELEMENT) {
if (reader.getLocalName().equals(AuthServerDefinition.TAG_NAME)) {
readAuthServer(reader, list);
if (reader.getLocalName().equals(WEB_CONTEXT.getXmlName())) {
WEB_CONTEXT.parseAndSetParameter(reader.getElementText(), addKeycloakSub, reader);
} else {
throw new XMLStreamException("Unknown keycloak-server subsystem tag: " + reader.getLocalName());
}
}
}
@ -64,64 +62,21 @@ class KeycloakSubsystemParser implements XMLStreamConstants, XMLElementReader<Li
return reader.nextTag();
}
private void readAuthServer(XMLExtendedStreamReader reader, List<ModelNode> list) throws XMLStreamException {
String authServerName = readNameAttribute(reader);
ModelNode addAuthServer = new ModelNode();
addAuthServer.get(ModelDescriptionConstants.OP).set(ModelDescriptionConstants.ADD);
PathAddress addr = PathAddress.pathAddress(PathElement.pathElement(ModelDescriptionConstants.SUBSYSTEM, KeycloakExtension.SUBSYSTEM_NAME),
PathElement.pathElement(AuthServerDefinition.TAG_NAME, authServerName));
addAuthServer.get(ModelDescriptionConstants.OP_ADDR).set(addr.toModelNode());
while (reader.hasNext() && nextTag(reader) != END_ELEMENT) {
String tagName = reader.getLocalName();
SimpleAttributeDefinition def = AuthServerDefinition.lookup(tagName);
if (def == null) throw new XMLStreamException("Unknown auth-server tag " + tagName);
def.parseAndSetParameter(reader.getElementText(), addAuthServer, reader);
}
list.add(addAuthServer);
}
// 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);
writeAuthServers(writer, context);
writeWebContext(writer, context);
writer.writeEndElement();
}
private void writeAuthServers(XMLExtendedStreamWriter writer, SubsystemMarshallingContext context) throws XMLStreamException {
if (!context.getModelNode().get(AuthServerDefinition.TAG_NAME).isDefined()) {
private void writeWebContext(XMLExtendedStreamWriter writer, SubsystemMarshallingContext context) throws XMLStreamException {
if (!context.getModelNode().get(WEB_CONTEXT.getName()).isDefined()) {
return;
}
for (Property authServer : context.getModelNode().get(AuthServerDefinition.TAG_NAME).asPropertyList()) {
writer.writeStartElement(AuthServerDefinition.TAG_NAME);
writer.writeAttribute("name", authServer.getName());
ModelNode authServerElements = authServer.getValue();
for (AttributeDefinition element : AuthServerDefinition.ALL_ATTRIBUTES) {
element.marshallAsElement(authServerElements, writer);
}
writer.writeEndElement();
}
WEB_CONTEXT.marshallAsElement(context.getModelNode(), writer);
}
}

View file

@ -14,18 +14,16 @@
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.keycloak.subsystem.server.extension;
package org.keycloak.subsystem.server.extension.authserver;
import org.jboss.as.controller.AbstractRemoveStepHandler;
import org.jboss.as.controller.OperationContext;
import org.jboss.as.controller.OperationFailedException;
import org.jboss.as.controller.OperationStepHandler;
import org.jboss.as.controller.PathAddress;
import org.jboss.as.controller.PathElement;
import org.jboss.as.controller.ReloadRequiredRemoveStepHandler;
import org.jboss.as.controller.operations.common.Util;
import org.jboss.dmr.ModelNode;
import org.keycloak.subsystem.server.extension.KeycloakAdapterConfigService;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.DEPLOYMENT;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.REMOVE;
@ -36,25 +34,25 @@ import org.jboss.as.controller.registry.ImmutableManagementResourceRegistration;
*
* @author Stan Silvert ssilvert@redhat.com (C) 2014 Red Hat Inc.
*/
public final class AuthServerRemoveHandler extends AbstractRemoveStepHandler {
public final class KeycloakSubsystemRemoveHandler extends ReloadRequiredRemoveStepHandler {
public static AuthServerRemoveHandler INSTANCE = new AuthServerRemoveHandler();
static KeycloakSubsystemRemoveHandler INSTANCE = new KeycloakSubsystemRemoveHandler();
private AuthServerRemoveHandler() {}
private KeycloakSubsystemRemoveHandler() {}
@Override
protected void performRemove(OperationContext context, ModelNode operation, ModelNode model) throws OperationFailedException {
String deploymentName = AuthServerUtil.getDeploymentName(operation);
KeycloakAdapterConfigService.getInstance().removeServerDeployment(deploymentName);
String deploymentName = ServerUtil.getDeploymentName(operation);
KeycloakAdapterConfigService.INSTANCE.setWebContext(null);
if (requiresRuntime(context)) { // don't do this on a domain controller
addStepToRemoveAuthServer(context, deploymentName);
addStepToRemoveServerWar(context, deploymentName);
}
super.performRemove(context, operation, model);
}
private void addStepToRemoveAuthServer(OperationContext context, String deploymentName) {
private void addStepToRemoveServerWar(OperationContext context, String deploymentName) {
PathAddress deploymentAddress = PathAddress.pathAddress(PathElement.pathElement(DEPLOYMENT, deploymentName));
ModelNode op = Util.createOperation(REMOVE, deploymentAddress);
context.addStep(op, getRemoveHandler(context, deploymentAddress), OperationContext.Stage.MODEL);

View file

@ -14,8 +14,7 @@
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.keycloak.subsystem.server.extension.authserver;
package org.keycloak.subsystem.server.extension;
import org.jboss.as.controller.AttributeDefinition;
import org.jboss.as.controller.SimpleAttributeDefinition;
@ -26,20 +25,19 @@ import org.jboss.as.controller.OperationContext;
import org.jboss.as.controller.OperationFailedException;
import org.jboss.as.controller.registry.Resource;
import org.jboss.dmr.ModelNode;
import org.keycloak.subsystem.server.extension.KeycloakAdapterConfigService;
/**
* Update an attribute on an Auth Server.
*
* @author Stan Silvert ssilvert@redhat.com (C) 2014 Red Hat Inc.
*/
public class AuthServerWriteAttributeHandler extends ModelOnlyWriteAttributeHandler { //extends ReloadRequiredWriteAttributeHandler {
public class KeycloakSubsystemWriteAttributeHandler extends ModelOnlyWriteAttributeHandler { //extends ReloadRequiredWriteAttributeHandler {
public AuthServerWriteAttributeHandler(List<SimpleAttributeDefinition> definitions) {
public KeycloakSubsystemWriteAttributeHandler(List<SimpleAttributeDefinition> definitions) {
this(definitions.toArray(new AttributeDefinition[definitions.size()]));
}
public AuthServerWriteAttributeHandler(AttributeDefinition... definitions) {
public KeycloakSubsystemWriteAttributeHandler(AttributeDefinition... definitions) {
super(definitions);
}
@ -50,34 +48,24 @@ public class AuthServerWriteAttributeHandler extends ModelOnlyWriteAttributeHand
return;
}
boolean isEnabled = AuthServerDefinition.ENABLED.resolveModelAttribute(context, model.getModel()).asBoolean();
String deploymentName = AuthServerUtil.getDeploymentName(operation);
String deploymentName = ServerUtil.getDeploymentName(operation);
if (attributeName.equals(AuthServerDefinition.WEB_CONTEXT.getName())) {
KeycloakAdapterConfigService.getInstance().removeServerDeployment(deploymentName);
KeycloakAdapterConfigService.getInstance().addServerDeployment(deploymentName, newValue.asString());
if (isEnabled) {
AuthServerUtil.addStepToRedeployAuthServer(context, deploymentName);
}
}
if (attributeName.equals(AuthServerDefinition.ENABLED.getName())) {
if (!isEnabled) { // we are disabling
AuthServerUtil.addStepToUndeployAuthServer(context, deploymentName);
} else { // we are enabling
AuthServerUtil.addStepToDeployAuthServer(context, deploymentName);
}
if (attributeName.equals(KeycloakSubsystemDefinition.WEB_CONTEXT.getName())) {
KeycloakAdapterConfigService.INSTANCE.setWebContext(newValue.asString());
ServerUtil.addStepToRedeployServerWar(context, deploymentName);
}
super.finishModelStage(context, operation, attributeName, newValue, oldValue, model);
}
private boolean attribNotChanging(String attributeName, ModelNode newValue, ModelNode oldValue) {
SimpleAttributeDefinition attribDef = AuthServerDefinition.lookup(attributeName);
if (!oldValue.isDefined()) oldValue = attribDef.getDefaultValue();
if (!newValue.isDefined()) newValue = attribDef.getDefaultValue();
SimpleAttributeDefinition attribDef = KeycloakSubsystemDefinition.lookup(attributeName);
if (!oldValue.isDefined()) {
oldValue = attribDef.getDefaultValue();
}
if (!newValue.isDefined()) {
newValue = attribDef.getDefaultValue();
}
return newValue.equals(oldValue);
}
}

View file

@ -14,7 +14,7 @@
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.keycloak.subsystem.server.extension.authserver;
package org.keycloak.subsystem.server.extension;
import java.io.File;
import java.net.URI;
@ -29,50 +29,42 @@ import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ADD
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ADDRESS;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ARCHIVE;
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.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.PERSISTENT;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.PATH;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.REDEPLOY;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.RUNTIME_NAME;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.UNDEPLOY;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.URL;
import org.jboss.as.controller.operations.common.Util;
import org.jboss.as.controller.registry.ImmutableManagementResourceRegistration;
import org.jboss.as.controller.registry.Resource;
import org.jboss.dmr.ModelNode;
import org.jboss.modules.Module;
import org.jboss.modules.ModuleIdentifier;
import org.jboss.modules.ModuleLoadException;
import org.keycloak.subsystem.server.extension.KeycloakExtension;
/**
* Utility methods that help assemble and start an auth server.
*
* @author Stan Silvert ssilvert@redhat.com (C) 2014 Red Hat Inc.
*/
public class AuthServerUtil {
public class ServerUtil {
private static final ModuleIdentifier KEYCLOAK_SUBSYSTEM = ModuleIdentifier.create("org.keycloak.keycloak-server-subsystem");
private final String deploymentName;
private final Module subsysModule;
private final String keycloakVersion;
private final boolean isAuthServerExploded;
private final URI authServerUri;
private final boolean isServerWarExploded;
private final URI serverWar;
AuthServerUtil(ModelNode operation) {
ServerUtil(ModelNode operation) {
this.deploymentName = getDeploymentName(operation);
this.subsysModule = findSubsysModule();
this.keycloakVersion = subsysModule.getProperty("keycloak-version");
this.isAuthServerExploded = Boolean.parseBoolean(subsysModule.getProperty("auth-server-exploded"));
this.authServerUri = findAuthServerUri();
}
String getDeploymentName() {
return this.deploymentName;
this.isServerWarExploded = Boolean.parseBoolean(subsysModule.getProperty("server-war-exploded"));
this.serverWar = findServerWarUri();
}
private Module findSubsysModule() {
@ -83,15 +75,15 @@ public class AuthServerUtil {
}
}
private URI findAuthServerUri() throws IllegalStateException {
private URI findServerWarUri() throws IllegalStateException {
try {
URL subsysResource = this.subsysModule.getExportedResource("module.xml");
File subsysDir = new File(subsysResource.toURI()).getParentFile();
File authServerDir = new File(subsysDir, "auth-server");
if (this.isAuthServerExploded) {
return authServerDir.toURI();
File serverWarDir = new File(subsysDir, "server-war");
if (this.isServerWarExploded) {
return serverWarDir.toURI();
} else {
return new File(authServerDir, "keycloak-server-" + keycloakVersion + ".war").toURI();
return new File(serverWarDir, "keycloak-server-" + keycloakVersion + ".war").toURI();
}
} catch (URISyntaxException e) {
throw new IllegalStateException(e);
@ -100,17 +92,21 @@ public class AuthServerUtil {
}
}
void addStepToUploadAuthServer(OperationContext context, boolean isEnabled) throws OperationFailedException {
void addStepToUploadServerWar(OperationContext context) throws OperationFailedException {
PathAddress deploymentAddress = deploymentAddress(deploymentName);
ModelNode op = Util.createOperation(ADD, deploymentAddress);
op.get(ENABLED).set(isEnabled);
op.get(PERSISTENT).set(false); // prevents writing this deployment out to standalone.xml
// this is required for deployment to take place
op.get(ENABLED).set(true);
// prevents writing this deployment out to standalone.xml
op.get(PERSISTENT).set(false);
// Owner attribute is valid starting with WidlFly 9. Ignored in WildFly 8
op.get("owner").set(new ModelNode().add("subsystem", KeycloakExtension.SUBSYSTEM_NAME));
if (authServerUri == null) {
throw new OperationFailedException("Keycloak Auth Server WAR not found in keycloak-server-subsystem module");
if (serverWar == null) {
throw new OperationFailedException("Keycloak Server WAR not found in keycloak-server-subsystem module");
}
op.get(CONTENT).add(makeContentItem());
@ -121,32 +117,26 @@ public class AuthServerUtil {
private ModelNode makeContentItem() throws OperationFailedException {
ModelNode contentItem = new ModelNode();
if (this.isAuthServerExploded) {
String urlString = new File(authServerUri).getAbsolutePath();
if (this.isServerWarExploded) {
String urlString = new File(serverWar).getAbsolutePath();
contentItem.get(PATH).set(urlString);
contentItem.get(ARCHIVE).set(false);
} else {
String urlString = authServerUri.toString();
String urlString = serverWar.toString();
contentItem.get(URL).set(urlString);
}
return contentItem;
}
static void addStepToRedeployAuthServer(OperationContext context, String deploymentName) {
static void addStepToRedeployServerWar(OperationContext context, String deploymentName) {
addDeploymentAction(context, REDEPLOY, deploymentName);
}
static void addStepToUndeployAuthServer(OperationContext context, String deploymentName) {
addDeploymentAction(context, UNDEPLOY, deploymentName);
}
static void addStepToDeployAuthServer(OperationContext context, String deploymentName) {
addDeploymentAction(context, DEPLOY, deploymentName);
}
private static void addDeploymentAction(OperationContext context, String operation, String deploymentName) {
if (!context.isNormalServer()) return;
if (!context.isNormalServer()) {
return;
}
PathAddress deploymentAddress = deploymentAddress(deploymentName);
ModelNode op = Util.createOperation(operation, deploymentAddress);
op.get(RUNTIME_NAME).set(deploymentName);
@ -170,27 +160,4 @@ public class AuthServerUtil {
return deploymentName;
}
static String getAuthServerName(ModelNode operation) {
PathAddress pathAddr = getPathAddress(operation);
return pathAddr.getElement(pathAddr.size() - 1).getValue();
}
static PathAddress getPathAddress(ModelNode operation) {
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

@ -1,206 +0,0 @@
/*
* 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.server.extension.authserver;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ADD;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.REMOVE;
import java.util.Set;
import org.jboss.as.controller.AbstractAddStepHandler;
import org.jboss.as.controller.OperationContext;
import org.jboss.as.controller.OperationFailedException;
import org.jboss.as.controller.PathAddress;
import org.jboss.as.controller.ProcessType;
import org.jboss.as.controller.SimpleAttributeDefinition;
import org.jboss.as.controller.SimpleAttributeDefinitionBuilder;
import org.jboss.as.controller.operations.common.Util;
import org.jboss.as.controller.registry.Resource;
import org.jboss.dmr.ModelNode;
import org.jboss.dmr.ModelType;
/**
* Base class for operations that create overlays for an auth server.
*
* @author Stan Silvert ssilvert@redhat.com (C) 2014 Red Hat Inc.
*/
public abstract class AbstractAddOverlayHandler extends AbstractAddStepHandler{
protected static final String UPLOADED_FILE_OP_NAME = "uploaded-file-name";
protected static final SimpleAttributeDefinition UPLOADED_FILE_NAME =new SimpleAttributeDefinitionBuilder(UPLOADED_FILE_OP_NAME, ModelType.STRING, false)
.setAllowExpression(false)
.setAllowNull(false)
.build();
protected static final SimpleAttributeDefinition BYTES_TO_UPLOAD= new SimpleAttributeDefinitionBuilder("bytes-to-upload", ModelType.BYTES, false)
.setAllowExpression(false)
.build();
static final SimpleAttributeDefinition REDEPLOY_SERVER =
new SimpleAttributeDefinitionBuilder("redeploy", ModelType.BOOLEAN, true)
.setAllowExpression(true)
.setDefaultValue(new ModelNode(false))
.build();
protected static final SimpleAttributeDefinition OVERWRITE =
new SimpleAttributeDefinitionBuilder("overwrite", ModelType.BOOLEAN, true)
.setAllowExpression(true)
.setDefaultValue(new ModelNode(false))
.build();
public AbstractAddOverlayHandler() {
super(AddProviderHandler.DEFINITION.getParameters());
}
@Override
protected void performRuntime(OperationContext context, ModelNode operation, ModelNode model) throws OperationFailedException {
final String uploadFileName = UPLOADED_FILE_NAME.resolveModelAttribute(context, model).asString();
final boolean isRedeploy = isRedeploy(context, operation);
final boolean isOverwrite = OVERWRITE.resolveModelAttribute(context, model).asBoolean();
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) {
addOverlay(context, overlayAddress);
if (!isHostController(context)) {
addDeploymentToOverlay(context, overlayAddress, deploymentName);
}
}
if (isHostController(context)) {
addOverlayToServerGroups(context, overlayAddress, operation, overlayName);
}
if (isOverlayExists && isContentExists(context, overlayAddress, overlayPath)) {
if (isOverwrite) {
removeContent(context, overlayAddress, overlayPath);
} else {
throw new OperationFailedException(pathExistsMessage(overlayAddress, overlayPath));
}
}
addContent(context, overlayAddress, BYTES_TO_UPLOAD.resolveModelAttribute(context, model).asBytes(), overlayPath);
if (isRedeploy) { AuthServerUtil.addStepToRedeployAuthServer(context, deploymentName); }
if (!isRedeploy) { context.restartRequired(); }
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, AuthServerUtil.getHandler(context, contentAddress, REMOVE), OperationContext.Stage.MODEL);
}
static boolean isRedeploy(OperationContext context, ModelNode model) throws OperationFailedException {
return isAuthServerEnabled(context) && REDEPLOY_SERVER.resolveModelAttribute(context, model).asBoolean();
}
private boolean isHostController(OperationContext context) {
return context.getProcessType() == ProcessType.HOST_CONTROLLER;
}
private String pathExistsMessage(PathAddress overlayAddress, String overlayPath) {
PathAddress contentAddress = overlayAddress.append("content", overlayPath);
String msg = "Can not update overlay at " + contentAddress.toCLIStyleString();
msg += " You may try your request again using the " + OVERWRITE.getName() + " attribute.";
return msg;
}
private boolean isContentExists(OperationContext context, PathAddress overlayAddress, String overlayPath) {
Resource resource = context.readResourceFromRoot(overlayAddress);
return resource.getChildrenNames("content").contains(overlayPath);
}
private void addOverlay(OperationContext context, PathAddress overlayAddress) {
ModelNode op = Util.createAddOperation(overlayAddress);
doAddStep(context, overlayAddress, op);
}
private void addDeploymentToOverlay(OperationContext context, PathAddress overlayAddress, String deploymentName) {
PathAddress deploymentAddress = overlayAddress.append("deployment", deploymentName);
ModelNode op = Util.createAddOperation(deploymentAddress);
doAddStep(context, deploymentAddress, op);
}
// only call this if context.getProcessType() == ProcessType.HOST_CONTROLLER
private void addOverlayToServerGroups(OperationContext context, PathAddress overlayAddress, ModelNode operation, String overlayName) {
String myProfile = context.getCurrentAddressValue();
for (String serverGroup : getServerGroupNames(context)) {
PathAddress address = PathAddress.pathAddress("server-group", serverGroup);
ModelNode serverGroupModel = context.readResourceFromRoot(address).getModel();
if (serverGroupModel.get("profile").asString().equals(myProfile)) {
PathAddress serverGroupOverlayAddress = address.append(overlayAddress);
boolean isOverlayExists = AuthServerUtil.isOverlayExists(context, overlayName, address);
if (!isOverlayExists) {
addOverlay(context, serverGroupOverlayAddress);
addDeploymentToOverlay(context, serverGroupOverlayAddress, AuthServerUtil.getDeploymentName(operation));
}
}
}
}
private Set<String> getServerGroupNames(OperationContext context) {
return context.readResourceFromRoot(PathAddress.EMPTY_ADDRESS).getChildrenNames("server-group");
}
private void addContent(OperationContext context, PathAddress overlayAddress, byte[] bytes, String overlayPath) throws OperationFailedException {
PathAddress contentAddress = overlayAddress.append("content", overlayPath);
ModelNode op = Util.createAddOperation(contentAddress);
ModelNode content = new ModelNode();
content.get("bytes").set(bytes);
op.get("content").set(content);
doAddStep(context, contentAddress, op);
}
private void doAddStep(OperationContext context, PathAddress address, ModelNode operation) {
//System.out.println("**** Adding Add Step ****");
//System.out.println(scrub(operation).toString());
context.addStep(operation, AuthServerUtil.getHandler(context, address, ADD), OperationContext.Stage.MODEL);
}
private static boolean isAuthServerEnabled(OperationContext context) throws OperationFailedException {
ModelNode authServerModel = context.readResource(PathAddress.EMPTY_ADDRESS).getModel().clone();
return AuthServerDefinition.ENABLED.resolveModelAttribute(context, authServerModel).asBoolean();
}
// used for debugging
private ModelNode scrub(ModelNode op) {
ModelNode scrubbed = op.clone();
if (scrubbed.has("content")) {
scrubbed.get("content").set("BYTES REMOVED FOR DISPLAY");
}
if (scrubbed.has("bytes-to-upload")) {
scrubbed.get("bytes-to-upload").set("BYTES REMOVED FOR DISPLAY");
}
return scrubbed;
}
/**
* Get the WAR path where the overlay will live.
*
* @param fileName The name of the file being uploaded.
* @return The overlay path as a String.
*/
abstract String getOverlayPath(String fileName);
}

View file

@ -1,51 +0,0 @@
/*
* 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.server.extension.authserver;
import org.jboss.as.controller.OperationDefinition;
import org.jboss.as.controller.SimpleOperationDefinitionBuilder;
/**
* Operation to add a provider jar to WEB-INF/lib.
*
* @author Stan Silvert ssilvert@redhat.com (C) 2014 Red Hat Inc.
*/
public class AddProviderHandler extends AbstractAddOverlayHandler {
public static final String OP = "add-provider";
public static OperationDefinition DEFINITION = new SimpleOperationDefinitionBuilder(OP, AuthServerDefinition.rscDescriptionResolver)
.addParameter(BYTES_TO_UPLOAD)
.addParameter(UPLOADED_FILE_NAME)
.addParameter(REDEPLOY_SERVER)
.addParameter(OVERWRITE)
.build();
public static final AddProviderHandler INSTANCE = new AddProviderHandler();
private AddProviderHandler() {}
@Override
String getOverlayPath(String fileName) {
if (!fileName.toLowerCase().endsWith(".jar")) {
throw new IllegalArgumentException("Uploaded file name must end with .jar");
}
return "/WEB-INF/lib/" + fileName;
}
}

View file

@ -1,74 +0,0 @@
/*
* 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.server.extension.authserver;
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.dmr.ModelNode;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ADD;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP;
import org.jboss.as.controller.registry.Resource;
import org.keycloak.subsystem.server.extension.KeycloakAdapterConfigService;
/**
* Add an auth server.
*
* @author Stan Silvert ssilvert@redhat.com (C) 2014 Red Hat Inc.
*/
public final class AuthServerAddHandler extends AbstractAddStepHandler {
public static AuthServerAddHandler INSTANCE = new AuthServerAddHandler();
private AuthServerAddHandler() {
}
@Override
protected void populateModel(OperationContext context, ModelNode operation, Resource resource) throws OperationFailedException {
// TODO: localize exception. get id number
if (!operation.get(OP).asString().equals(ADD)) {
throw new OperationFailedException("Unexpected operation for add Auth Server. operation=" + operation.toString());
}
ModelNode model = resource.getModel();
for (AttributeDefinition attr : AuthServerDefinition.ALL_ATTRIBUTES) {
attr.validateAndSet(operation, model);
}
model = context.resolveExpressions(model);
// returns early if on domain controller
if (!requiresRuntime(context)) return;
// don't want to try to start server on host controller
if (!context.isNormalServer()) return;
ModelNode webContextNode = model.get(AuthServerDefinition.WEB_CONTEXT.getName());
if (!webContextNode.isDefined()) webContextNode = AuthServerDefinition.WEB_CONTEXT.getDefaultValue();
String webContext = webContextNode.asString();
ModelNode isEnabled = model.get("enabled");
boolean enabled = isEnabled.isDefined() && isEnabled.asBoolean();
AuthServerUtil authServerUtil = new AuthServerUtil(operation);
authServerUtil.addStepToUploadAuthServer(context, enabled);
KeycloakAdapterConfigService.getInstance().addServerDeployment(authServerUtil.getDeploymentName(), webContext);
}
}

View file

@ -1,131 +0,0 @@
/*
* 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.server.extension.authserver;
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.registry.ManagementResourceRegistration;
import org.jboss.dmr.ModelNode;
import org.jboss.dmr.ModelType;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.jboss.as.controller.OperationFailedException;
import org.jboss.as.controller.descriptions.ResourceDescriptionResolver;
import org.jboss.as.controller.operations.validation.ParameterValidator;
import org.jboss.as.controller.registry.OperationEntry;
import org.keycloak.subsystem.server.extension.KeycloakAdapterConfigService;
import org.keycloak.subsystem.server.extension.KeycloakExtension;
/**
* Defines attributes and operations for an Auth Server
*
* @author Stan Silvert ssilvert@redhat.com (C) 2014 Red Hat Inc.
*/
public class AuthServerDefinition extends SimpleResourceDefinition {
public static final String TAG_NAME = "auth-server";
protected static final SimpleAttributeDefinition ENABLED =
new SimpleAttributeDefinitionBuilder("enabled", ModelType.BOOLEAN, true)
.setAllowExpression(true)
.setDefaultValue(new ModelNode(false))
.setRestartAllServices()
.build();
protected static final SimpleAttributeDefinition WEB_CONTEXT =
new SimpleAttributeDefinitionBuilder("web-context", ModelType.STRING, true)
.setAllowExpression(true)
.setDefaultValue(new ModelNode("auth"))
.setValidator(new WebContextValidator())
.setRestartAllServices()
.build();
protected static final ResourceDescriptionResolver rscDescriptionResolver = KeycloakExtension.getResourceDescriptionResolver(TAG_NAME);
public static final List<SimpleAttributeDefinition> ALL_ATTRIBUTES = new ArrayList<SimpleAttributeDefinition>();
static {
ALL_ATTRIBUTES.add(ENABLED);
ALL_ATTRIBUTES.add(WEB_CONTEXT);
}
private static final Map<String, SimpleAttributeDefinition> DEFINITION_LOOKUP = new HashMap<String, SimpleAttributeDefinition>();
static {
for (SimpleAttributeDefinition def : ALL_ATTRIBUTES) {
DEFINITION_LOOKUP.put(def.getXmlName(), def);
}
}
private static AuthServerWriteAttributeHandler attrHandler = new AuthServerWriteAttributeHandler(ALL_ATTRIBUTES);
public AuthServerDefinition() {
super(PathElement.pathElement(TAG_NAME),
rscDescriptionResolver,
AuthServerAddHandler.INSTANCE,
AuthServerRemoveHandler.INSTANCE,
null,
OperationEntry.Flag.RESTART_ALL_SERVICES);
}
@Override
public void registerOperations(ManagementResourceRegistration resourceRegistration) {
super.registerOperations(resourceRegistration);
resourceRegistration.registerOperationHandler(GenericSubsystemDescribeHandler.DEFINITION, GenericSubsystemDescribeHandler.INSTANCE);
resourceRegistration.registerOperationHandler(AddProviderHandler.DEFINITION, AddProviderHandler.INSTANCE);
resourceRegistration.registerOperationHandler(OverlayKeycloakServerJsonHandler.DEFINITION, OverlayKeycloakServerJsonHandler.INSTANCE);
resourceRegistration.registerOperationHandler(ListOverlaysHandler.DEFINITION, ListOverlaysHandler.INSTANCE);
resourceRegistration.registerOperationHandler(RemoveOverlayHandler.DEFINITION, RemoveOverlayHandler.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);
}
private static class WebContextValidator implements ParameterValidator {
@Override
public void validateParameter(String paramName, ModelNode value) throws OperationFailedException {
String strValue = value.asString();
if (KeycloakAdapterConfigService.getInstance().isWebContextUsed(strValue)) {
throw new OperationFailedException("Can not set web-context to '" + strValue + "'. web-context must be unique among all deployments.");
}
}
@Override
public void validateResolvedParameter(String paramName, ModelNode value) throws OperationFailedException {
String strValue = value.asString();
if (KeycloakAdapterConfigService.getInstance().isWebContextUsed(strValue)) {
throw new OperationFailedException("Can not set web-context to '" + strValue + "'. web-context must be unique among all deployments.");
}
}
}
}

View file

@ -1,74 +0,0 @@
/*
* 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.server.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);
}
}
}
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

@ -1,47 +0,0 @@
/*
* 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.server.extension.authserver;
import org.jboss.as.controller.OperationDefinition;
import org.jboss.as.controller.SimpleOperationDefinitionBuilder;
/**
* Operation to overlay keycloak-server.json.
*
* @author Stan Silvert ssilvert@redhat.com (C) 2014 Red Hat Inc.
*/
public class OverlayKeycloakServerJsonHandler extends AbstractAddOverlayHandler {
public static final String OP = "update-server-config";
public static final OverlayKeycloakServerJsonHandler INSTANCE = new OverlayKeycloakServerJsonHandler();
public static OperationDefinition DEFINITION = new SimpleOperationDefinitionBuilder(OP, AuthServerDefinition.rscDescriptionResolver)
.addParameter(BYTES_TO_UPLOAD)
.addParameter(REDEPLOY_SERVER)
.addParameter(OVERWRITE)
.build();
private OverlayKeycloakServerJsonHandler() {}
@Override
String getOverlayPath(String fileName) {
return "/WEB-INF/classes/META-INF/keycloak-server.json";
}
}

View file

@ -1,78 +0,0 @@
/*
* 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.server.extension.authserver;
import static org.keycloak.subsystem.server.extension.authserver.AbstractAddOverlayHandler.REDEPLOY_SERVER;
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)
.build();
static final OperationDefinition DEFINITION = new SimpleOperationDefinitionBuilder(REMOVE_OVERLAY_OPERATION, AuthServerDefinition.rscDescriptionResolver)
.addParameter(OVERLAY_FILE_PATH)
.addParameter(REDEPLOY_SERVER)
.build();
static final OperationStepHandler INSTANCE = new RemoveOverlayHandler();
private RemoveOverlayHandler() {}
@Override
public void execute(OperationContext context, ModelNode operation) throws OperationFailedException {
final ModelNode model = new ModelNode();
OVERLAY_FILE_PATH.validateAndSet(operation, model);
REDEPLOY_SERVER.validateAndSet(operation, model);
String overlayName = AuthServerUtil.getOverlayName(operation);
boolean isOverlayExists = AuthServerUtil.isOverlayExists(context, overlayName, PathAddress.EMPTY_ADDRESS);
String overlayPath = OVERLAY_FILE_PATH.resolveModelAttribute(context, model).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();
}
}

View file

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

View file

@ -18,21 +18,8 @@
]]>
</xs:documentation>
</xs:annotation>
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="auth-server" maxOccurs="1" minOccurs="0" type="auth-server-type"/>
<xs:choice minOccurs="0" maxOccurs="1">
<xs:element name="web-context" type="xs:string" minOccurs="0" maxOccurs="1"/>
</xs:choice>
</xs:complexType>
<xs:complexType name="auth-server-type">
<xs:all>
<xs:element name="web-context" type="xs:string" minOccurs="1" maxOccurs="1"/>
<xs:element name="enabled" type="xs:boolean" minOccurs="0" maxOccurs="1"/>
</xs:all>
<xs:attribute name="name" type="xs:string" use="required">
<xs:annotation>
<xs:documentation>The name of the war archive containing the Keycloak server web application.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:schema>

View file

@ -3,9 +3,6 @@
<config>
<extension-module>org.keycloak.keycloak-server-subsystem</extension-module>
<subsystem xmlns="urn:jboss:domain:keycloak-server:1.1">
<auth-server name="main-auth-server">
<enabled>true</enabled>
<web-context>auth</web-context>
</auth-server>
<web-context>auth</web-context>
</subsystem>
</config>

View file

@ -41,7 +41,6 @@ public class SubsystemParsingTestCase extends AbstractSubsystemBaseTest {
@Test
public void testJson() throws Exception {
ModelNode node = new ModelNode();
node.get("enabled").set(true);
node.get("web-context").set("auth");
System.out.println("json=" + node.toJSONString(false));

View file

@ -1,6 +1,3 @@
<subsystem xmlns="urn:jboss:domain:keycloak-server:1.1">
<auth-server name="main-auth-server">
<enabled>true</enabled>
<web-context>auth</web-context>
</auth-server>
<web-context>auth</web-context>
</subsystem>