more subsystem work

This commit is contained in:
Bill Burke 2014-02-13 10:43:29 -05:00
parent 4c71eeb627
commit 9390e90b85
38 changed files with 475 additions and 362 deletions

View file

@ -23,6 +23,26 @@
</xsl:copy>
</xsl:template>
<!-- for some reason, Wildfly 8 final decided to turn off management-native which means jboss-as-maven-plugin no
longer works -->
<xsl:template match="node()[name(.)='management-interfaces']">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
<native-interface security-realm="ManagementRealm">
<socket-binding native="management-native"/>
</native-interface>
</xsl:copy>
</xsl:template>
<!-- for some reason, Wildfly 8 final decided to turn off management-native which means jboss-as-maven-plugin no
longer works -->
<xsl:template match="node()[name(.)='socket-binding-group']">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
<socket-binding name="management-native" interface="management" port="9999"/>
</xsl:copy>
</xsl:template>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()" />

View file

@ -20,10 +20,11 @@ import java.util.List;
*/
public class CustomerDatabaseClient {
static class TypedList extends ArrayList<String> {}
static class TypedList extends ArrayList<String> {
}
public static List<String> getCustomers(HttpServletRequest req) {
SkeletonKeySession session = (SkeletonKeySession)req.getAttribute(SkeletonKeySession.class.getName());
SkeletonKeySession session = (SkeletonKeySession) req.getAttribute(SkeletonKeySession.class.getName());
HttpClient client = new HttpClientBuilder()
.trustStore(session.getMetadata().getTruststore())

View file

@ -3,7 +3,7 @@
<dependencies>
<!-- the Demo code uses classes in these modules. These are optional to import if you are not using
Apache Http Client or the HttpClientBuilder that comes with the adapter core -->
<module name="org.apache.httpcomponents" />
<module name="org.apache.httpcomponents"/>
</dependencies>
</deployment>
</jboss-deployment-structure>

View file

@ -1,11 +1,11 @@
{
"realm" : "demo",
"resource" : "customer-portal",
"realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"auth-server-url" : "http://localhost:8080/auth",
"ssl-not-required" : true,
"expose-token" : true,
"credentials" : {
"password" : "password"
}
"realm": "demo",
"resource": "customer-portal",
"realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"auth-server-url": "http://localhost:8080/auth",
"ssl-not-required": true,
"expose-token": true,
"credentials": {
"password": "password"
}
}

View file

@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<module-name>customer-portal</module-name>
<module-name>customer-portal</module-name>
<security-constraint>
<web-resource-collection>

View file

@ -1,11 +1,12 @@
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
pageEncoding="ISO-8859-1" %>
<html>
<head>
<title>Customer Admin Interface</title>
</head>
<body bgcolor="#E3F6CE">
<h1>Customer Admin Interface</h1>
User <b><%=request.getUserPrincipal().getName()%></b> made this request.
User <b><%=request.getUserPrincipal().getName()%>
</b> made this request.
</body>
</html>

View file

@ -1,5 +1,5 @@
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
pageEncoding="ISO-8859-1" %>
<%@ page import="org.keycloak.example.CustomerDatabaseClient" %>
<%@ page import="org.keycloak.util.KeycloakUriBuilder" %>
<html>
@ -10,20 +10,21 @@
<%
String logoutUri = KeycloakUriBuilder.fromUri("http://localhost:8080/auth/rest/realms/demo/tokens/logout")
.queryParam("redirect_uri", "http://localhost:8080/customer-portal").build().toString();
String acctUri = "http://localhost:8080/auth/rest/realms/demo/account";
String acctUri = "http://localhost:8080/auth/rest/realms/demo/account";
%>
<p>Goto: <a href="http://localhost:8080/product-portal">products</a> | <a href="<%=logoutUri%>">logout</a> | <a href="<%=acctUri%>">manage acct</a></p>
User <b><%=request.getUserPrincipal().getName()%></b> made this request.
<p>Goto: <a href="http://localhost:8080/product-portal">products</a> | <a href="<%=logoutUri%>">logout</a> | <a
href="<%=acctUri%>">manage acct</a></p>
User <b><%=request.getUserPrincipal().getName()%>
</b> made this request.
<h2>Customer Listing</h2>
<%
java.util.List<String> list = CustomerDatabaseClient.getCustomers(request);
for (String cust : list)
{
out.print("<p>");
out.print(cust);
out.println("</p>");
java.util.List<String> list = CustomerDatabaseClient.getCustomers(request);
for (String cust : list) {
out.print("<p>");
out.print(cust);
out.println("</p>");
}
}
%>
<br><br>
</body>

View file

@ -8,6 +8,7 @@
<h1>Customer Portal</h1>
<p><a href="customers/view.jsp">Customer Listing</a></p>
<p><a href="admin/admin.html">Customer Admin Interface</a></p>
</body>

View file

@ -11,16 +11,18 @@ import java.util.List;
* @version $Revision: 1 $
*/
@Path("customers")
public class CustomerService
{
@GET
@Produces("application/json")
public List<String> getCustomers()
{
ArrayList<String> rtn = new ArrayList<String>();
rtn.add("Bill Burke");
rtn.add("Ron Sigal");
rtn.add("Weinan Li");
return rtn;
}
public class CustomerService {
@GET
@Produces("application/json")
public List<String> getCustomers() {
ArrayList<String> rtn = new ArrayList<String>();
rtn.add("Bill Burke");
rtn.add("Stian Thorgersen");
rtn.add("Stan Silvert");
rtn.add("Gabriel Cordoso");
rtn.add("Viliam Rockai");
rtn.add("Marek Posolda");
rtn.add("Boleslaw Dawidowicz");
return rtn;
}
}

View file

@ -11,16 +11,14 @@ import java.util.List;
* @version $Revision: 1 $
*/
@Path("products")
public class ProductService
{
@GET
@Produces("application/json")
public List<String> getProducts()
{
ArrayList<String> rtn = new ArrayList<String>();
rtn.add("iphone");
rtn.add("ipad");
rtn.add("ipod");
return rtn;
}
public class ProductService {
@GET
@Produces("application/json")
public List<String> getProducts() {
ArrayList<String> rtn = new ArrayList<String>();
rtn.add("iphone");
rtn.add("ipad");
rtn.add("ipod");
return rtn;
}
}

View file

@ -4,17 +4,20 @@
<realm-public-key>MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB</realm-public-key>
<auth-server-url>http://localhost:8080/auth</auth-server-url>
<ssl-not-required>true</ssl-not-required>
<secure-deployment name="customer-portal.war">
<resource>customer-portal</resource>
<credential name="password">password</credential>
</secure-deployment>
<secure-deployment name="product-portal.war">
<resource>product-portal</resource>
<credential name="password">password</credential>
</secure-deployment>
<secure-deployment name="database.war">
<resource>database</resource>
<credential name="password">password</credential>
</secure-deployment>
</realm>
<secure-deployment name="customer-portal.war">
<realm>demo</realm>
<resource>customer-portal</resource>
<credential name="password">password</credential>
</secure-deployment>
<secure-deployment name="product-portal.war">
<realm>demo</realm>
<resource>product-portal</resource>
<credential name="password">password</credential>
</secure-deployment>
<secure-deployment name="database.war">
<realm>demo</realm>
<resource>database-service</resource>
<bearer-only>true</bearer-only>
</secure-deployment>
</subsystem>

View file

@ -39,11 +39,29 @@
<version>3.1.2.GA</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-core</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-servlet-oauth-client</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>${keycloak.apache.httpcomponents.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>

View file

@ -0,0 +1,9 @@
<jboss-deployment-structure>
<deployment>
<dependencies>
<!-- the Demo code uses classes in these modules. These are optional to import if you are not using
Apache Http Client or the HttpClientBuilder that comes with the adapter core -->
<module name="org.apache.httpcomponents" />
</dependencies>
</deployment>
</jboss-deployment-structure>

View file

@ -21,11 +21,29 @@
<version>1.0.1.Final</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-core</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-servlet-oauth-client</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>${keycloak.apache.httpcomponents.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>

View file

@ -0,0 +1,9 @@
<jboss-deployment-structure>
<deployment>
<dependencies>
<!-- the Demo code uses classes in these modules. These are optional to import if you are not using
Apache Http Client or the HttpClientBuilder that comes with the adapter core -->
<module name="org.apache.httpcomponents" />
</dependencies>
</deployment>
</jboss-deployment-structure>

View file

@ -17,8 +17,6 @@
package org.keycloak.subsystem.extension;
import java.util.HashMap;
import java.util.Map;
import org.jboss.as.controller.OperationContext;
import org.jboss.dmr.ModelNode;
import org.jboss.dmr.Property;
@ -31,6 +29,9 @@ import org.jboss.msc.service.StartContext;
import org.jboss.msc.service.StartException;
import org.jboss.msc.service.StopContext;
import java.util.HashMap;
import java.util.Map;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ADDRESS;
/**
@ -86,7 +87,6 @@ public final class KeycloakAdapterConfigService implements Service<KeycloakAdapt
public void addSecureDeployment(ModelNode operation, ModelNode model) {
ModelNode deployment = model.clone();
deployment.get(RealmDefinition.TAG_NAME).set(realmNameFromOp(operation));
this.deployments.put(deploymentNameFromOp(operation), deployment);
}
@ -178,7 +178,7 @@ public final class KeycloakAdapterConfigService implements Service<KeycloakAdapt
json.get(RealmDefinition.TAG_NAME).set(realmName);
// Realm values set first. Some can be overridden by deployment values.
setJSONValues(json, realm);
if (realm != null) setJSONValues(json, realm);
setJSONValues(json, deployment);
return json.toJSONString(true);
}

View file

@ -19,11 +19,11 @@ package org.keycloak.subsystem.extension;
import org.jboss.as.controller.Extension;
import org.jboss.as.controller.ExtensionContext;
import org.jboss.as.controller.PathElement;
import org.jboss.as.controller.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.jboss.as.controller.ResourceDefinition;
import org.keycloak.subsystem.logging.KeycloakLogger;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SUBSYSTEM;
@ -77,7 +77,7 @@ public class KeycloakExtension implements Extension {
ManagementResourceRegistration registration = subsystem.registerSubsystemModel(KEYCLOAK_SUBSYSTEM_RESOURCE);
ManagementResourceRegistration realmRegistration = registration.registerSubModel(REALM_DEFINITION);
ManagementResourceRegistration secureDeploymentRegistration = realmRegistration.registerSubModel(SECURE_DEPLOYMENT_DEFINITION);
ManagementResourceRegistration secureDeploymentRegistration = registration.registerSubModel(SECURE_DEPLOYMENT_DEFINITION);
secureDeploymentRegistration.registerSubModel(CREDENTIAL_DEFINITION);
subsystem.registerXMLElementWriter(PARSER);

View file

@ -16,11 +16,6 @@
*/
package org.keycloak.subsystem.extension;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import org.jboss.as.controller.AttributeDefinition;
import org.jboss.as.controller.PathAddress;
import org.jboss.as.controller.PathElement;
@ -35,6 +30,12 @@ import org.jboss.staxmapper.XMLElementWriter;
import org.jboss.staxmapper.XMLExtendedStreamReader;
import org.jboss.staxmapper.XMLExtendedStreamWriter;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* The subsystem parser, which uses stax to read and write to and from xml
*/
@ -51,10 +52,12 @@ class KeycloakSubsystemParser implements XMLStreamConstants, XMLElementReader<Li
list.add(addKeycloakSub);
while (reader.hasNext() && nextTag(reader) != END_ELEMENT) {
if (!reader.getLocalName().equals("realm")) {
throw ParseUtils.unexpectedElement(reader);
if (reader.getLocalName().equals(RealmDefinition.TAG_NAME)) {
readRealm(reader, list);
}
else if (reader.getLocalName().equals(SecureDeploymentDefinition.TAG_NAME)) {
readDeployment(reader, list);
}
readRealm(reader, list);
}
}
@ -65,48 +68,33 @@ class KeycloakSubsystemParser implements XMLStreamConstants, XMLElementReader<Li
private void readRealm(XMLExtendedStreamReader reader, List<ModelNode> list) throws XMLStreamException {
String realmName = readNameAttribute(reader);
ModelNode composite = new ModelNode();
composite.get(ModelDescriptionConstants.OP_ADDR).setEmptyList();
composite.get(ModelDescriptionConstants.OP).set(ModelDescriptionConstants.COMPOSITE);
ModelNode addRealm = new ModelNode();
addRealm.get(ModelDescriptionConstants.OP).set(ModelDescriptionConstants.ADD);
PathAddress addr = PathAddress.pathAddress(PathElement.pathElement(ModelDescriptionConstants.SUBSYSTEM, KeycloakExtension.SUBSYSTEM_NAME),
PathElement.pathElement(RealmDefinition.TAG_NAME, realmName));
addRealm.get(ModelDescriptionConstants.OP_ADDR).set(addr.toModelNode());
List<ModelNode> resourcesToAdd = new ArrayList<ModelNode>();
while (reader.hasNext() && nextTag(reader) != END_ELEMENT) {
String tagName = reader.getLocalName();
if (tagName.equals(SecureDeploymentDefinition.TAG_NAME)) {
readDeployment(reader, addr, resourcesToAdd);
continue;
}
SimpleAttributeDefinition def = RealmDefinition.lookup(tagName);
if (def == null) throw new XMLStreamException("Unknown realm tag " + tagName);
def.parseAndSetParameter(reader.getElementText(), addRealm, reader);
}
if (!RealmDefinition.validateTruststoreSetIfRequired(addRealm)) {
if (!SharedAttributeDefinitons.validateTruststoreSetIfRequired(addRealm)) {
//TODO: externalize the message
throw new XMLStreamException("truststore and truststore-password must be set if both ssl-not-required and disable-trust-maanger are false.");
}
ModelNode steps = new ModelNode();
steps.add(addRealm);
for (ModelNode resource : resourcesToAdd) {
steps.add(resource);
}
composite.get(ModelDescriptionConstants.STEPS).set(steps);
list.add(composite);
list.add(addRealm);
}
private void readDeployment(XMLExtendedStreamReader reader, PathAddress parent, List<ModelNode> resourcesToAdd) throws XMLStreamException {
private void readDeployment(XMLExtendedStreamReader reader, List<ModelNode> resourcesToAdd) throws XMLStreamException {
String name = readNameAttribute(reader);
ModelNode addSecureDeployment = new ModelNode();
addSecureDeployment.get(ModelDescriptionConstants.OP).set(ModelDescriptionConstants.ADD);
PathAddress addr = PathAddress.pathAddress(parent, PathElement.pathElement(SecureDeploymentDefinition.TAG_NAME, name));
PathAddress addr = PathAddress.pathAddress(PathElement.pathElement(ModelDescriptionConstants.SUBSYSTEM, KeycloakExtension.SUBSYSTEM_NAME),
PathElement.pathElement(SecureDeploymentDefinition.TAG_NAME, name));
addSecureDeployment.get(ModelDescriptionConstants.OP_ADDR).set(addr.toModelNode());
List<ModelNode> credentialsToAdd = new ArrayList<ModelNode>();
while (reader.hasNext() && nextTag(reader) != END_ELEMENT) {
@ -120,6 +108,16 @@ class KeycloakSubsystemParser implements XMLStreamConstants, XMLElementReader<Li
if (def == null) throw new XMLStreamException("Unknown secure-deployment tag " + tagName);
def.parseAndSetParameter(reader.getElementText(), addSecureDeployment, reader);
}
/**
* TODO need to check realm-ref first.
if (!SharedAttributeDefinitons.validateTruststoreSetIfRequired(addSecureDeployment)) {
//TODO: externalize the message
throw new XMLStreamException("truststore and truststore-password must be set if both ssl-not-required and disable-trust-maanger are false.");
}
*/
// Must add credentials after the deployment is added.
resourcesToAdd.add(addSecureDeployment);
resourcesToAdd.addAll(credentialsToAdd);
@ -159,6 +157,7 @@ class KeycloakSubsystemParser implements XMLStreamConstants, XMLElementReader<Li
public void writeContent(final XMLExtendedStreamWriter writer, final SubsystemMarshallingContext context) throws XMLStreamException {
context.startSubsystemElement(KeycloakExtension.NAMESPACE, false);
writeRealms(writer, context);
writeSecureDeployments(writer, context);
writer.writeEndElement();
}
@ -174,17 +173,15 @@ class KeycloakSubsystemParser implements XMLStreamConstants, XMLElementReader<Li
element.marshallAsElement(realmElements, writer);
}
ModelNode deployments = realmElements.get(SecureDeploymentDefinition.TAG_NAME);
if (deployments.isDefined()) {
writeSecureDeployments(writer, deployments);
}
writer.writeEndElement();
}
}
private void writeSecureDeployments(XMLExtendedStreamWriter writer, ModelNode deployments) throws XMLStreamException {
for (Property deployment : deployments.asPropertyList()) {
private void writeSecureDeployments(XMLExtendedStreamWriter writer, SubsystemMarshallingContext context) throws XMLStreamException {
if (!context.getModelNode().get(SecureDeploymentDefinition.TAG_NAME).isDefined()) {
return;
}
for (Property deployment : context.getModelNode().get(SecureDeploymentDefinition.TAG_NAME).asPropertyList()) {
writer.writeStartElement(SecureDeploymentDefinition.TAG_NAME);
writer.writeAttribute("name", deployment.getName());
ModelNode deploymentElements = deployment.getValue();

View file

@ -17,17 +17,18 @@
package org.keycloak.subsystem.extension;
import java.util.List;
import org.jboss.as.controller.AbstractAddStepHandler;
import org.jboss.as.controller.AttributeDefinition;
import org.jboss.as.controller.OperationContext;
import org.jboss.as.controller.OperationFailedException;
import org.jboss.as.controller.ServiceVerificationHandler;
import org.jboss.dmr.ModelNode;
import org.jboss.msc.service.ServiceController;
import java.util.List;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ADD;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP;
import org.jboss.msc.service.ServiceController;
/**
* Add a new realm.
@ -51,7 +52,7 @@ public final class RealmAddHandler extends AbstractAddStepHandler {
attrib.validateAndSet(operation, model);
}
if (!RealmDefinition.validateTruststoreSetIfRequired(model.clone())) {
if (!SharedAttributeDefinitons.validateTruststoreSetIfRequired(model.clone())) {
//TODO: externalize message
throw new OperationFailedException("truststore and truststore-password must be set if both ssl-not-required and disable-trust-maanger are false.");
}

View file

@ -44,65 +44,9 @@ public class RealmDefinition extends SimpleResourceDefinition {
public static final String TAG_NAME = "realm";
protected static final SimpleAttributeDefinition REALM_PUBLIC_KEY =
new SimpleAttributeDefinitionBuilder("realm-public-key", ModelType.STRING, false)
.setXmlName("realm-public-key")
.setAllowExpression(true)
.setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, false, true))
.build();
protected static final SimpleAttributeDefinition AUTH_SERVER_URL =
new SimpleAttributeDefinitionBuilder("auth-server-url", ModelType.STRING, false)
.setXmlName("auth-server-url")
.setAllowExpression(true)
.setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, false, true))
.build();
protected static final SimpleAttributeDefinition SSL_NOT_REQUIRED =
new SimpleAttributeDefinitionBuilder("ssl-not-required", ModelType.BOOLEAN, true)
.setXmlName("ssl-not-required")
.setAllowExpression(true)
.setDefaultValue(new ModelNode(false))
.build();
protected static final SimpleAttributeDefinition ALLOW_ANY_HOSTNAME =
new SimpleAttributeDefinitionBuilder("allow-any-hostname", ModelType.BOOLEAN, true)
.setXmlName("allow-any-hostname")
.setAllowExpression(true)
.setDefaultValue(new ModelNode(false))
.build();
protected static final SimpleAttributeDefinition DISABLE_TRUST_MANAGER =
new SimpleAttributeDefinitionBuilder("disable-trust-manager", ModelType.BOOLEAN, true)
.setXmlName("disable-trust-manager")
.setAllowExpression(true)
.setDefaultValue(new ModelNode(false))
.build();
protected static final SimpleAttributeDefinition TRUSTSTORE =
new SimpleAttributeDefinitionBuilder("truststore", ModelType.STRING, true)
.setXmlName("truststore")
.setAllowExpression(true)
.setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, true, true))
.build();
protected static final SimpleAttributeDefinition TRUSTSTORE_PASSWORD =
new SimpleAttributeDefinitionBuilder("truststore-password", ModelType.STRING, true)
.setXmlName("truststore-password")
.setAllowExpression(true)
.setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, true, true))
.build();
protected static final SimpleAttributeDefinition CONNECTION_POOL_SIZE =
new SimpleAttributeDefinitionBuilder("connection-pool-size", ModelType.INT, true)
.setXmlName("connection-pool-size")
.setAllowExpression(true)
.setValidator(new IntRangeValidator(0, true))
.build();
protected static final List<SimpleAttributeDefinition> REALM_ONLY_ATTRIBUTES = new ArrayList<SimpleAttributeDefinition>();
static {
REALM_ONLY_ATTRIBUTES.add(REALM_PUBLIC_KEY);
REALM_ONLY_ATTRIBUTES.add(AUTH_SERVER_URL);
REALM_ONLY_ATTRIBUTES.add(TRUSTSTORE);
REALM_ONLY_ATTRIBUTES.add(TRUSTSTORE_PASSWORD);
REALM_ONLY_ATTRIBUTES.add(SSL_NOT_REQUIRED);
REALM_ONLY_ATTRIBUTES.add(ALLOW_ANY_HOSTNAME);
REALM_ONLY_ATTRIBUTES.add(DISABLE_TRUST_MANAGER);
REALM_ONLY_ATTRIBUTES.add(CONNECTION_POOL_SIZE);
}
protected static final List<SimpleAttributeDefinition> ALL_ATTRIBUTES = new ArrayList<SimpleAttributeDefinition>();
@ -144,32 +88,6 @@ public class RealmDefinition extends SimpleResourceDefinition {
}
}
/**
* truststore and truststore-password must be set if ssl-not-required and disable-trust-manager are both false.
*
* @param attributes The full set of attributes.
*
* @return <code>true</code> if the attributes are valid, <code>false</code> otherwise.
*/
public static boolean validateTruststoreSetIfRequired(ModelNode attributes) {
if (!isSet(attributes, SSL_NOT_REQUIRED) && !isSet(attributes, DISABLE_TRUST_MANAGER)) {
if (!(isSet(attributes, TRUSTSTORE) && isSet(attributes, TRUSTSTORE_PASSWORD))) {
return false;
}
}
return true;
}
private static boolean isSet(ModelNode attributes, SimpleAttributeDefinition def) {
ModelNode attribute = attributes.get(def.getName());
if (def.getType() == ModelType.BOOLEAN) {
return attribute.isDefined() && attribute.asBoolean();
}
return attribute.isDefined() && !attribute.asString().isEmpty();
}
public static SimpleAttributeDefinition lookup(String name) {
return DEFINITION_LOOKUP.get(name);

View file

@ -43,12 +43,18 @@ public class SecureDeploymentDefinition extends SimpleResourceDefinition {
public static final String TAG_NAME = "secure-deployment";
protected static final SimpleAttributeDefinition REALM =
new SimpleAttributeDefinitionBuilder("realm", ModelType.STRING, true)
.setXmlName("realm")
.setAllowExpression(true)
.setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, true, true))
.build();
protected static final SimpleAttributeDefinition RESOURCE =
new SimpleAttributeDefinitionBuilder("resource", ModelType.STRING, true)
.setXmlName("resource")
.setAllowExpression(true)
.setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, true, true))
.build();
.setXmlName("resource")
.setAllowExpression(true)
.setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, true, true))
.build();
protected static final SimpleAttributeDefinition USE_RESOURCE_ROLE_MAPPINGS =
new SimpleAttributeDefinitionBuilder("use-resource-role-mappings", ModelType.BOOLEAN, true)
.setXmlName("use-resource-role-mappings")
@ -64,6 +70,7 @@ public class SecureDeploymentDefinition extends SimpleResourceDefinition {
protected static final List<SimpleAttributeDefinition> DEPLOYMENT_ONLY_ATTRIBUTES = new ArrayList<SimpleAttributeDefinition>();
static {
DEPLOYMENT_ONLY_ATTRIBUTES.add(REALM);
DEPLOYMENT_ONLY_ATTRIBUTES.add(RESOURCE);
DEPLOYMENT_ONLY_ATTRIBUTES.add(USE_RESOURCE_ROLE_MAPPINGS);
DEPLOYMENT_ONLY_ATTRIBUTES.add(BEARER_ONLY);

View file

@ -16,8 +16,6 @@
*/
package org.keycloak.subsystem.extension;
import java.util.ArrayList;
import java.util.List;
import org.jboss.as.controller.SimpleAttributeDefinition;
import org.jboss.as.controller.SimpleAttributeDefinitionBuilder;
import org.jboss.as.controller.operations.validation.IntRangeValidator;
@ -25,6 +23,9 @@ import org.jboss.as.controller.operations.validation.StringLengthValidator;
import org.jboss.dmr.ModelNode;
import org.jboss.dmr.ModelType;
import java.util.ArrayList;
import java.util.List;
/**
* Defines attributes that can be present in both a realm and an application (secure-deployment).
*
@ -32,6 +33,55 @@ import org.jboss.dmr.ModelType;
*/
public class SharedAttributeDefinitons {
protected static final SimpleAttributeDefinition REALM_PUBLIC_KEY =
new SimpleAttributeDefinitionBuilder("realm-public-key", ModelType.STRING, true)
.setXmlName("realm-public-key")
.setAllowExpression(true)
.setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, true, true))
.build();
protected static final SimpleAttributeDefinition AUTH_SERVER_URL =
new SimpleAttributeDefinitionBuilder("auth-server-url", ModelType.STRING, true)
.setXmlName("auth-server-url")
.setAllowExpression(true)
.setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, true, true))
.build();
protected static final SimpleAttributeDefinition SSL_NOT_REQUIRED =
new SimpleAttributeDefinitionBuilder("ssl-not-required", ModelType.BOOLEAN, true)
.setXmlName("ssl-not-required")
.setAllowExpression(true)
.setDefaultValue(new ModelNode(false))
.build();
protected static final SimpleAttributeDefinition ALLOW_ANY_HOSTNAME =
new SimpleAttributeDefinitionBuilder("allow-any-hostname", ModelType.BOOLEAN, true)
.setXmlName("allow-any-hostname")
.setAllowExpression(true)
.setDefaultValue(new ModelNode(false))
.build();
protected static final SimpleAttributeDefinition DISABLE_TRUST_MANAGER =
new SimpleAttributeDefinitionBuilder("disable-trust-manager", ModelType.BOOLEAN, true)
.setXmlName("disable-trust-manager")
.setAllowExpression(true)
.setDefaultValue(new ModelNode(false))
.build();
protected static final SimpleAttributeDefinition TRUSTSTORE =
new SimpleAttributeDefinitionBuilder("truststore", ModelType.STRING, true)
.setXmlName("truststore")
.setAllowExpression(true)
.setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, true, true))
.build();
protected static final SimpleAttributeDefinition TRUSTSTORE_PASSWORD =
new SimpleAttributeDefinitionBuilder("truststore-password", ModelType.STRING, true)
.setXmlName("truststore-password")
.setAllowExpression(true)
.setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, true, true))
.build();
protected static final SimpleAttributeDefinition CONNECTION_POOL_SIZE =
new SimpleAttributeDefinitionBuilder("connection-pool-size", ModelType.INT, true)
.setXmlName("connection-pool-size")
.setAllowExpression(true)
.setValidator(new IntRangeValidator(0, true))
.build();
protected static final SimpleAttributeDefinition ENABLE_CORS =
new SimpleAttributeDefinitionBuilder("enable-cors", ModelType.BOOLEAN, true)
.setXmlName("enable-cors")
@ -84,6 +134,14 @@ public class SharedAttributeDefinitons {
protected static final List<SimpleAttributeDefinition> ATTRIBUTES = new ArrayList<SimpleAttributeDefinition>();
static {
ATTRIBUTES.add(REALM_PUBLIC_KEY);
ATTRIBUTES.add(AUTH_SERVER_URL);
ATTRIBUTES.add(TRUSTSTORE);
ATTRIBUTES.add(TRUSTSTORE_PASSWORD);
ATTRIBUTES.add(SSL_NOT_REQUIRED);
ATTRIBUTES.add(ALLOW_ANY_HOSTNAME);
ATTRIBUTES.add(DISABLE_TRUST_MANAGER);
ATTRIBUTES.add(CONNECTION_POOL_SIZE);
ATTRIBUTES.add(ENABLE_CORS);
ATTRIBUTES.add(CLIENT_KEYSTORE);
ATTRIBUTES.add(CLIENT_KEYSTORE_PASSWORD);
@ -94,4 +152,32 @@ public class SharedAttributeDefinitons {
ATTRIBUTES.add(EXPOSE_TOKEN);
}
/**
* truststore and truststore-password must be set if ssl-not-required and disable-trust-manager are both false.
*
* @param attributes The full set of attributes.
*
* @return <code>true</code> if the attributes are valid, <code>false</code> otherwise.
*/
public static boolean validateTruststoreSetIfRequired(ModelNode attributes) {
if (!isSet(attributes, SSL_NOT_REQUIRED) && !isSet(attributes, DISABLE_TRUST_MANAGER)) {
if (!(isSet(attributes, TRUSTSTORE) && isSet(attributes, TRUSTSTORE_PASSWORD))) {
return false;
}
}
return true;
}
private static boolean isSet(ModelNode attributes, SimpleAttributeDefinition def) {
ModelNode attribute = attributes.get(def.getName());
if (def.getType() == ModelType.BOOLEAN) {
return attribute.isDefined() && attribute.asBoolean();
}
return attribute.isDefined() && !attribute.asString().isEmpty();
}
}

View file

@ -2,6 +2,7 @@ keycloak.subsystem=Keycloak subsystem
keycloak.subsystem.add=Operation Adds Keycloak subsystem
keycloak.subsystem.remove=Operation removes Keycloak subsystem
keycloak.subsystem.realm=A Keycloak realm.
keycloak.subsystem.secure-deployment=A deployment secured by Keycloak.
keycloak.realm=A Keycloak realm.
keycloak.realm.add=Add a realm definition to the subsystem.
@ -23,11 +24,18 @@ keycloak.realm.cors-allowed-headers=TODO: fill in help text
keycloak.realm.cors-allowed-methods=TODO: fill in help text
keycloak.realm.expose-token=TODO: fill in help text
keycloak.realm.secure-deployment=A deployment secured by Keycloak
keycloak.secure-deployment=A deployment secured by Keycloak
keycloak.secure-deployment.add=Add a deployment to be secured by Keycloak
keycloak.secure-deployment.realm=Keycloak realm
keycloak.secure-deployment.remove=Remove a deployment to be secured by Keycloak
keycloak.secure-deployment.realm-public-key=TODO: fill in help text
keycloak.secure-deployment.auth-server-url=TODO: fill in help text
keycloak.secure-deployment.disable-trust-manager=TODO: fill in help text
keycloak.secure-deployment.ssl-not-required=TODO: fill in help text
keycloak.secure-deployment.allow-any-hostname=TODO: fill in help text
keycloak.secure-deployment.truststore=TODO: fill in help text
keycloak.secure-deployment.truststore-password=TODO: fill in help text
keycloak.secure-deployment.connection-pool-size=TODO: fill in help text
keycloak.secure-deployment.resource=TODO: fill in help text
keycloak.secure-deployment.use-resource-role-mappings=TODO: fill in help text
keycloak.secure-deployment.credentials=TODO: fill in help text

View file

@ -17,6 +17,7 @@
</xs:documentation>
</xs:annotation>
<xs:element name="realm" maxOccurs="unbounded" minOccurs="0" type="realm-type"/>
<xs:element name="realm" maxOccurs="unbounded" minOccurs="0" type="secure-deployment-type"/>
</xs:complexType>
@ -28,18 +29,6 @@
</xs:attribute>
<xs:complexContent>
<xs:extension base="override-element-type">
<xs:element name="realm-public-key" type="xs:string" minOccurs="1" maxOccurs="1" use="required"/>
<xs:element name="auth-url" type="xs:string" minOccurs="1" maxOccurs="1" use="required"/>
<xs:element name="code-url" type="xs:string" minOccurs="1" maxOccurs="1" use="required"/>
<xs:element name="ssl-not-required" type="xs:boolean" minOccurs="0" maxOccurs="1" />
<xs:element name="allow-any-hostname" type="xs:boolean" minOccurs="0" maxOccurs="1" />
<xs:element name="disable-trust-manager" type="xs:boolean" minOccurs="0" maxOccurs="1" />
<xs:element name="truststore" type="xs:string" minOccurs="0" maxOccurs="1"/>
<xs:element name="truststore-password" type="xs:string" minOccurs="0" maxOccurs="1"/>
<xs:element name="connection-pool-size" type="xs:integer" minOccurs="0" maxOccurs="1"/>
<xs:choice maxOccurs="unbounded" minOccurs="0">
<xs:element name="secure-deployment" maxOccurs="unbounded" minOccurs="0" type="secure-deployment-type"/>
</xs:choice>
</xs:extension>
</xs:complexContent>
</xs:complexType>
@ -52,6 +41,7 @@
</xs:attribute>
<xs:complexContent>
<xs:extension base="override-element-type">
<xs:element name="realm" type="xs:string" minOccurs="0" maxOccurs="1" use="required"/>
<xs:element name="resource" type="xs:string" minOccurs="0" maxOccurs="1" use="required"/>
<xs:element name="use-resource-role-mappings" type="xs:boolean" minOccurs="0" maxOccurs="1" />
<xs:element name="bearer-only" type="xs:boolean" minOccurs="0" maxOccurs="1" />
@ -76,6 +66,15 @@
]]>
</xs:documentation>
</xs:annotation>
<xs:element name="realm-public-key" type="xs:string" minOccurs="1" maxOccurs="1" use="required"/>
<xs:element name="auth-url" type="xs:string" minOccurs="1" maxOccurs="1" use="required"/>
<xs:element name="code-url" type="xs:string" minOccurs="1" maxOccurs="1" use="required"/>
<xs:element name="ssl-not-required" type="xs:boolean" minOccurs="0" maxOccurs="1" />
<xs:element name="allow-any-hostname" type="xs:boolean" minOccurs="0" maxOccurs="1" />
<xs:element name="disable-trust-manager" type="xs:boolean" minOccurs="0" maxOccurs="1" />
<xs:element name="truststore" type="xs:string" minOccurs="0" maxOccurs="1"/>
<xs:element name="truststore-password" type="xs:string" minOccurs="0" maxOccurs="1"/>
<xs:element name="connection-pool-size" type="xs:integer" minOccurs="0" maxOccurs="1"/>
<xs:element name="enable-cors" type="xs:boolean" minOccurs="0" maxOccurs="1"/>
<xs:element name="client-keystore" type="xs:string" minOccurs="0" maxOccurs="1"/>
<xs:element name="client-keystore-password" type="xs:string" minOccurs="0" maxOccurs="1"/>

