keycloak-aplcache

saml basic

10/4/2014 10:27:48 PM

Details

diff --git a/dependencies/server-all/pom.xml b/dependencies/server-all/pom.xml
index 0f7fed8..a812b08 100755
--- a/dependencies/server-all/pom.xml
+++ b/dependencies/server-all/pom.xml
@@ -138,6 +138,17 @@
             <version>${project.version}</version>
         </dependency>
 
+        <!-- saml -->
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-saml-protocol</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.picketlink</groupId>
+            <artifactId>picketlink-federation</artifactId>
+        </dependency>
+
         <!-- mongo -->
         <dependency>
             <groupId>org.keycloak</groupId>

pom.xml 8(+7 -1)

diff --git a/pom.xml b/pom.xml
index 55f5369..73523c0 100755
--- a/pom.xml
+++ b/pom.xml
@@ -18,7 +18,7 @@
         <resteasy.version>2.3.7.Final</resteasy.version>
         <resteasy.version.latest>3.0.9.Final</resteasy.version.latest>
         <undertow.version>1.0.15.Final</undertow.version>
-        <picketlink.version>2.7.0.Beta1</picketlink.version>
+        <picketlink.version>2.7.0.CR1-20140924</picketlink.version>
         <picketbox.ldap.version>1.0.2.Final</picketbox.ldap.version>
         <mongo.driver.version>2.11.3</mongo.driver.version>
         <jboss.logging.version>3.1.4.GA</jboss.logging.version>
@@ -107,6 +107,7 @@
         <module>picketlink</module>
         <module>federation</module>
         <module>services</module>
+        <module>saml</module>
         <module>social</module>
         <module>forms</module>
         <module>examples</module>
@@ -231,6 +232,11 @@
             </dependency>
             <dependency>
                 <groupId>org.picketlink</groupId>