View file

@ -23,7 +23,6 @@ import org.junit.Before;
import org.junit.Test;
/**
*
* @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc.
@ -50,30 +49,30 @@ public class RealmDefinitionTestCase {
public void testIsTruststoreSetIfRequired() throws Exception {
model.get("ssl-not-required").set(true);
model.get("disable-trust-manager").set(true);
Assert.assertTrue(RealmDefinition.validateTruststoreSetIfRequired(model));
Assert.assertTrue(SharedAttributeDefinitons.validateTruststoreSetIfRequired(model));
model.get("ssl-not-required").set(true);
model.get("disable-trust-manager").set(false);
Assert.assertTrue(RealmDefinition.validateTruststoreSetIfRequired(model));
Assert.assertTrue(SharedAttributeDefinitons.validateTruststoreSetIfRequired(model));
model.get("ssl-not-required").set(false);
model.get("disable-trust-manager").set(true);
Assert.assertTrue(RealmDefinition.validateTruststoreSetIfRequired(model));
Assert.assertTrue(SharedAttributeDefinitons.validateTruststoreSetIfRequired(model));
model.get("ssl-not-required").set(false);
model.get("disable-trust-manager").set(false);
Assert.assertFalse(RealmDefinition.validateTruststoreSetIfRequired(model));
Assert.assertFalse(SharedAttributeDefinitons.validateTruststoreSetIfRequired(model));
model.get("ssl-not-required").set(false);
model.get("disable-trust-manager").set(false);
model.get("truststore").set("foo");
Assert.assertFalse(RealmDefinition.validateTruststoreSetIfRequired(model));
Assert.assertFalse(SharedAttributeDefinitons.validateTruststoreSetIfRequired(model));
model.get("ssl-not-required").set(false);
model.get("disable-trust-manager").set(false);
model.get("truststore").set("foo");
model.get("truststore-password").set("password");
Assert.assertTrue(RealmDefinition.validateTruststoreSetIfRequired(model));
Assert.assertTrue(SharedAttributeDefinitons.validateTruststoreSetIfRequired(model));
}
}

View file

@ -16,21 +16,25 @@
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk16</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-core</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>${keycloak.apache.httpcomponents.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
@ -43,6 +47,7 @@
<dependency>
<groupId>net.iharder</groupId>
<artifactId>base64</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.spec.javax.servlet</groupId>

View file

@ -86,7 +86,6 @@ public final class KeycloakAdapterConfigService implements Service<KeycloakAdapt
public void addSecureDeployment(ModelNode operation, ModelNode model) {
ModelNode deployment = model.clone();
deployment.get(RealmDefinition.TAG_NAME).set(realmNameFromOp(operation));
this.deployments.put(deploymentNameFromOp(operation), deployment);
}
@ -178,7 +177,7 @@ public final class KeycloakAdapterConfigService implements Service<KeycloakAdapt
json.get(RealmDefinition.TAG_NAME).set(realmName);
// Realm values set first. Some can be overridden by deployment values.
setJSONValues(json, realm);
if (realm != null) setJSONValues(json, realm);
setJSONValues(json, deployment);
return json.toJSONString(true);
}

View file

@ -77,7 +77,7 @@ public class KeycloakExtension implements Extension {
ManagementResourceRegistration registration = subsystem.registerSubsystemModel(KEYCLOAK_SUBSYSTEM_RESOURCE);
ManagementResourceRegistration realmRegistration = registration.registerSubModel(REALM_DEFINITION);
ManagementResourceRegistration secureDeploymentRegistration = realmRegistration.registerSubModel(SECURE_DEPLOYMENT_DEFINITION);
ManagementResourceRegistration secureDeploymentRegistration = registration.registerSubModel(SECURE_DEPLOYMENT_DEFINITION);
secureDeploymentRegistration.registerSubModel(CREDENTIAL_DEFINITION);
subsystem.registerXMLElementWriter(PARSER);

View file

@ -52,10 +52,12 @@ class KeycloakSubsystemParser implements XMLStreamConstants, XMLElementReader<Li
list.add(addKeycloakSub);
while (reader.hasNext() && nextTag(reader) != END_ELEMENT) {
if (!reader.getLocalName().equals("realm")) {
throw ParseUtils.unexpectedElement(reader);
if (reader.getLocalName().equals(RealmDefinition.TAG_NAME)) {
readRealm(reader, list);
}
else if (reader.getLocalName().equals(SecureDeploymentDefinition.TAG_NAME)) {
readDeployment(reader, list);
}
readRealm(reader, list);
}
}
@ -66,48 +68,33 @@ class KeycloakSubsystemParser implements XMLStreamConstants, XMLElementReader<Li
private void readRealm(XMLExtendedStreamReader reader, List<ModelNode> list) throws XMLStreamException {
String realmName = readNameAttribute(reader);
ModelNode composite = new ModelNode();
composite.get(ModelDescriptionConstants.OP_ADDR).setEmptyList();
composite.get(ModelDescriptionConstants.OP).set(ModelDescriptionConstants.COMPOSITE);
ModelNode addRealm = new ModelNode();
addRealm.get(ModelDescriptionConstants.OP).set(ModelDescriptionConstants.ADD);
PathAddress addr = PathAddress.pathAddress(PathElement.pathElement(ModelDescriptionConstants.SUBSYSTEM, KeycloakExtension.SUBSYSTEM_NAME),
PathElement.pathElement(RealmDefinition.TAG_NAME, realmName));
addRealm.get(ModelDescriptionConstants.OP_ADDR).set(addr.toModelNode());
List<ModelNode> resourcesToAdd = new ArrayList<ModelNode>();
while (reader.hasNext() && nextTag(reader) != END_ELEMENT) {
String tagName = reader.getLocalName();
if (tagName.equals(SecureDeploymentDefinition.TAG_NAME)) {
readDeployment(reader, addr, resourcesToAdd);
continue;
}
SimpleAttributeDefinition def = RealmDefinition.lookup(tagName);
if (def == null) throw new XMLStreamException("Unknown realm tag " + tagName);
def.parseAndSetParameter(reader.getElementText(), addRealm, reader);
}
if (!RealmDefinition.validateTruststoreSetIfRequired(addRealm)) {
if (!SharedAttributeDefinitons.validateTruststoreSetIfRequired(addRealm)) {
//TODO: externalize the message
throw new XMLStreamException("truststore and truststore-password must be set if both ssl-not-required and disable-trust-maanger are false.");
}
ModelNode steps = new ModelNode();
steps.add(addRealm);
for (ModelNode resource : resourcesToAdd) {
steps.add(resource);
}
composite.get(ModelDescriptionConstants.STEPS).set(steps);
list.add(composite);
list.add(addRealm);
}
private void readDeployment(XMLExtendedStreamReader reader, PathAddress parent, List<ModelNode> resourcesToAdd) throws XMLStreamException {
private void readDeployment(XMLExtendedStreamReader reader, List<ModelNode> resourcesToAdd) throws XMLStreamException {
String name = readNameAttribute(reader);
ModelNode addSecureDeployment = new ModelNode();
addSecureDeployment.get(ModelDescriptionConstants.OP).set(ModelDescriptionConstants.ADD);
PathAddress addr = PathAddress.pathAddress(parent, PathElement.pathElement(SecureDeploymentDefinition.TAG_NAME, name));
PathAddress addr = PathAddress.pathAddress(PathElement.pathElement(ModelDescriptionConstants.SUBSYSTEM, KeycloakExtension.SUBSYSTEM_NAME),
PathElement.pathElement(SecureDeploymentDefinition.TAG_NAME, name));
addSecureDeployment.get(ModelDescriptionConstants.OP_ADDR).set(addr.toModelNode());
List<ModelNode> credentialsToAdd = new ArrayList<ModelNode>();
while (reader.hasNext() && nextTag(reader) != END_ELEMENT) {
@ -121,6 +108,16 @@ class KeycloakSubsystemParser implements XMLStreamConstants, XMLElementReader<Li
if (def == null) throw new XMLStreamException("Unknown secure-deployment tag " + tagName);
def.parseAndSetParameter(reader.getElementText(), addSecureDeployment, reader);
}
/**
* TODO need to check realm-ref first.
if (!SharedAttributeDefinitons.validateTruststoreSetIfRequired(addSecureDeployment)) {
//TODO: externalize the message
throw new XMLStreamException("truststore and truststore-password must be set if both ssl-not-required and disable-trust-maanger are false.");
}
*/
// Must add credentials after the deployment is added.
resourcesToAdd.add(addSecureDeployment);
resourcesToAdd.addAll(credentialsToAdd);
@ -160,6 +157,7 @@ class KeycloakSubsystemParser implements XMLStreamConstants, XMLElementReader<Li
public void writeContent(final XMLExtendedStreamWriter writer, final SubsystemMarshallingContext context) throws XMLStreamException {
context.startSubsystemElement(KeycloakExtension.NAMESPACE, false);
writeRealms(writer, context);
writeSecureDeployments(writer, context);
writer.writeEndElement();
}
@ -175,17 +173,15 @@ class KeycloakSubsystemParser implements XMLStreamConstants, XMLElementReader<Li
element.marshallAsElement(realmElements, writer);
}
ModelNode deployments = realmElements.get(SecureDeploymentDefinition.TAG_NAME);
if (deployments.isDefined()) {
writeSecureDeployments(writer, deployments);
}
writer.writeEndElement();
}
}
private void writeSecureDeployments(XMLExtendedStreamWriter writer, ModelNode deployments) throws XMLStreamException {
for (Property deployment : deployments.asPropertyList()) {
private void writeSecureDeployments(XMLExtendedStreamWriter writer, SubsystemMarshallingContext context) throws XMLStreamException {
if (!context.getModelNode().get(SecureDeploymentDefinition.TAG_NAME).isDefined()) {
return;
}
for (Property deployment : context.getModelNode().get(SecureDeploymentDefinition.TAG_NAME).asPropertyList()) {
writer.writeStartElement(SecureDeploymentDefinition.TAG_NAME);
writer.writeAttribute("name", deployment.getName());
ModelNode deploymentElements = deployment.getValue();

View file

@ -51,7 +51,7 @@ public final class RealmAddHandler extends AbstractAddStepHandler {
attrib.validateAndSet(operation, model);
}
if (!RealmDefinition.validateTruststoreSetIfRequired(model.clone())) {
if (!SharedAttributeDefinitons.validateTruststoreSetIfRequired(model.clone())) {
//TODO: externalize message
throw new OperationFailedException("truststore and truststore-password must be set if both ssl-not-required and disable-trust-maanger are false.");
}

View file

@ -41,65 +41,9 @@ public class RealmDefinition extends SimpleResourceDefinition {
public static final String TAG_NAME = "realm";
protected static final SimpleAttributeDefinition REALM_PUBLIC_KEY =
new SimpleAttributeDefinitionBuilder("realm-public-key", ModelType.STRING, false)
.setXmlName("realm-public-key")
.setAllowExpression(true)
.setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, false, true))
.build();
protected static final SimpleAttributeDefinition AUTH_SERVER_URL =
new SimpleAttributeDefinitionBuilder("auth-server-url", ModelType.STRING, false)
.setXmlName("auth-server-url")
.setAllowExpression(true)
.setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, false, true))
.build();
protected static final SimpleAttributeDefinition SSL_NOT_REQUIRED =
new SimpleAttributeDefinitionBuilder("ssl-not-required", ModelType.BOOLEAN, true)
.setXmlName("ssl-not-required")
.setAllowExpression(true)
.setDefaultValue(new ModelNode(false))
.build();
protected static final SimpleAttributeDefinition ALLOW_ANY_HOSTNAME =
new SimpleAttributeDefinitionBuilder("allow-any-hostname", ModelType.BOOLEAN, true)
.setXmlName("allow-any-hostname")
.setAllowExpression(true)
.setDefaultValue(new ModelNode(false))
.build();
protected static final SimpleAttributeDefinition DISABLE_TRUST_MANAGER =
new SimpleAttributeDefinitionBuilder("disable-trust-manager", ModelType.BOOLEAN, true)
.setXmlName("disable-trust-manager")
.setAllowExpression(true)
.setDefaultValue(new ModelNode(false))
.build();
protected static final SimpleAttributeDefinition TRUSTSTORE =
new SimpleAttributeDefinitionBuilder("truststore", ModelType.STRING, true)
.setXmlName("truststore")
.setAllowExpression(true)
.setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, true, true))
.build();
protected static final SimpleAttributeDefinition TRUSTSTORE_PASSWORD =
new SimpleAttributeDefinitionBuilder("truststore-password", ModelType.STRING, true)
.setXmlName("truststore-password")
.setAllowExpression(true)
.setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, true, true))
.build();
protected static final SimpleAttributeDefinition CONNECTION_POOL_SIZE =
new SimpleAttributeDefinitionBuilder("connection-pool-size", ModelType.INT, true)
.setXmlName("connection-pool-size")
.setAllowExpression(true)
.setValidator(new IntRangeValidator(0, true))
.build();
protected static final List<SimpleAttributeDefinition> REALM_ONLY_ATTRIBUTES = new ArrayList<SimpleAttributeDefinition>();
static {
REALM_ONLY_ATTRIBUTES.add(REALM_PUBLIC_KEY);
REALM_ONLY_ATTRIBUTES.add(AUTH_SERVER_URL);
REALM_ONLY_ATTRIBUTES.add(TRUSTSTORE);
REALM_ONLY_ATTRIBUTES.add(TRUSTSTORE_PASSWORD);
REALM_ONLY_ATTRIBUTES.add(SSL_NOT_REQUIRED);
REALM_ONLY_ATTRIBUTES.add(ALLOW_ANY_HOSTNAME);
REALM_ONLY_ATTRIBUTES.add(DISABLE_TRUST_MANAGER);
REALM_ONLY_ATTRIBUTES.add(CONNECTION_POOL_SIZE);
}
protected static final List<SimpleAttributeDefinition> ALL_ATTRIBUTES = new ArrayList<SimpleAttributeDefinition>();
@ -140,32 +84,6 @@ public class RealmDefinition extends SimpleResourceDefinition {
}
}
/**
* truststore and truststore-password must be set if ssl-not-required and disable-trust-manager are both false.
*
* @param attributes The full set of attributes.
*
* @return <code>true</code> if the attributes are valid, <code>false</code> otherwise.
*/
public static boolean validateTruststoreSetIfRequired(ModelNode attributes) {
if (!isSet(attributes, SSL_NOT_REQUIRED) && !isSet(attributes, DISABLE_TRUST_MANAGER)) {
if (!(isSet(attributes, TRUSTSTORE) && isSet(attributes, TRUSTSTORE_PASSWORD))) {
return false;
}
}
return true;
}
private static boolean isSet(ModelNode attributes, SimpleAttributeDefinition def) {
ModelNode attribute = attributes.get(def.getName());
if (def.getType() == ModelType.BOOLEAN) {
return attribute.isDefined() && attribute.asBoolean();
}
return attribute.isDefined() && !attribute.asString().isEmpty();
}
public static SimpleAttributeDefinition lookup(String name) {
return DEFINITION_LOOKUP.get(name);

View file

@ -40,12 +40,18 @@ public class SecureDeploymentDefinition extends SimpleResourceDefinition {
public static final String TAG_NAME = "secure-deployment";
protected static final SimpleAttributeDefinition REALM =
new SimpleAttributeDefinitionBuilder("realm", ModelType.STRING, true)
.setXmlName("realm")
.setAllowExpression(true)
.setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, true, true))
.build();
protected static final SimpleAttributeDefinition RESOURCE =
new SimpleAttributeDefinitionBuilder("resource", ModelType.STRING, true)
.setXmlName("resource")
.setAllowExpression(true)
.setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, true, true))
.build();
.setXmlName("resource")
.setAllowExpression(true)
.setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, true, true))
.build();
protected static final SimpleAttributeDefinition USE_RESOURCE_ROLE_MAPPINGS =
new SimpleAttributeDefinitionBuilder("use-resource-role-mappings", ModelType.BOOLEAN, true)
.setXmlName("use-resource-role-mappings")
@ -61,6 +67,7 @@ public class SecureDeploymentDefinition extends SimpleResourceDefinition {
protected static final List<SimpleAttributeDefinition> DEPLOYMENT_ONLY_ATTRIBUTES = new ArrayList<SimpleAttributeDefinition>();
static {
DEPLOYMENT_ONLY_ATTRIBUTES.add(REALM);
DEPLOYMENT_ONLY_ATTRIBUTES.add(RESOURCE);
DEPLOYMENT_ONLY_ATTRIBUTES.add(USE_RESOURCE_ROLE_MAPPINGS);
DEPLOYMENT_ONLY_ATTRIBUTES.add(BEARER_ONLY);

View file

@ -32,6 +32,55 @@ import org.jboss.dmr.ModelType;
*/
public class SharedAttributeDefinitons {
protected static final SimpleAttributeDefinition REALM_PUBLIC_KEY =
new SimpleAttributeDefinitionBuilder("realm-public-key", ModelType.STRING, true)
.setXmlName("realm-public-key")
.setAllowExpression(true)
.setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, true, true))
.build();
protected static final SimpleAttributeDefinition AUTH_SERVER_URL =
new SimpleAttributeDefinitionBuilder("auth-server-url", ModelType.STRING, true)
.setXmlName("auth-server-url")
.setAllowExpression(true)
.setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, true, true))
.build();
protected static final SimpleAttributeDefinition SSL_NOT_REQUIRED =
new SimpleAttributeDefinitionBuilder("ssl-not-required", ModelType.BOOLEAN, true)
.setXmlName("ssl-not-required")
.setAllowExpression(true)
.setDefaultValue(new ModelNode(false))
.build();
protected static final SimpleAttributeDefinition ALLOW_ANY_HOSTNAME =
new SimpleAttributeDefinitionBuilder("allow-any-hostname", ModelType.BOOLEAN, true)
.setXmlName("allow-any-hostname")
.setAllowExpression(true)
.setDefaultValue(new ModelNode(false))
.build();
protected static final SimpleAttributeDefinition DISABLE_TRUST_MANAGER =
new SimpleAttributeDefinitionBuilder("disable-trust-manager", ModelType.BOOLEAN, true)
.setXmlName("disable-trust-manager")
.setAllowExpression(true)
.setDefaultValue(new ModelNode(false))
.build();
protected static final SimpleAttributeDefinition TRUSTSTORE =
new SimpleAttributeDefinitionBuilder("truststore", ModelType.STRING, true)
.setXmlName("truststore")
.setAllowExpression(true)
.setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, true, true))
.build();
protected static final SimpleAttributeDefinition TRUSTSTORE_PASSWORD =
new SimpleAttributeDefinitionBuilder("truststore-password", ModelType.STRING, true)
.setXmlName("truststore-password")
.setAllowExpression(true)
.setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, true, true))
.build();
protected static final SimpleAttributeDefinition CONNECTION_POOL_SIZE =
new SimpleAttributeDefinitionBuilder("connection-pool-size", ModelType.INT, true)
.setXmlName("connection-pool-size")
.setAllowExpression(true)
.setValidator(new IntRangeValidator(0, true))
.build();
protected static final SimpleAttributeDefinition ENABLE_CORS =
new SimpleAttributeDefinitionBuilder("enable-cors", ModelType.BOOLEAN, true)
.setXmlName("enable-cors")
@ -84,6 +133,14 @@ public class SharedAttributeDefinitons {
protected static final List<SimpleAttributeDefinition> ATTRIBUTES = new ArrayList<SimpleAttributeDefinition>();
static {
ATTRIBUTES.add(REALM_PUBLIC_KEY);
ATTRIBUTES.add(AUTH_SERVER_URL);
ATTRIBUTES.add(TRUSTSTORE);
ATTRIBUTES.add(TRUSTSTORE_PASSWORD);
ATTRIBUTES.add(SSL_NOT_REQUIRED);
ATTRIBUTES.add(ALLOW_ANY_HOSTNAME);
ATTRIBUTES.add(DISABLE_TRUST_MANAGER);
ATTRIBUTES.add(CONNECTION_POOL_SIZE);
ATTRIBUTES.add(ENABLE_CORS);
ATTRIBUTES.add(CLIENT_KEYSTORE);
ATTRIBUTES.add(CLIENT_KEYSTORE_PASSWORD);
@ -94,4 +151,32 @@ public class SharedAttributeDefinitons {
ATTRIBUTES.add(EXPOSE_TOKEN);
}
/**
* truststore and truststore-password must be set if ssl-not-required and disable-trust-manager are both false.
*
* @param attributes The full set of attributes.
*
* @return <code>true</code> if the attributes are valid, <code>false</code> otherwise.
*/
public static boolean validateTruststoreSetIfRequired(ModelNode attributes) {
if (!isSet(attributes, SSL_NOT_REQUIRED) && !isSet(attributes, DISABLE_TRUST_MANAGER)) {
if (!(isSet(attributes, TRUSTSTORE) && isSet(attributes, TRUSTSTORE_PASSWORD))) {
return false;
}
}
return true;
}
private static boolean isSet(ModelNode attributes, SimpleAttributeDefinition def) {
ModelNode attribute = attributes.get(def.getName());
if (def.getType() == ModelType.BOOLEAN) {
return attribute.isDefined() && attribute.asBoolean();
}
return attribute.isDefined() && !attribute.asString().isEmpty();
}
}

View file

@ -2,6 +2,7 @@ keycloak.subsystem=Keycloak subsystem
keycloak.subsystem.add=Operation Adds Keycloak subsystem
keycloak.subsystem.remove=Operation removes Keycloak subsystem
keycloak.subsystem.realm=A Keycloak realm.
keycloak.subsystem.secure-deployment=A deployment secured by Keycloak.
keycloak.realm=A Keycloak realm.
keycloak.realm.add=Add a realm definition to the subsystem.
@ -23,11 +24,18 @@ keycloak.realm.cors-allowed-headers=TODO: fill in help text
keycloak.realm.cors-allowed-methods=TODO: fill in help text
keycloak.realm.expose-token=TODO: fill in help text
keycloak.realm.secure-deployment=A deployment secured by Keycloak
keycloak.secure-deployment=A deployment secured by Keycloak
keycloak.secure-deployment.add=Add a deployment to be secured by Keycloak
keycloak.secure-deployment.realm=Keycloak realm
keycloak.secure-deployment.remove=Remove a deployment to be secured by Keycloak
keycloak.secure-deployment.realm-public-key=TODO: fill in help text
keycloak.secure-deployment.auth-server-url=TODO: fill in help text
keycloak.secure-deployment.disable-trust-manager=TODO: fill in help text
keycloak.secure-deployment.ssl-not-required=TODO: fill in help text
keycloak.secure-deployment.allow-any-hostname=TODO: fill in help text
keycloak.secure-deployment.truststore=TODO: fill in help text
keycloak.secure-deployment.truststore-password=TODO: fill in help text
keycloak.secure-deployment.connection-pool-size=TODO: fill in help text
keycloak.secure-deployment.resource=TODO: fill in help text
keycloak.secure-deployment.use-resource-role-mappings=TODO: fill in help text
keycloak.secure-deployment.credentials=TODO: fill in help text

View file

@ -17,6 +17,7 @@
</xs:documentation>
</xs:annotation>
<xs:element name="realm" maxOccurs="unbounded" minOccurs="0" type="realm-type"/>
<xs:element name="realm" maxOccurs="unbounded" minOccurs="0" type="secure-deployment-type"/>
</xs:complexType>
@ -28,18 +29,6 @@
</xs:attribute>
<xs:complexContent>
<xs:extension base="override-element-type">
<xs:element name="realm-public-key" type="xs:string" minOccurs="1" maxOccurs="1" use="required"/>
<xs:element name="auth-url" type="xs:string" minOccurs="1" maxOccurs="1" use="required"/>
<xs:element name="code-url" type="xs:string" minOccurs="1" maxOccurs="1" use="required"/>
<xs:element name="ssl-not-required" type="xs:boolean" minOccurs="0" maxOccurs="1" />
<xs:element name="allow-any-hostname" type="xs:boolean" minOccurs="0" maxOccurs="1" />
<xs:element name="disable-trust-manager" type="xs:boolean" minOccurs="0" maxOccurs="1" />
<xs:element name="truststore" type="xs:string" minOccurs="0" maxOccurs="1"/>
<xs:element name="truststore-password" type="xs:string" minOccurs="0" maxOccurs="1"/>
<xs:element name="connection-pool-size" type="xs:integer" minOccurs="0" maxOccurs="1"/>
<xs:choice maxOccurs="unbounded" minOccurs="0">
<xs:element name="secure-deployment" maxOccurs="unbounded" minOccurs="0" type="secure-deployment-type"/>
</xs:choice>
</xs:extension>
</xs:complexContent>
</xs:complexType>
@ -52,6 +41,7 @@
</xs:attribute>
<xs:complexContent>
<xs:extension base="override-element-type">
<xs:element name="realm" type="xs:string" minOccurs="0" maxOccurs="1" use="required"/>
<xs:element name="resource" type="xs:string" minOccurs="0" maxOccurs="1" use="required"/>
<xs:element name="use-resource-role-mappings" type="xs:boolean" minOccurs="0" maxOccurs="1" />
<xs:element name="bearer-only" type="xs:boolean" minOccurs="0" maxOccurs="1" />
@ -76,6 +66,15 @@
]]>
</xs:documentation>
</xs:annotation>
<xs:element name="realm-public-key" type="xs:string" minOccurs="1" maxOccurs="1" use="required"/>
<xs:element name="auth-url" type="xs:string" minOccurs="1" maxOccurs="1" use="required"/>
<xs:element name="code-url" type="xs:string" minOccurs="1" maxOccurs="1" use="required"/>
<xs:element name="ssl-not-required" type="xs:boolean" minOccurs="0" maxOccurs="1" />
<xs:element name="allow-any-hostname" type="xs:boolean" minOccurs="0" maxOccurs="1" />
<xs:element name="disable-trust-manager" type="xs:boolean" minOccurs="0" maxOccurs="1" />
<xs:element name="truststore" type="xs:string" minOccurs="0" maxOccurs="1"/>
<xs:element name="truststore-password" type="xs:string" minOccurs="0" maxOccurs="1"/>
<xs:element name="connection-pool-size" type="xs:integer" minOccurs="0" maxOccurs="1"/>
<xs:element name="enable-cors" type="xs:boolean" minOccurs="0" maxOccurs="1"/>
<xs:element name="client-keystore" type="xs:string" minOccurs="0" maxOccurs="1"/>
<xs:element name="client-keystore-password" type="xs:string" minOccurs="0" maxOccurs="1"/>

View file

@ -50,30 +50,30 @@ public class RealmDefinitionTestCase {
public void testIsTruststoreSetIfRequired() throws Exception {
model.get("ssl-not-required").set(true);
model.get("disable-trust-manager").set(true);
Assert.assertTrue(RealmDefinition.validateTruststoreSetIfRequired(model));
Assert.assertTrue(SharedAttributeDefinitons.validateTruststoreSetIfRequired(model));
model.get("ssl-not-required").set(true);
model.get("disable-trust-manager").set(false);
Assert.assertTrue(RealmDefinition.validateTruststoreSetIfRequired(model));
Assert.assertTrue(SharedAttributeDefinitons.validateTruststoreSetIfRequired(model));
model.get("ssl-not-required").set(false);
model.get("disable-trust-manager").set(true);
Assert.assertTrue(RealmDefinition.validateTruststoreSetIfRequired(model));
Assert.assertTrue(SharedAttributeDefinitons.validateTruststoreSetIfRequired(model));
model.get("ssl-not-required").set(false);
model.get("disable-trust-manager").set(false);
Assert.assertFalse(RealmDefinition.validateTruststoreSetIfRequired(model));
Assert.assertFalse(SharedAttributeDefinitons.validateTruststoreSetIfRequired(model));
model.get("ssl-not-required").set(false);
model.get("disable-trust-manager").set(false);
model.get("truststore").set("foo");
Assert.assertFalse(RealmDefinition.validateTruststoreSetIfRequired(model));
Assert.assertFalse(SharedAttributeDefinitons.validateTruststoreSetIfRequired(model));
model.get("ssl-not-required").set(false);
model.get("disable-trust-manager").set(false);
model.get("truststore").set("foo");
model.get("truststore-password").set("password");
Assert.assertTrue(RealmDefinition.validateTruststoreSetIfRequired(model));
Assert.assertTrue(SharedAttributeDefinitons.validateTruststoreSetIfRequired(model));
}
}