+                <artifactId>picketlink-federation</artifactId>
+                <version>${picketlink.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.picketlink</groupId>
                 <artifactId>picketlink-idm-simple-schema</artifactId>
                 <version>${picketlink.version}</version>
             </dependency>

saml/pom.xml 20(+20 -0)

diff --git a/saml/pom.xml b/saml/pom.xml
new file mode 100755
index 0000000..4e7a81f
--- /dev/null
+++ b/saml/pom.xml
@@ -0,0 +1,20 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <parent>
+        <artifactId>keycloak-parent</artifactId>
+        <groupId>org.keycloak</groupId>
+        <version>1.1.0-Alpha1-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+    <name>Keycloak SAML Integration</name>
+    <description/>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>keycloak-saml-pom</artifactId>
+    <packaging>pom</packaging>
+
+    <modules>
+        <module>saml-core</module>
+        <module>saml-protocol</module>
+    </modules>
+</project>
diff --git a/saml/saml-core/pom.xml b/saml/saml-core/pom.xml
new file mode 100755
index 0000000..87b0250
--- /dev/null
+++ b/saml/saml-core/pom.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <parent>
+        <artifactId>keycloak-parent</artifactId>
+        <groupId>org.keycloak</groupId>
+        <version>1.1.0-Alpha1-SNAPSHOT</version>
+        <relativePath>../../pom.xml</relativePath>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>keycloak-saml-core</artifactId>
+    <name>Keycloak SAML Core</name>
+    <description/>
+
+    <properties>
+        <timestamp>${maven.build.timestamp}</timestamp>
+        <maven.build.timestamp.format>yyyy-MM-dd HH:mm</maven.build.timestamp.format>
+    </properties>
+    <dependencies>
+        <dependency>
+            <groupId>org.picketlink</groupId>
+            <artifactId>picketlink-federation</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.jboss.resteasy</groupId>
+            <artifactId>jaxrs-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+    <build>
+        <resources>
+            <resource>
+                <directory>src/main/resources</directory>
+                <filtering>true</filtering>
+            </resource>
+        </resources>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <source>${maven.compiler.source}</source>
+                    <target>${maven.compiler.target}</target>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/saml/saml-protocol/pom.xml b/saml/saml-protocol/pom.xml
new file mode 100755
index 0000000..ea64438
--- /dev/null
+++ b/saml/saml-protocol/pom.xml
@@ -0,0 +1,128 @@
+<?xml version="1.0"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <parent>
+        <artifactId>keycloak-parent</artifactId>
+        <groupId>org.keycloak</groupId>
+        <version>1.1.0-Alpha1-SNAPSHOT</version>
+        <relativePath>../../pom.xml</relativePath>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>keycloak-saml-protocol</artifactId>
+    <name>Keycloak SAML Protocol</name>
+    <description/>
+
+    <properties>
+        <timestamp>${maven.build.timestamp}</timestamp>
+        <maven.build.timestamp.format>yyyy-MM-dd HH:mm</maven.build.timestamp.format>
+    </properties>
+    <dependencies>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-core</artifactId>
+            <version>${project.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-services</artifactId>
+            <version>${project.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-forms-common-freemarker</artifactId>
+            <version>${project.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-events-api</artifactId>
+            <version>${project.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-account-api</artifactId>
+            <version>${project.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-email-api</artifactId>
+            <version>${project.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-login-api</artifactId>
+            <version>${project.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-model-api</artifactId>
+            <version>${project.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.jboss.logging</groupId>
+            <artifactId>jboss-logging</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.jboss.resteasy</groupId>
+            <artifactId>resteasy-jaxrs</artifactId>
+            <scope>provided</scope>
+            <exclusions>
+                <exclusion>
+                    <groupId>log4j</groupId>
+                    <artifactId>log4j</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.slf4j</groupId>
+                    <artifactId>slf4j-api</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.slf4j</groupId>
+                    <artifactId>slf4j-simple</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.picketlink</groupId>
+            <artifactId>picketlink-federation</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.jboss.resteasy</groupId>
+            <artifactId>jaxrs-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+    <build>
+        <resources>
+            <resource>
+                <directory>src/main/resources</directory>
+                <filtering>true</filtering>
+            </resource>
+        </resources>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <source>${maven.compiler.source}</source>
+                    <target>${maven.compiler.target}</target>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SAML2PostBindingResponseBuilder.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SAML2PostBindingResponseBuilder.java
new file mode 100755
index 0000000..0d7d4f5
--- /dev/null
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SAML2PostBindingResponseBuilder.java
@@ -0,0 +1,320 @@
+package org.keycloak.protocol.saml;
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+import org.picketlink.common.PicketLinkLogger;
+import org.picketlink.common.PicketLinkLoggerFactory;
+import org.picketlink.common.constants.GeneralConstants;
+import org.picketlink.common.constants.JBossSAMLURIConstants;
+import org.picketlink.common.exceptions.ConfigurationException;
+import org.picketlink.common.exceptions.ProcessingException;
+import org.picketlink.common.util.DocumentUtil;
+import org.picketlink.identity.federation.api.saml.v2.response.SAML2Response;
+import org.picketlink.identity.federation.api.saml.v2.sig.SAML2Signature;
+import org.picketlink.identity.federation.core.saml.v2.common.IDGenerator;
+import org.picketlink.identity.federation.core.saml.v2.factories.JBossSAMLAuthnResponseFactory;
+import org.picketlink.identity.federation.core.saml.v2.holders.IDPInfoHolder;
+import org.picketlink.identity.federation.core.saml.v2.holders.IssuerInfoHolder;
+import org.picketlink.identity.federation.core.saml.v2.holders.SPInfoHolder;
+import org.picketlink.identity.federation.core.saml.v2.util.StatementUtil;
+import org.picketlink.identity.federation.core.saml.v2.util.XMLTimeUtil;
+import org.picketlink.identity.federation.saml.v2.assertion.AssertionType;
+import org.picketlink.identity.federation.saml.v2.assertion.AttributeStatementType;
+import org.picketlink.identity.federation.saml.v2.assertion.AuthnStatementType;
+import org.picketlink.identity.federation.saml.v2.protocol.ResponseType;
+import org.picketlink.identity.federation.web.util.PostBindingUtil;
+import org.w3c.dom.Document;
+
+import javax.ws.rs.core.CacheControl;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import static org.picketlink.common.util.StringUtil.isNotNull;
+
+/**
+ * <p> Handles for dealing with SAML2 Authentication </p>
+ * <p/>
+ * Configuration Options:
+ *
+ * @author Anil.Saldhana@redhat.com
+*/
+public class SAML2PostBindingResponseBuilder {
+    protected static final PicketLinkLogger logger = PicketLinkLoggerFactory.getLogger();
+
+    protected List<String> roles = new LinkedList<String>();
+    protected String userPrincipal;
+    protected boolean multiValuedRoles;
+    protected boolean disableAuthnStatement;
+    protected String requestID;
+    protected String responseIssuer;
+    protected String authMethod;
+    protected String relayState;
+    protected String destination;
+    protected String requestIssuer;
+    protected Map<String, Object> attributes = new HashMap<String, Object>();
+
+
+    public SAML2PostBindingResponseBuilder attributes(Map<String, Object> attributes) {
+        this.attributes = attributes;
+        return this;
+    }
+
+    public SAML2PostBindingResponseBuilder attribute(String name, Object value) {
+        this.attributes.put(name, value);
+        return this;
+    }
+
+    public SAML2PostBindingResponseBuilder requestID(String requestID) {
+        this.requestID =requestID;
+        return this;
+    }
+
+    public SAML2PostBindingResponseBuilder requestIssuer(String requestIssuer) {
+        this.requestIssuer =requestIssuer;
+        return this;
+    }
+
+    public SAML2PostBindingResponseBuilder responseIssuer(String issuer) {
+        this.responseIssuer = issuer;
+        return this;
+    }
+
+    public SAML2PostBindingResponseBuilder roles(List<String> roles) {
+        this.roles = roles;
+        return this;
+    }
+
+    public SAML2PostBindingResponseBuilder roles(String... roles) {
+        for (String role : roles) {
+            this.roles.add(role);
+        }
+        return this;
+    }
+
+    public SAML2PostBindingResponseBuilder authMethod(String authMethod) {
+        this.authMethod = authMethod;
+        return this;
+    }
+
+    public SAML2PostBindingResponseBuilder userPrincipal(String userPrincipal) {
+        this.userPrincipal = userPrincipal;
+        return this;
+    }
+
+    public SAML2PostBindingResponseBuilder relayState(String relayState) {
+        this.relayState = relayState;
+        return this;
+    }
+
+    public SAML2PostBindingResponseBuilder destination(String destination) {
+        this.destination = destination;
+        return this;
+    }
+
+    public SAML2PostBindingResponseBuilder multiValuedRoles(boolean multiValuedRoles) {
+        this.multiValuedRoles = multiValuedRoles;
+        return this;
+    }
+
+    public SAML2PostBindingResponseBuilder disableAuthnStatement(boolean disableAuthnStatement) {
+        this.disableAuthnStatement = disableAuthnStatement;
+        return this;
+    }
+
+    public Response error(String status)  throws ConfigurationException, ProcessingException, IOException {
+        Document doc = getErrorResponse(status);
+        return buildResponse(doc);
+
+
+    }
+
+    public Document getErrorResponse(String status) {
+        Document samlResponse = null;
+        ResponseType responseType = null;
+
+        SAML2Response saml2Response = new SAML2Response();
+
+        // Create a response type
+        String id = IDGenerator.create("ID_");
+
+        IssuerInfoHolder issuerHolder = new IssuerInfoHolder(responseIssuer);
+        issuerHolder.setStatusCode(status);
+
+        IDPInfoHolder idp = new IDPInfoHolder();
+        idp.setNameIDFormatValue(null);
+        idp.setNameIDFormat(JBossSAMLURIConstants.NAMEID_FORMAT_PERSISTENT.get());
+
+        SPInfoHolder sp = new SPInfoHolder();
+        sp.setResponseDestinationURI(destination);
+
+        responseType = saml2Response.createResponseType(id);
+        responseType.setStatus(JBossSAMLAuthnResponseFactory.createStatusTypeForResponder(status));
+        responseType.setDestination(destination);
+
+        // Lets see how the response looks like
+        if (logger.isTraceEnabled()) {
+            StringWriter sw = new StringWriter();
+            try {
+                saml2Response.marshall(responseType, sw);
+            } catch (ProcessingException e) {
+                logger.trace(e);
+            }
+            logger.trace("SAML Response Document: " + sw.toString());
+        }
+
+        /*
+        if (supportSignature) {
+            try {
+                SAML2Signature ss = new SAML2Signature();
+                samlResponse = ss.sign(responseType, keyManager.getSigningKeyPair());
+            } catch (Exception e) {
+                logger.trace(e);
+                throw new RuntimeException(logger.signatureError(e));
+            }
+        } else
+            try {
+                samlResponse = saml2Response.convert(responseType);
+            } catch (Exception e) {
+                logger.trace(e);
+            }
+            */
+
+        return samlResponse;
+    }
+
+    public Response build() throws ConfigurationException, ProcessingException, IOException {
+        Document responseDoc = getResponse();
+        return buildResponse(responseDoc);
+    }
+
+    protected Response buildResponse(Document responseDoc) throws ProcessingException, ConfigurationException, IOException {
+        byte[] responseBytes = DocumentUtil.getDocumentAsString(responseDoc).getBytes("UTF-8");
+        String samlResponse = PostBindingUtil.base64Encode(new String(responseBytes));
+
+        if (destination == null) {
+            throw logger.nullValueError("Destination is null");
+        }
+
+        StringBuilder builder = new StringBuilder();
+
+        String key = GeneralConstants.SAML_RESPONSE_KEY;
+        builder.append("<HTML>");
+        builder.append("<HEAD>");
+
+        builder.append("<TITLE>HTTP Post Binding Response (Response)</TITLE>");
+        builder.append("</HEAD>");
+        builder.append("<BODY Onload=\"document.forms[0].submit()\">");
+
+        builder.append("<FORM METHOD=\"POST\" ACTION=\"" + destination + "\">");
+        builder.append("<INPUT TYPE=\"HIDDEN\" NAME=\"" + key + "\"" + " VALUE=\"" + samlResponse + "\"/>");
+
+        if (isNotNull(relayState)) {
+            builder.append("<INPUT TYPE=\"HIDDEN\" NAME=\"RelayState\" " + "VALUE=\"" + relayState + "\"/>");
+        }
+
+        builder.append("<NOSCRIPT>");
+        builder.append("<P>JavaScript is disabled. We strongly recommend to enable it. Click the button below to continue.</P>");
+        builder.append("<INPUT TYPE=\"SUBMIT\" VALUE=\"CONTINUE\" />");
+        builder.append("</NOSCRIPT>");
+
+        builder.append("</FORM></BODY></HTML>");
+
+        String str = builder.toString();
+
+        CacheControl cacheControl = new CacheControl();
+        cacheControl.setNoCache(true);
+        return Response.ok(str, MediaType.TEXT_HTML_TYPE)
+                       .header("Pragma", "no-cache")
+                       .header("Cache-Control", "no-cache, no-store").build();
+    }
+
+    public Document getResponse() throws ConfigurationException, ProcessingException {
+
+        Document samlResponseDocument = null;
+
+        ResponseType responseType = null;
+
+        SAML2Response saml2Response = new SAML2Response();
+
+        // Create a response type
+        String id = IDGenerator.create("ID_");
+
+        IssuerInfoHolder issuerHolder = new IssuerInfoHolder(responseIssuer);
+        issuerHolder.setStatusCode(JBossSAMLURIConstants.STATUS_SUCCESS.get());
+
+        IDPInfoHolder idp = new IDPInfoHolder();
+        idp.setNameIDFormatValue(userPrincipal);
+        idp.setNameIDFormat(JBossSAMLURIConstants.NAMEID_FORMAT_PERSISTENT.get());
+
+        SPInfoHolder sp = new SPInfoHolder();
+        sp.setResponseDestinationURI(destination);
+        sp.setRequestID(requestID);
+        sp.setIssuer(requestIssuer);
+        responseType = saml2Response.createResponseType(id, sp, idp, issuerHolder);
+
+        // Add information on the roles
+        AssertionType assertion = responseType.getAssertions().get(0).getAssertion();
+
+        // Create an AuthnStatementType
+        if (!disableAuthnStatement) {
+            String authContextRef = JBossSAMLURIConstants.AC_PASSWORD.get();
+            if (isNotNull(authMethod))
+                authContextRef = authMethod;
+
+            AuthnStatementType authnStatement = StatementUtil.createAuthnStatement(XMLTimeUtil.getIssueInstant(),
+                    authContextRef);
+
+            authnStatement.setSessionIndex(assertion.getID());
+
+            assertion.addStatement(authnStatement);
+        }
+
+        if (roles != null && !roles.isEmpty()) {
+            AttributeStatementType attrStatement = StatementUtil.createAttributeStatementForRoles(roles, multiValuedRoles);
+            assertion.addStatement(attrStatement);
+        }
+
+        // Add in the attributes information
+        if (attributes != null && attributes.size() > 0) {
+            AttributeStatementType attStatement = StatementUtil.createAttributeStatement(attributes);
+            assertion.addStatement(attStatement);
+        }
+
+        try {
+            samlResponseDocument = saml2Response.convert(responseType);
+
+            if (logger.isTraceEnabled()) {
+                logger.trace("SAML Response Document: " + DocumentUtil.asString(samlResponseDocument));
+            }
+        } catch (Exception e) {
+            throw logger.samlAssertionMarshallError(e);
+        }
+
+        return samlResponseDocument;
+    }
+}
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlLogin.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlLogin.java
new file mode 100755
index 0000000..8344170
--- /dev/null
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlLogin.java
@@ -0,0 +1,170 @@
+package org.keycloak.protocol.saml;
+
+import org.jboss.logging.Logger;
+import org.jboss.resteasy.spi.HttpRequest;
+import org.keycloak.ClientConnection;
+import org.keycloak.models.ClaimMask;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.ClientSessionModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.UserSessionModel;
+import org.keycloak.protocol.LoginProtocol;
+import org.keycloak.services.managers.ClientSessionCode;
+import org.keycloak.services.resources.RealmsResource;
+import org.keycloak.services.resources.flows.Flows;
+import org.picketlink.common.constants.GeneralConstants;
+import org.picketlink.common.constants.JBossSAMLURIConstants;
+import org.picketlink.common.exceptions.ConfigurationException;
+import org.picketlink.common.exceptions.ProcessingException;
+import org.picketlink.identity.federation.core.saml.v2.constants.X500SAMLProfileConstants;
+
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+import java.io.IOException;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class SamlLogin implements LoginProtocol {
+    protected static final Logger logger = Logger.getLogger(SamlLogin.class);
+    public static final String LOGIN_PROTOCOL = "saml";
+    public static final String SAML_BINDING = "saml_binding";
+    public static final String SAML_POST_BINDING = "post";
+
+    protected KeycloakSession session;
+
+    protected RealmModel realm;
+
+    protected HttpRequest request;
+
+    protected UriInfo uriInfo;
+
+    protected ClientConnection clientConnection;
+
+
+    @Override
+    public SamlLogin setSession(KeycloakSession session) {
+        this.session = session;
+        return this;
+    }
+
+    @Override
+    public SamlLogin setRealm(RealmModel realm) {
+        this.realm = realm;
+        return this;
+    }
+
+    @Override
+    public SamlLogin setRequest(HttpRequest request) {
+        this.request = request;
+        return this;
+    }
+
+    @Override
+    public SamlLogin setUriInfo(UriInfo uriInfo) {
+        this.uriInfo = uriInfo;
+        return this;
+    }
+
+    @Override
+    public SamlLogin setClientConnection(ClientConnection clientConnection) {
+        this.clientConnection = clientConnection;
+        return this;
+    }
+
+    @Override
+    public Response cancelLogin(ClientSessionModel clientSession) {
+        return getErrorResponse(clientSession, JBossSAMLURIConstants.STATUS_REQUEST_DENIED.get());
+    }
+
+    @Override
+    public Response invalidSessionError(ClientSessionModel clientSession) {
+        return getErrorResponse(clientSession, JBossSAMLURIConstants.STATUS_AUTHNFAILED.get());
+    }
+
+    protected String getResponseIssuer(RealmModel realm) {
+        return RealmsResource.realmBaseUrl(uriInfo).build(realm.getName()).toString();
+    }
+
+    protected Response getErrorResponse(ClientSessionModel clientSession, String status) {
+        String relayState = clientSession.getNote(GeneralConstants.RELAY_STATE);
+        String redirectUri = clientSession.getRedirectUri();
+        SAML2PostBindingResponseBuilder builder = new SAML2PostBindingResponseBuilder();
+        String responseIssuer = getResponseIssuer(realm);
+        builder .relayState(relayState)
+                .destination(redirectUri)
+                .responseIssuer(responseIssuer)
+                .requestIssuer(clientSession.getClient().getClientId());
+        try {
+            return builder.error(status);
+        } catch (Exception e) {
+            return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Failed to process response");
+        }
+    }
+
+    @Override
+    public Response authenticated(UserSessionModel userSession, ClientSessionCode accessCode) {
+        ClientSessionModel clientSession = accessCode.getClientSession();
+        if (SamlLogin.SAML_POST_BINDING.equals(clientSession.getNote(SamlLogin.SAML_BINDING))) {
+            return postBinding(userSession, clientSession);
+        }
+        throw new RuntimeException("still need to implement redirect binding");
+    }
+
+    protected Response postBinding(UserSessionModel userSession, ClientSessionModel clientSession) {
+        String requestID = clientSession.getNote("REQUEST_ID");
+        String relayState = clientSession.getNote(GeneralConstants.RELAY_STATE);
+        String redirectUri = clientSession.getRedirectUri();
+        String responseIssuer = getResponseIssuer(realm);
+
+        SAML2PostBindingResponseBuilder builder = new SAML2PostBindingResponseBuilder();
+        builder.requestID(requestID)
+               .relayState(relayState)
+               .destination(redirectUri)
+               .responseIssuer(responseIssuer)
+               .requestIssuer(clientSession.getClient().getClientId())
+               .userPrincipal(userSession.getUser().getUsername()) // todo userId instead?  There is no username claim it seems
+               .attribute(X500SAMLProfileConstants.USERID.getFriendlyName(), userSession.getUser().getId())
+               .authMethod(JBossSAMLURIConstants.AC_UNSPECIFIED.get());
+        initClaims(builder, clientSession.getClient(), userSession.getUser());
+        if (clientSession.getRoles() != null) {
+            for (String roleId : clientSession.getRoles()) {
+                // todo need a role mapping
+                RoleModel roleModel = clientSession.getRealm().getRoleById(roleId);
+                builder.roles(roleModel.getName());
+            }
+        }
+
+        try {
+            return builder.build();
+        } catch (Exception e) {
+            logger.error("failed", e);
+            return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Failed to process response");
+        }
+    }
+
+    public void initClaims(SAML2PostBindingResponseBuilder builder, ClientModel model, UserModel user) {
+        if (ClaimMask.hasEmail(model.getAllowedClaimsMask())) {
+            builder.attribute(X500SAMLProfileConstants.EMAIL_ADDRESS.getFriendlyName(), user.getEmail());
+        }
+        if (ClaimMask.hasName(model.getAllowedClaimsMask())) {
+            builder.attribute(X500SAMLProfileConstants.GIVEN_NAME.getFriendlyName(), user.getFirstName());
+            builder.attribute(X500SAMLProfileConstants.SURNAME.getFriendlyName(), user.getLastName());
+        }
+    }
+
+
+    @Override
+    public Response consentDenied(ClientSessionModel clientSession) {
+        return getErrorResponse(clientSession, JBossSAMLURIConstants.STATUS_REQUEST_DENIED.get());
+    }
+
+    @Override
+    public void close() {
+
+    }
+}
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlLoginFactory.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlLoginFactory.java
new file mode 100755
index 0000000..21618ed
--- /dev/null
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlLoginFactory.java
@@ -0,0 +1,43 @@
+package org.keycloak.protocol.saml;
+
+import org.keycloak.Config;
+import org.keycloak.events.EventBuilder;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.protocol.LoginProtocol;
+import org.keycloak.protocol.LoginProtocolFactory;
+import org.keycloak.services.managers.AuthenticationManager;
+import org.picketlink.identity.federation.core.sts.PicketLinkCoreSTS;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class SamlLoginFactory implements LoginProtocolFactory {
+
+    @Override
+    public Object createProtocolEndpoint(RealmModel realm, EventBuilder event, AuthenticationManager authManager) {
+        return new SamlService(realm, event, authManager);
+    }
+
+    @Override
+    public LoginProtocol create(KeycloakSession session) {
+        return new SamlLogin().setSession(session);
+    }
+
+    @Override
+    public void init(Config.Scope config) {
+        PicketLinkCoreSTS sts = PicketLinkCoreSTS.instance();
+        sts.installDefaultConfiguration();
+    }
+
+    @Override
+    public void close() {
+
+    }
+
+    @Override
+    public String getId() {
+        return "saml";
+    }
+}
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SAMLRequestParser.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SAMLRequestParser.java
new file mode 100755
index 0000000..4d5b6d4
--- /dev/null
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SAMLRequestParser.java
@@ -0,0 +1,48 @@
+package org.keycloak.protocol.saml;
+
+import org.picketlink.common.PicketLinkLogger;
+import org.picketlink.common.PicketLinkLoggerFactory;
+import org.picketlink.identity.federation.api.saml.v2.request.SAML2Request;
+import org.picketlink.identity.federation.core.saml.v2.common.SAMLDocumentHolder;
+import org.picketlink.identity.federation.web.util.PostBindingUtil;
+import org.picketlink.identity.federation.web.util.RedirectBindingUtil;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class SAMLRequestParser {
+    private static final PicketLinkLogger logger = PicketLinkLoggerFactory.getLogger();
+
+    public static SAMLDocumentHolder parseRedirectBinding(String samlMessage) {
+        InputStream is;
+        is = RedirectBindingUtil.base64DeflateDecode(samlMessage);
+        SAML2Request saml2Request = new SAML2Request();
+        try {
+            saml2Request.getSAML2ObjectFromStream(is);
+            return saml2Request.getSamlDocumentHolder();
+        } catch (Exception e) {
+            logger.samlBase64DecodingError(e);
+        }
+        return null;
+
+    }
+
+    public static SAMLDocumentHolder parsePostBinding(String samlMessage) {
+        InputStream is;
+        byte[] samlBytes = PostBindingUtil.base64Decode(samlMessage);
+        is = new ByteArrayInputStream(samlBytes);
+        SAML2Request saml2Request = new SAML2Request();
+        try {
+            saml2Request.getSAML2ObjectFromStream(is);
+            return saml2Request.getSamlDocumentHolder();
+        } catch (Exception e) {
+            logger.samlBase64DecodingError(e);
+        }
+        return null;
+
+    }
+}
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlService.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlService.java
new file mode 100755
index 0000000..a5ed37d
--- /dev/null
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlService.java
@@ -0,0 +1,246 @@
+package org.keycloak.protocol.saml;
+
+import org.jboss.logging.Logger;
+import org.jboss.resteasy.annotations.cache.NoCache;
+import org.jboss.resteasy.specimpl.MultivaluedMapImpl;
+import org.jboss.resteasy.spi.HttpRequest;
+import org.jboss.resteasy.spi.HttpResponse;
+import org.keycloak.ClientConnection;
+import org.keycloak.OAuth2Constants;
+import org.keycloak.events.Details;
+import org.keycloak.events.Errors;
+import org.keycloak.events.EventBuilder;
+import org.keycloak.events.EventType;
+import org.keycloak.login.LoginFormsProvider;
+import org.keycloak.models.ApplicationModel;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.ClientSessionModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserSessionModel;
+import org.keycloak.protocol.oidc.OpenIDConnectService;
+import org.keycloak.services.managers.AuthenticationManager;
+import org.keycloak.services.managers.ClientSessionCode;
+import org.keycloak.services.resources.flows.Flows;
+import org.picketlink.common.constants.GeneralConstants;
+import org.picketlink.identity.federation.core.saml.v2.common.SAMLDocumentHolder;
+import org.picketlink.identity.federation.saml.v2.SAML2Object;
+import org.picketlink.identity.federation.saml.v2.protocol.AuthnRequestType;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.FormParam;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Cookie;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.SecurityContext;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
+import javax.ws.rs.ext.Providers;
+import java.net.URI;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Resource class for the oauth/openid connect token service
+ *
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class SamlService {
+
+    protected static final Logger logger = Logger.getLogger(SamlService.class);
+
+    protected RealmModel realm;
+    private EventBuilder event;
+    protected AuthenticationManager authManager;
+
+    @Context
+    protected Providers providers;
+    @Context
+    protected SecurityContext securityContext;
+    @Context
+    protected UriInfo uriInfo;
+    @Context
+    protected HttpHeaders headers;
+    @Context
+    protected HttpRequest request;
+    @Context
+    protected HttpResponse response;
+    @Context
+    protected KeycloakSession session;
+    @Context
+    protected ClientConnection clientConnection;
+
+    /*
+    @Context
+    protected ResourceContext resourceContext;
+    */
+
+    public SamlService(RealmModel realm, EventBuilder event, AuthenticationManager authManager) {
+        this.realm = realm;
+        this.event = event;
+        this.authManager = authManager;
+    }
+
+    /**
+     */
+    @Path("POST")
+    @POST
+    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
+    public Response loginPage(@FormParam(GeneralConstants.SAML_REQUEST_KEY) String samlRequest,
+                              @FormParam(GeneralConstants.RELAY_STATE) String relayState) {
+        event.event(EventType.LOGIN);
+        if (!checkSsl()) {
+            event.error(Errors.SSL_REQUIRED);
+            return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "HTTPS required");
+        }
+        if (!realm.isEnabled()) {
+            event.error(Errors.REALM_DISABLED);
+            return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Realm not enabled");
+        }
+
+        if (samlRequest == null) {
+            event.error(Errors.INVALID_TOKEN);
+            return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid Request");
+
+        }
+
+        SAMLDocumentHolder documentHolder = SAMLRequestParser.parsePostBinding(samlRequest);
+        if (documentHolder == null) {
+            event.error(Errors.INVALID_TOKEN);
+            return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid Request");
+        }
+
+        SAML2Object samlObject = documentHolder.getSamlObject();
+        if (!(samlObject instanceof AuthnRequestType)) {
+            event.error(Errors.INVALID_TOKEN);
+            return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid Request");
+        }
+
+        // Get the SAML Request Message
+        AuthnRequestType requestAbstractType = (AuthnRequestType) samlObject;
+        String issuer = requestAbstractType.getIssuer().getValue();
+        ClientModel client = realm.findClient(issuer);
+
+        if (client == null) {
+            event.error(Errors.CLIENT_NOT_FOUND);
+            return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Unknown login requester.");
+        }
+
+        if (!client.isEnabled()) {
+            event.error(Errors.CLIENT_DISABLED);
+            return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Login requester not enabled.");
+        }
+        if ((client instanceof ApplicationModel) && ((ApplicationModel)client).isBearerOnly()) {
+            event.error(Errors.NOT_ALLOWED);
+            return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Bearer-only applications are not allowed to initiate browser login");
+        }
+        if (client.isDirectGrantsOnly()) {
+            event.error(Errors.NOT_ALLOWED);
+            return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "direct-grants-only clients are not allowed to initiate browser login");
+        }
+
+        URI redirectUri = requestAbstractType.getAssertionConsumerServiceURL();
+        String redirect = OpenIDConnectService.verifyRedirectUri(uriInfo, redirectUri.toString(), realm, client);
+
+        if (redirect == null) {
+            event.error(Errors.INVALID_REDIRECT_URI);
+            return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid redirect_uri.");
+        }
+
+
+        ClientSessionModel clientSession = session.sessions().createClientSession(realm, client);
+        clientSession.setAuthMethod(SamlLogin.LOGIN_PROTOCOL);
+        clientSession.setRedirectUri(redirect);
+        clientSession.setAction(ClientSessionModel.Action.AUTHENTICATE);
+        clientSession.setNote(SamlLogin.SAML_BINDING, SamlLogin.SAML_POST_BINDING);
+        clientSession.setNote(GeneralConstants.RELAY_STATE, relayState);
+        clientSession.setNote("REQUEST_ID", requestAbstractType.getID());
+
+        Response response = authManager.checkNonFormAuthentication(session, clientSession, realm, uriInfo, request, clientConnection, headers, event);
+        if (response != null) return response;
+
+        LoginFormsProvider forms = Flows.forms(session, realm, clientSession.getClient(), uriInfo)
+                .setClientSessionCode(new ClientSessionCode(realm, clientSession).getCode());
+
+        String rememberMeUsername = null;
+        if (realm.isRememberMe()) {
+            Cookie rememberMeCookie = headers.getCookies().get(AuthenticationManager.KEYCLOAK_REMEMBER_ME);
+            if (rememberMeCookie != null && !"".equals(rememberMeCookie.getValue())) {
+                rememberMeUsername = rememberMeCookie.getValue();
+            }
+        }
+
+        if (rememberMeUsername != null) {
+            MultivaluedMap<String, String> formData = new MultivaluedMapImpl<String, String>();
+            formData.add(AuthenticationManager.FORM_USERNAME, rememberMeUsername);
+            formData.add("rememberMe", "on");
+
+            forms.setFormData(formData);
+        }
+
+        return forms.createLogin();
+    }
+
+
+    /**
+     * Logout user session.  User must be logged in via a session cookie.
+     *
+     * @param redirectUri
+     * @return
+     */
+    @Path("logout")
+    @GET
+    @NoCache
+    public Response logout(final @QueryParam("shit") String redirectUri) {
+        event.event(EventType.LOGOUT);
+        if (redirectUri != null) {
+            event.detail(Details.REDIRECT_URI, redirectUri);
+        }
+        // authenticate identity cookie, but ignore an access token timeout as we're logging out anyways.
+        AuthenticationManager.AuthResult authResult = authManager.authenticateIdentityCookie(session, realm, uriInfo, clientConnection, headers, false);
+        if (authResult != null) {
+            logout(authResult.getSession());
+        }
+
+        if (redirectUri != null) {
+            String validatedRedirect = OpenIDConnectService.verifyRealmRedirectUri(uriInfo, redirectUri, realm);
+            if (validatedRedirect == null) {
+                return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid redirect uri.");
+            }
+            return Response.status(302).location(UriBuilder.fromUri(validatedRedirect).build()).build();
+        } else {
+            return Response.ok().build();
+        }
+    }
+
+    private void logout(UserSessionModel userSession) {
+        authManager.logout(session, realm, userSession, uriInfo, clientConnection);
+        event.user(userSession.getUser()).session(userSession).success();
+    }
+
+    private boolean checkSsl() {
+        if (uriInfo.getBaseUri().getScheme().equals("https")) {
+            return true;
+        } else {
+            return !realm.getSslRequired().isRequired(clientConnection);
+        }
+    }
+
+    private Response createError(String error, String errorDescription, Response.Status status) {
+        Map<String, String> e = new HashMap<String, String>();
+        e.put(OAuth2Constants.ERROR, error);
+        if (errorDescription != null) {
+            e.put(OAuth2Constants.ERROR_DESCRIPTION, errorDescription);
+        }
+        return Response.status(status).entity(e).type("application/json").build();
+    }
+
+}
diff --git a/saml/saml-protocol/src/main/resources/META-INF/services/org.keycloak.protocol.LoginProtocolFactory b/saml/saml-protocol/src/main/resources/META-INF/services/org.keycloak.protocol.LoginProtocolFactory
new file mode 100755
index 0000000..632a1db
--- /dev/null
+++ b/saml/saml-protocol/src/main/resources/META-INF/services/org.keycloak.protocol.LoginProtocolFactory
@@ -0,0 +1 @@
+org.keycloak.protocol.saml.SamlLoginFactory
\ No newline at end of file
diff --git a/services/src/main/java/org/keycloak/protocol/LoginProtocol.java b/services/src/main/java/org/keycloak/protocol/LoginProtocol.java
index 8a66f16..86de30a 100755
--- a/services/src/main/java/org/keycloak/protocol/LoginProtocol.java
+++ b/services/src/main/java/org/keycloak/protocol/LoginProtocol.java
@@ -18,15 +18,15 @@ import javax.ws.rs.core.UriInfo;
  * @version $Revision: 1 $
  */
 public interface LoginProtocol extends Provider {
-    OpenIDConnect setSession(KeycloakSession session);
+    LoginProtocol setSession(KeycloakSession session);
 
-    OpenIDConnect setRealm(RealmModel realm);
+    LoginProtocol setRealm(RealmModel realm);
 
-    OpenIDConnect setRequest(HttpRequest request);
+    LoginProtocol setRequest(HttpRequest request);
 
-    OpenIDConnect setUriInfo(UriInfo uriInfo);
+    LoginProtocol setUriInfo(UriInfo uriInfo);
 
-    OpenIDConnect setClientConnection(ClientConnection clientConnection);
+    LoginProtocol setClientConnection(ClientConnection clientConnection);
 
     Response cancelLogin(ClientSessionModel clientSession);
     Response invalidSessionError(ClientSessionModel clientSession);
diff --git a/testsuite/integration/src/test/resources/testsaml.json b/testsuite/integration/src/test/resources/testsaml.json
new file mode 100755
index 0000000..6dc9d71
--- /dev/null
+++ b/testsuite/integration/src/test/resources/testsaml.json
@@ -0,0 +1,48 @@
+{
+    "id": "demo",
+    "realm": "demo",
+    "enabled": true,
+    "sslRequired": "external",
+    "registrationAllowed": true,
+    "resetPasswordAllowed": true,
+    "passwordCredentialGrantAllowed": true,
+    "privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=",
+    "publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
+    "requiredCredentials": [ "password" ],
+    "defaultRoles": [ "user" ],
+    "smtpServer": {
+        "from": "auto@keycloak.org",
+        "host": "localhost",
+        "port":"3025"
+    },
+    "users" : [
+        {
+            "username" : "bburke",
+            "enabled": true,
+            "email" : "bburke@redhat.com",
+            "credentials" : [
+                { "type" : "password",
+                  "value" : "password" }
+            ],
+            "realmRoles": ["manager"]
+        }
+    ],
+    "applications": [
+        {
+            "name": "http://localhost:8080/sales-post/",
+            "enabled": true,
+            "fullScopeAllowed": true,
+            "redirectUris": [
+                "http://localhost:8080/sales-post/*"
+            ]
+         }
+    ],
+    "roles" : {
+        "realm" : [
+            {
+                "name": "manager",
+                "description": "Have Manager privileges"
+            }
+        ]
+    }
+}