View file

@ -14,7 +14,7 @@
<properties>
<keycloak.apache.httpcomponents.version>4.1.2</keycloak.apache.httpcomponents.version>
<resteasy.version>3.0.6.Final</resteasy.version>
<undertow.version>1.0.0.Beta30</undertow.version>
<undertow.version>1.0.0.Final</undertow.version>
<picketlink.version>2.5.0.Beta6</picketlink.version>
<mongo.driver.version>2.11.3</mongo.driver.version>
<jboss.logging.version>3.1.1.GA</jboss.logging.version>
@ -26,7 +26,7 @@
<mysql.version>5.1.25</mysql.version>
<slf4j.version>1.6.1</slf4j.version>
<jboss.version>7.1.1.Final</jboss.version>
<wildfly.version>8.0.0.CR1</wildfly.version>
<wildfly.version>8.0.0.Final</wildfly.version>
<json.version>20131018</json.version>
</properties>

View file

@ -112,7 +112,7 @@ public class AccountTest {
});
}
@Test
//@Test
public void returnToAppFromHeader() {
appPage.open();
appPage.openAccount();
@ -124,7 +124,7 @@ public class AccountTest {
Assert.assertTrue(appPage.isCurrent());
}
@Test
//@Test
public void returnToAppFromQueryParam() {
driver.navigate().to(AccountUpdateProfilePage.PATH + "?referrer=test-app");
loginPage.login("test-user@localhost", "password");