keycloak-aplcache

Changes

pom.xml 12(+11 -1)

Details

diff --git a/core/src/main/java/org/keycloak/representations/idm/UserFederationMapperTypeRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/UserFederationMapperTypeRepresentation.java
index f03b376..f7f594a 100644
--- a/core/src/main/java/org/keycloak/representations/idm/UserFederationMapperTypeRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/UserFederationMapperTypeRepresentation.java
@@ -1,5 +1,6 @@
 package org.keycloak.representations.idm;
 
+import java.util.LinkedList;
 import java.util.List;
 
 /**
@@ -11,7 +12,7 @@ public class UserFederationMapperTypeRepresentation {
     protected String category;
     protected String helpText;
 
-    protected List<ConfigPropertyRepresentation> properties;
+    protected List<ConfigPropertyRepresentation> properties  = new LinkedList<>();
 
     public String getId() {
         return id;
diff --git a/distribution/adapters/pom.xml b/distribution/adapters/pom.xml
index 612ecae..8c13893 100755
--- a/distribution/adapters/pom.xml
+++ b/distribution/adapters/pom.xml
@@ -38,5 +38,6 @@
         <module>tomcat7-adapter-zip</module>
         <module>tomcat8-adapter-zip</module>
         <module>wildfly-adapter-zip</module>
+        <module>wf8-adapter</module>
     </modules>
 </project>
diff --git a/distribution/adapters/wf8-adapter/pom.xml b/distribution/adapters/wf8-adapter/pom.xml
new file mode 100644
index 0000000..7f71d64
--- /dev/null
+++ b/distribution/adapters/wf8-adapter/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.3.0.Final-SNAPSHOT</version>
+        <relativePath>../../../pom.xml</relativePath>
+    </parent>
+    <name>Keycloak Wildfly 8 Adapter</name>
+    <description/>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>keycloak-wf8-adapter-dist-pom</artifactId>
+    <packaging>pom</packaging>
+
+    <modules>
+        <module>wf8-modules</module>
+        <module>wf8-adapter-zip</module>
+    </modules>
+</project>
diff --git a/distribution/adapters/wf8-adapter/wf8-adapter-zip/assembly.xml b/distribution/adapters/wf8-adapter/wf8-adapter-zip/assembly.xml
new file mode 100755
index 0000000..cad94c5
--- /dev/null
+++ b/distribution/adapters/wf8-adapter/wf8-adapter-zip/assembly.xml
@@ -0,0 +1,29 @@
+<assembly>
+    <id>war-dist</id>
+
+    <formats>
+        <format>zip</format>
+        <format>tar.gz</format>
+    </formats>
+    <includeBaseDirectory>false</includeBaseDirectory>
+
+    <fileSets>
+        <fileSet>
+            <directory>${project.build.directory}/unpacked</directory>
+            <includes>
+                <include>net/iharder/base64/**</include>
+                <include>org/apache/httpcomponents/**</include>
+                <include>org/keycloak/keycloak-core/**</include>
+                <include>org/keycloak/keycloak-adapter-core/**</include>
+                <include>org/keycloak/keycloak-jboss-adapter-core/**</include>
+                <include>org/keycloak/keycloak-undertow-adapter/**</include>
+                <include>org/keycloak/keycloak-wildfly-adapter/**</include>
+                <include>org/keycloak/keycloak-wf8-subsystem/**</include>
+            </includes>
+            <excludes>
+                <exclude>**/*.war</exclude>
+            </excludes>
+            <outputDirectory>modules/system/layers/base</outputDirectory>
+        </fileSet>
+    </fileSets>
+</assembly>
diff --git a/distribution/adapters/wf8-adapter/wf8-adapter-zip/pom.xml b/distribution/adapters/wf8-adapter/wf8-adapter-zip/pom.xml
new file mode 100755
index 0000000..aa33dd3
--- /dev/null
+++ b/distribution/adapters/wf8-adapter/wf8-adapter-zip/pom.xml
@@ -0,0 +1,76 @@
+<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">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <artifactId>keycloak-parent</artifactId>
+        <groupId>org.keycloak</groupId>
+        <version>1.3.0.Final-SNAPSHOT</version>
+        <relativePath>../../../../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>keycloak-wf8-adapter-dist</artifactId>
+    <packaging>pom</packaging>
+    <name>Keycloak Wildfly 8 Adapter Distro</name>
+    <description/>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-wf8-modules</artifactId>
+            <type>zip</type>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-dependency-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>unpack</id>
+                        <phase>prepare-package</phase>
+                        <goals>
+                            <goal>unpack</goal>
+                        </goals>
+                        <configuration>
+                            <artifactItems>
+                                <artifactItem>
+                                    <groupId>org.keycloak</groupId>
+                                    <artifactId>keycloak-wf8-modules</artifactId>
+                                    <type>zip</type>
+                                    <outputDirectory>${project.build.directory}/unpacked</outputDirectory>
+                                </artifactItem>
+                            </artifactItems>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <artifactId>maven-assembly-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>assemble</id>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>single</goal>
+                        </goals>
+                        <configuration>
+                            <descriptors>
+                                <descriptor>assembly.xml</descriptor>
+                            </descriptors>
+                            <outputDirectory>
+                                target
+                            </outputDirectory>
+                            <workDirectory>
+                                target/assembly/work
+                            </workDirectory>
+                            <appendAssemblyId>false</appendAssemblyId>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/distribution/adapters/wf8-adapter/wf8-modules/assembly.xml b/distribution/adapters/wf8-adapter/wf8-modules/assembly.xml
new file mode 100755
index 0000000..4a34435
--- /dev/null
+++ b/distribution/adapters/wf8-adapter/wf8-modules/assembly.xml
@@ -0,0 +1,22 @@
+<assembly>
+    <id>dist</id>
+
+    <formats>
+        <format>zip</format>
+    </formats>
+    <includeBaseDirectory>false</includeBaseDirectory>
+
+    <fileSets>
+        <fileSet>
+            <directory>../../</directory>
+            <includes>
+                <include>License.html</include>
+            </includes>
+            <outputDirectory></outputDirectory>
+        </fileSet>
+        <fileSet>
+            <directory>${project.build.directory}/modules</directory>
+            <outputDirectory></outputDirectory>
+        </fileSet>
+    </fileSets>
+</assembly>
diff --git a/distribution/adapters/wf8-adapter/wf8-modules/build.xml b/distribution/adapters/wf8-adapter/wf8-modules/build.xml
new file mode 100755
index 0000000..0ede555
--- /dev/null
+++ b/distribution/adapters/wf8-adapter/wf8-modules/build.xml
@@ -0,0 +1,88 @@
+<!--
+  ~ JBoss, Home of Professional Open Source.
+  ~ Copyright 2012, Red Hat, Inc., and individual contributors
+  ~ as indicated by the @author tags. See the copyright.txt file in the
+  ~ distribution for a full listing of individual contributors.
+  ~
+  ~ This is free software; you can redistribute it and/or modify it
+  ~ under the terms of the GNU Lesser General Public License as
+  ~ published by the Free Software Foundation; either version 2.1 of
+  ~ the License, or (at your option) any later version.
+  ~
+  ~ This software is distributed in the hope that it will be useful,
+  ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
+  ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  ~ Lesser General Public License for more details.
+  ~
+  ~ You should have received a copy of the GNU Lesser General Public
+  ~ License along with this software; if not, write to the Free
+  ~ Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+  ~ 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+  -->
+
+<project name="module-repository" basedir="." default="all">
+
+    <import file="lib.xml"/>
+
+    <property name="output.dir" value="target"/>
+
+    <target name="all">
+        <antcall target="modules">
+            <param name="mavenized.modules" value="false"/>
+            <param name="output.dir" value="target"/>
+        </antcall>
+    </target>
+
+
+    <target name="modules">
+
+        <!-- server min dependencies -->
+
+        <module-def name="org.keycloak.keycloak-core">
+            <maven-resource group="org.keycloak" artifact="keycloak-core"/>
+        </module-def>
+
+        <module-def name="net.iharder.base64">
+            <maven-resource group="net.iharder" artifact="base64"/>
+        </module-def>
+
+
+        <!-- subsystems -->
+
+        <module-def name="org.keycloak.keycloak-adapter-core">
+            <maven-resource group="org.keycloak" artifact="keycloak-adapter-core"/>
+        </module-def>
+
+        <module-def name="org.keycloak.keycloak-jboss-adapter-core">
+            <maven-resource group="org.keycloak" artifact="keycloak-jboss-adapter-core"/>
+        </module-def>
+
+        <module-def name="org.keycloak.keycloak-undertow-adapter">
+            <maven-resource group="org.keycloak" artifact="keycloak-undertow-adapter"/>
+        </module-def>
+
+        <module-def name="org.keycloak.keycloak-wildfly-adapter">
+            <maven-resource group="org.keycloak" artifact="keycloak-wildfly-adapter"/>
+        </module-def>
+
+        <module-def name="org.keycloak.keycloak-wf8-subsystem">
+            <maven-resource group="org.keycloak" artifact="keycloak-wf8-subsystem"/>
+        </module-def>
+
+        <module-def name="org.apache.httpcomponents" slot="4.3">
+            <maven-resource group="org.apache.httpcomponents" artifact="httpclient"/>
+            <maven-resource group="org.apache.httpcomponents" artifact="httpcore"/>
+            <maven-resource group="org.apache.httpcomponents" artifact="httpmime"/>
+        </module-def>
+
+    </target>
+
+    <target name="clean-target">
+        <delete dir="${output.dir}"/>
+    </target>
+
+    <target name="clean" depends="clean-target">
+        <delete file="maven-ant-tasks.jar"/>
+    </target>
+
+</project>
diff --git a/distribution/adapters/wf8-adapter/wf8-modules/lib.xml b/distribution/adapters/wf8-adapter/wf8-modules/lib.xml
new file mode 100755
index 0000000..3d9438a
--- /dev/null
+++ b/distribution/adapters/wf8-adapter/wf8-modules/lib.xml
@@ -0,0 +1,282 @@
+<!--
+  ~ JBoss, Home of Professional Open Source.
+  ~ Copyright 2010, Red Hat, Inc., and individual contributors
+  ~ as indicated by the @author tags. See the copyright.txt file in the
+  ~ distribution for a full listing of individual contributors.
+  ~
+  ~ This is free software; you can redistribute it and/or modify it
+  ~ under the terms of the GNU Lesser General Public License as
+  ~ published by the Free Software Foundation; either version 2.1 of
+  ~ the License, or (at your option) any later version.
+  ~
+  ~ This software is distributed in the hope that it will be useful,
+  ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
+  ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  ~ Lesser General Public License for more details.
+  ~
+  ~ You should have received a copy of the GNU Lesser General Public
+  ~ License along with this software; if not, write to the Free
+  ~ Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+  ~ 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+  -->
+
+<project name="module-repository-lib">
+
+    <property name="src.dir" value="src"/>
+    <property name="module.repo.src.dir" value="${src.dir}/main/resources/modules"/>
+    <property name="module.xml" value="module.xml"/>
+
+    <taskdef resource="net/sf/antcontrib/antlib.xml"/>
+    <taskdef name="jandex" classname="org.jboss.jandex.JandexAntTask" />
+
+    <macrodef name="module-def">
+        <attribute name="name"/>
+        <attribute name="slot" default="main"/>
+        <element name="resources" implicit="yes" optional="yes"/>
+
+        <sequential>
+            <echo message="Initializing module -> @{name}"/>
+            <property name="module.repo.output.dir" value="${output.dir}/modules"/>
+            <!-- Figure out the correct module path -->
+            <define-module-dir name="@{name}" slot="@{slot}"/>
+
+            <!-- Make the module output director -->
+            <mkdir dir="${module.repo.output.dir}/${current.module.path}"/>
+
+            <!-- Copy the module.xml and other stuff to the output director -->
+            <copy todir="${module.repo.output.dir}/${current.module.path}" overwrite="true">
+                <fileset dir="${module.repo.src.dir}/${current.module.path}">
+                    <include name="**"/>
+                </fileset>
+            </copy>
+
+            <!-- Process the resource -->
+            <resources/>
+            
+            <!-- Add keycloak version property to module xml -->
+            <replace file="${module.repo.output.dir}/${current.module.path}/${module.xml}"
+                     token="$${project.version}"
+                     value="${project.version}"/>
+            
+            <!-- Some final cleanup -->
+            <replace file="${module.repo.output.dir}/${current.module.path}/${module.xml}">
+                <replacetoken>
+                    <![CDATA[
+        <!-- Insert resources here -->]]></replacetoken>
+                <replacevalue>
+                </replacevalue>
+            </replace>
+
+        </sequential>
+    </macrodef>
+
+    <macrodef name="bundle-def">
+        <attribute name="name"/>
+        <attribute name="slot" default="main"/>
+        <element name="resources" implicit="yes" optional="yes"/>
+
+        <sequential>
+            <echo message="Initializing bundle -> @{name}"/>
+            <property name="bundle.repo.output.dir" value="${output.dir}/bundles/system/layers/base"/>
+            <!-- Figure out the correct bundle path -->
+            <define-bundle-dir name="@{name}" slot="@{slot}" />
+
+            <!-- Make the bundle output director -->
+            <mkdir dir="${bundle.repo.output.dir}/${current.bundle.path}"/>
+
+            <!-- Process the resource -->
+            <resources/>
+
+        </sequential>
+    </macrodef>
+
+    <macrodef name="maven-bundle" >
+        <attribute name="group"/>
+        <attribute name="artifact"/>
+
+        <sequential>
+            <!-- Copy the jar to the bundle dir -->
+            <property name="bundle.repo.output.dir" value="${output.dir}/bundles/system/layers/base"/>
+            <copy todir="${bundle.repo.output.dir}/${current.bundle.path}" failonerror="true">
+                <fileset file="${@{group}:@{artifact}:jar}"/>
+                <mapper type="flatten" />
+            </copy>
+        </sequential>
+    </macrodef>
+
+    <scriptdef name="define-module-dir" language="javascript" manager="bsf">
+        <attribute name="name"/>
+        <attribute name="slot"/>
+        <![CDATA[
+            name = attributes.get("name");
+            name = name.replace(".", "/");
+            project.setProperty("current.module.path", name + "/" + attributes.get("slot"));
+        ]]>
+    </scriptdef>
+
+    <scriptdef name="define-bundle-dir" language="javascript"  manager="bsf">
+        <attribute name="name"/>
+        <attribute name="slot"/>
+        <![CDATA[
+            name = attributes.get("name");
+            name = name.replace(".", "/");
+            project.setProperty("current.bundle.path", name + "/" + attributes.get("slot"));
+        ]]>
+    </scriptdef>
+
+    <!--
+       Get the version from the parent directory of the jar.  If the parent directory is 'target' this
+       means that the jar is contained in AS build so extract the version from the file name
+    -->
+    <scriptdef name="define-maven-artifact" language="javascript"  manager="bsf">
+        <attribute name="group"/>
+        <attribute name="artifact"/>
+        <attribute name="classifier"/>
+        <attribute name="element"/>
+        <attribute name="path"/>
+        <![CDATA[
+            importClass(Packages.java.io.File);
+            group = attributes.get("group");
+            artifact = attributes.get("artifact");
+            classifier = attributes.get("classifier");
+            element = attributes.get("element");
+            path = attributes.get("path");
+            if(path.indexOf('${') != -1) {
+                throw "Module resource root not found, make sure it is listed in build/pom.xml" + path;
+            }
+            fp = new File(path);
+            version = fp.getParentFile().getName();
+            if (version.equals("target")) {
+               version = fp.getName();
+               version = version.substring(artifact.length() + 1);
+               suffix = ".jar";
+               if (classifier) {
+                  suffix = "-" + classifier + suffix;
+               }
+               version = version.replace(suffix, "");
+            }
+
+            root = "<" + element + " name=\"" + group + ":" + artifact + ":" + version;
+            if (classifier) {
+               root = root + ":" + classifier;
+            }
+            root = root + "\"/>";
+            project.setProperty("current.maven.root", root);
+        ]]>
+    </scriptdef>
+
+    <macrodef name="maven-resource" >
+        <attribute name="group"/>
+        <attribute name="artifact"/>
+        <attribute name="jandex" default="false" />
+
+        <sequential>
+            <if>
+               <equals arg1="${mavenized.modules}" arg2="true"/>
+               <then>
+                <define-maven-artifact group="@{group}" artifact="@{artifact}" element="artifact" path="${@{group}:@{artifact}:jar}"/>
+                <replace file="${module.repo.output.dir}/${current.module.path}/${module.xml}">
+                    <replacefilter token="&lt;!-- Insert resources here --&gt;" value="${current.maven.root}&#10;        &lt;!-- Insert resources here --&gt;"/>
+                </replace>
+               </then>
+
+            <else>
+            <!-- Copy the jar to the module dir -->
+            <copy todir="${module.repo.output.dir}/${current.module.path}" failonerror="true">
+                <fileset file="${@{group}:@{artifact}:jar}"/>
+                <mapper type="flatten" />
+            </copy>
+
+            <basename file="${@{group}:@{artifact}:jar}" property="resourcename.@{group}.@{artifact}"/>
+            <!-- Generate the Jandex Index -->
+            <jandex run="@{jandex}" newJar="true" >
+                <fileset dir="${module.repo.output.dir}/${current.module.path}" />
+            </jandex>
+            <!-- Update the resource entry in module.xml -->
+            <define-resource-root path="${resourcename.@{group}.@{artifact}}" jandex="@{jandex}"/>
+            <replace file="${module.repo.output.dir}/${current.module.path}/${module.xml}">
+                <replacefilter token="&lt;!-- Insert resources here --&gt;" value="${current.resource.root}&#10;        &lt;!-- Insert resources here --&gt;"/>
+            </replace>
+            </else>
+            </if>
+        </sequential>
+    </macrodef>
+
+
+
+    <macrodef name="maven-resource-with-classifier" >
+        <attribute name="group"/>
+        <attribute name="artifact"/>
+        <attribute name="classifier"/>
+        <attribute name="jandex" default="false" />
+
+        <sequential>
+            <if>
+            <equals arg1="${mavenized.modules}" arg2="true"/>
+            <then>
+                <define-maven-artifact group="@{group}" artifact="@{artifact}" element="artifact" classifier="@{classifier}" path="${@{group}:@{artifact}:jar:@{classifier}}"/>
+                <replace file="${module.repo.output.dir}/${current.module.path}/${module.xml}">
+                    <replacefilter token="&lt;!-- Insert resources here --&gt;" value="${current.maven.root}&#10;        &lt;!-- Insert resources here --&gt;"/>
+                </replace>
+            </then>
+            <else>
+            <!-- Copy the jar to the module dir -->
+            <copy todir="${module.repo.output.dir}/${current.module.path}" failonerror="true">
+                <fileset file="${@{group}:@{artifact}:jar:@{classifier}}"/>
+                <!-- http://jira.codehaus.org/browse/MANTRUN-159 -->
+                <mapper type="flatten" />
+            </copy>
+
+            <basename file="${@{group}:@{artifact}:jar:@{classifier}}" property="resourcename.@{group}.@{artifact}.@{classifier}"/>
+
+            <!-- Update the resource entry in module.xml -->
+            <define-resource-root path="${resourcename.@{group}.@{artifact}.@{classifier}}"/>
+            <replace file="${module.repo.output.dir}/${current.module.path}/${module.xml}">
+                <replacefilter token="&lt;!-- Insert resources here --&gt;" value="${current.resource.root}&#10;        &lt;!-- Insert resources here --&gt;"/>
+            </replace>
+            </else>
+            </if>
+        </sequential>
+    </macrodef>
+
+    <macrodef name="extract-native-jar" >
+        <attribute name="group"/>
+        <attribute name="artifact"/>
+        <sequential>
+            <if>
+            <equals arg1="${mavenized.modules}" arg2="true"/>
+            <then>
+                <define-maven-artifact group="@{group}" artifact="@{artifact}" element="native-artifact" path="${@{group}:@{artifact}:jar}"/>
+                <replace file="${module.repo.output.dir}/${current.module.path}/${module.xml}">
+                    <replacefilter token="&lt;!-- Insert resources here --&gt;" value="${current.maven.root}&#10;        &lt;!-- Insert resources here --&gt;"/>
+                </replace>
+            </then>
+
+            <else>
+            <unzip src="${@{group}:@{artifact}:jar}" dest="${module.repo.output.dir}/${current.module.path}">
+           <patternset>
+               <include name="lib/**"/>
+           </patternset>
+           </unzip>
+           </else>
+           </if>
+        </sequential>
+    </macrodef>
+
+    <scriptdef name="define-resource-root" language="javascript" manager="bsf">
+        <attribute name="path"/>
+        <attribute name="jandex"/>
+        <![CDATA[
+            path = attributes.get("path");
+            root = "<resource-root path=\"" + path + "\"/>";
+            if(path.indexOf('${') != -1) {
+                throw "Module resource root not found, make sure it is listed in build/pom.xml" + path;
+            }
+            if(attributes.get("jandex") == "true" ) {
+                root = root + "\n\t<resource-root path=\"" + path.replace(".jar","-jandex.jar") + "\"/>";
+            }
+            project.setProperty("current.resource.root", root);
+        ]]>
+    </scriptdef>
+
+</project>
diff --git a/distribution/adapters/wf8-adapter/wf8-modules/pom.xml b/distribution/adapters/wf8-adapter/wf8-modules/pom.xml
new file mode 100755
index 0000000..d79e42c
--- /dev/null
+++ b/distribution/adapters/wf8-adapter/wf8-modules/pom.xml
@@ -0,0 +1,137 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<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/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <artifactId>keycloak-parent</artifactId>
+        <groupId>org.keycloak</groupId>
+        <version>1.3.0.Final-SNAPSHOT</version>
+        <relativePath>../../../../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>keycloak-wf8-modules</artifactId>
+
+    <name>Keycloak Wildfly 8 Modules</name>
+    <packaging>pom</packaging>
+    <dependencies>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-adapter-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-jboss-adapter-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-undertow-adapter</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-wildfly-adapter</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-wf8-subsystem</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpmime</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpcore</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-antrun-plugin</artifactId>
+                <inherited>false</inherited>
+                <executions>
+                    <execution>
+                        <id>build-dist</id>
+                        <goals>
+                            <goal>run</goal>
+                        </goals>
+                        <phase>compile</phase>
+                        <configuration>
+                            <target>
+                                <ant antfile="build.xml" inheritRefs="true">
+                                    <target name="all"/>
+                                </ant>
+                            </target>
+                        </configuration>
+                    </execution>
+                </executions>
+                <dependencies>
+                    <dependency>
+                        <groupId>org.jboss</groupId>
+                        <artifactId>jandex</artifactId>
+                        <version>1.0.3.Final</version>
+                    </dependency>
+                    <dependency>
+                        <groupId>ant-contrib</groupId>
+                        <artifactId>ant-contrib</artifactId>
+                        <version>1.0b3</version>
+                        <exclusions>
+                            <exclusion>
+                                <groupId>ant</groupId>
+                                <artifactId>ant</artifactId>
+                            </exclusion>
+                        </exclusions>
+                    </dependency>
+                    <dependency>
+                        <groupId>org.apache.ant</groupId>
+                        <artifactId>ant-apache-bsf</artifactId>
+                        <version>1.9.3</version>
+                    </dependency>
+                    <dependency>
+                        <groupId>org.apache.bsf</groupId>
+                        <artifactId>bsf-api</artifactId>
+                        <version>3.1</version>
+                    </dependency>
+                    <dependency>
+                        <groupId>rhino</groupId>
+                        <artifactId>js</artifactId>
+                        <version>1.7R2</version>
+                    </dependency>
+                </dependencies>
+            </plugin>
+            <plugin>
+                <artifactId>maven-assembly-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>assemble</id>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>single</goal>
+                        </goals>
+                        <configuration>
+                            <descriptors>
+                                <descriptor>assembly.xml</descriptor>
+                            </descriptors>
+                            <outputDirectory>
+                                target
+                            </outputDirectory>
+                            <workDirectory>
+                                target/assembly/work
+                            </workDirectory>
+                            <appendAssemblyId>false</appendAssemblyId>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/distribution/adapters/wf8-adapter/wf8-modules/src/main/resources/modules/net/iharder/base64/main/module.xml b/distribution/adapters/wf8-adapter/wf8-modules/src/main/resources/modules/net/iharder/base64/main/module.xml
new file mode 100755
index 0000000..c99b968
--- /dev/null
+++ b/distribution/adapters/wf8-adapter/wf8-modules/src/main/resources/modules/net/iharder/base64/main/module.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+
+
+<module xmlns="urn:jboss:module:1.1" name="net.iharder.base64">
+    <resources>
+        <!-- Insert resources here -->
+    </resources>
+    <dependencies>
+        <module name="javax.api"/>
+    </dependencies>
+
+</module>
diff --git a/distribution/adapters/wf8-adapter/wf8-modules/src/main/resources/modules/org/apache/httpcomponents/4.3/module.xml b/distribution/adapters/wf8-adapter/wf8-modules/src/main/resources/modules/org/apache/httpcomponents/4.3/module.xml
new file mode 100644
index 0000000..a3e65f8
--- /dev/null
+++ b/distribution/adapters/wf8-adapter/wf8-modules/src/main/resources/modules/org/apache/httpcomponents/4.3/module.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<module xmlns="urn:jboss:module:1.1" name="org.apache.httpcomponents" slot="4.3">
+    <resources>
+        <!-- Insert resources here -->
+    </resources>
+
+    <dependencies>
+        <module name="javax.api"/>
+        <module name="org.apache.commons.codec"/>
+        <module name="org.apache.commons.logging"/>
+        <module name="org.apache.james.mime4j"/>
+    </dependencies>
+</module>
diff --git a/distribution/adapters/wf8-adapter/wf8-modules/src/main/resources/modules/org/keycloak/keycloak-adapter-core/main/module.xml b/distribution/adapters/wf8-adapter/wf8-modules/src/main/resources/modules/org/keycloak/keycloak-adapter-core/main/module.xml
new file mode 100755
index 0000000..1be1486
--- /dev/null
+++ b/distribution/adapters/wf8-adapter/wf8-modules/src/main/resources/modules/org/keycloak/keycloak-adapter-core/main/module.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+
+
+<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-adapter-core">
+    <resources>
+        <!-- Insert resources here -->
+    </resources>
+    <dependencies>
+        <module name="javax.api"/>
+        <module name="org.codehaus.jackson.jackson-core-asl"/>
+        <module name="org.codehaus.jackson.jackson-mapper-asl"/>
+        <module name="org.codehaus.jackson.jackson-xc"/>
+        <module name="org.apache.httpcomponents" slot="4.3" />
+        <module name="org.jboss.logging"/>
+        <module name="org.keycloak.keycloak-core"/>
+        <module name="net.iharder.base64"/>
+    </dependencies>
+
+</module>
diff --git a/distribution/adapters/wf8-adapter/wf8-modules/src/main/resources/modules/org/keycloak/keycloak-core/main/module.xml b/distribution/adapters/wf8-adapter/wf8-modules/src/main/resources/modules/org/keycloak/keycloak-core/main/module.xml
new file mode 100755
index 0000000..545f168
--- /dev/null
+++ b/distribution/adapters/wf8-adapter/wf8-modules/src/main/resources/modules/org/keycloak/keycloak-core/main/module.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+
+
+<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-core">
+    <resources>
+        <!-- Insert resources here -->
+    </resources>
+    <dependencies>
+        <module name="org.codehaus.jackson.jackson-core-asl"/>
+        <module name="org.codehaus.jackson.jackson-mapper-asl"/>
+        <module name="org.codehaus.jackson.jackson-xc"/>
+        <module name="org.bouncycastle" />
+        <module name="net.iharder.base64"/>
+        <module name="javax.api"/>
+        <module name="javax.activation.api"/>
+        <module name="sun.jdk" optional="true" />
+        <module name="sun.jdk.jgss" optional="true" />
+    </dependencies>
+
+</module>
diff --git a/distribution/adapters/wf8-adapter/wf8-modules/src/main/resources/modules/org/keycloak/keycloak-jboss-adapter-core/main/module.xml b/distribution/adapters/wf8-adapter/wf8-modules/src/main/resources/modules/org/keycloak/keycloak-jboss-adapter-core/main/module.xml
new file mode 100755
index 0000000..beac07b
--- /dev/null
+++ b/distribution/adapters/wf8-adapter/wf8-modules/src/main/resources/modules/org/keycloak/keycloak-jboss-adapter-core/main/module.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+
+
+<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-jboss-adapter-core">
+    <resources>
+        <!-- Insert resources here -->
+    </resources>
+    <dependencies>
+        <module name="javax.api"/>
+        <module name="org.jboss.logging"/>
+        <module name="org.picketbox"/>
+        <module name="org.keycloak.keycloak-adapter-core"/>
+        <module name="org.keycloak.keycloak-core"/>
+    </dependencies>
+
+</module>
diff --git a/distribution/adapters/wf8-adapter/wf8-modules/src/main/resources/modules/org/keycloak/keycloak-undertow-adapter/main/module.xml b/distribution/adapters/wf8-adapter/wf8-modules/src/main/resources/modules/org/keycloak/keycloak-undertow-adapter/main/module.xml
new file mode 100755
index 0000000..1772a22
--- /dev/null
+++ b/distribution/adapters/wf8-adapter/wf8-modules/src/main/resources/modules/org/keycloak/keycloak-undertow-adapter/main/module.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+
+
+<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-undertow-adapter">
+    <resources>
+        <!-- Insert resources here -->
+    </resources>
+    <dependencies>
+        <module name="javax.api"/>
+        <module name="org.bouncycastle" />
+        <module name="org.codehaus.jackson.jackson-core-asl"/>
+        <module name="org.codehaus.jackson.jackson-mapper-asl"/>
+        <module name="org.codehaus.jackson.jackson-xc"/>
+        <module name="org.apache.httpcomponents" slot="4.3" />
+        <module name="javax.servlet.api"/>
+        <module name="org.jboss.logging"/>
+        <module name="org.jboss.xnio"/>
+        <module name="io.undertow.core"/>
+        <module name="io.undertow.servlet"/>
+        <module name="org.keycloak.keycloak-adapter-core"/>
+        <module name="org.keycloak.keycloak-core"/>
+    </dependencies>
+
+</module>
diff --git a/distribution/adapters/wf8-adapter/wf8-modules/src/main/resources/modules/org/keycloak/keycloak-wf8-subsystem/main/module.xml b/distribution/adapters/wf8-adapter/wf8-modules/src/main/resources/modules/org/keycloak/keycloak-wf8-subsystem/main/module.xml
new file mode 100755
index 0000000..9fdf2b6
--- /dev/null
+++ b/distribution/adapters/wf8-adapter/wf8-modules/src/main/resources/modules/org/keycloak/keycloak-wf8-subsystem/main/module.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  ~ JBoss, Home of Professional Open Source.
+  ~ Copyright 2014, Red Hat, Inc., and individual contributors
+  ~ as indicated by the @author tags. See the copyright.txt file in the
+  ~ distribution for a full listing of individual contributors.
+  ~
+  ~ This is free software; you can redistribute it and/or modify it
+  ~ under the terms of the GNU Lesser General Public License as
+  ~ published by the Free Software Foundation; either version 2.1 of
+  ~ the License, or (at your option) any later version.
+  ~
+  ~ This software is distributed in the hope that it will be useful,
+  ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
+  ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  ~ Lesser General Public License for more details.
+  ~
+  ~ You should have received a copy of the GNU Lesser General Public
+  ~ License along with this software; if not, write to the Free
+  ~ Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+  ~ 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+  -->
+
+<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-wf8-subsystem">
+    <properties>
+        <property name="keycloak-version" value="${project.version}"/>
+    </properties>
+
+    <resources>
+        <resource-root path="."/>
+        <!-- Insert resources here -->
+    </resources>
+
+    <dependencies>
+        <module name="javax.api"/>
+        <module name="org.jboss.staxmapper"/>
+        <module name="org.jboss.as.controller"/>
+        <module name="org.jboss.as.ee"/>
+        <module name="org.jboss.as.server"/>
+        <module name="org.jboss.modules"/>
+        <module name="org.jboss.msc"/>
+        <module name="org.jboss.logging"/>
+        <module name="org.jboss.vfs"/>
+        <module name="org.jboss.as.web-common"/>
+        <module name="org.jboss.metadata"/>
+    </dependencies>
+</module>
diff --git a/distribution/adapters/wf8-adapter/wf8-modules/src/main/resources/modules/org/keycloak/keycloak-wildfly-adapter/main/module.xml b/distribution/adapters/wf8-adapter/wf8-modules/src/main/resources/modules/org/keycloak/keycloak-wildfly-adapter/main/module.xml
new file mode 100755
index 0000000..2b0e537
--- /dev/null
+++ b/distribution/adapters/wf8-adapter/wf8-modules/src/main/resources/modules/org/keycloak/keycloak-wildfly-adapter/main/module.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+
+
+<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-wildfly-adapter">
+    <resources>
+        <!-- Insert resources here -->
+    </resources>
+    <dependencies>
+        <module name="javax.api"/>
+        <module name="org.bouncycastle" />
+        <module name="org.codehaus.jackson.jackson-core-asl"/>
+        <module name="org.codehaus.jackson.jackson-mapper-asl"/>
+        <module name="org.codehaus.jackson.jackson-xc"/>
+        <module name="org.apache.httpcomponents" slot="4.3" />
+        <module name="javax.servlet.api"/>
+        <module name="org.jboss.logging"/>
+        <module name="io.undertow.core"/>
+        <module name="io.undertow.servlet"/>
+        <module name="org.picketbox"/>
+        <module name="org.keycloak.keycloak-undertow-adapter"/>
+        <module name="org.keycloak.keycloak-adapter-core"/>
+        <module name="org.keycloak.keycloak-core"/>
+    </dependencies>
+
+</module>
diff --git a/distribution/adapters/wildfly-adapter-zip/assembly.xml b/distribution/adapters/wildfly-adapter-zip/assembly.xml
index 9448028..738ad2a 100755
--- a/distribution/adapters/wildfly-adapter-zip/assembly.xml
+++ b/distribution/adapters/wildfly-adapter-zip/assembly.xml
@@ -18,7 +18,7 @@
                 <include>org/keycloak/keycloak-jboss-adapter-core/**</include>
                 <include>org/keycloak/keycloak-undertow-adapter/**</include>
                 <include>org/keycloak/keycloak-wildfly-adapter/**</include>
-                <include>org/keycloak/keycloak-subsystem/**</include>
+                <include>org/keycloak/keycloak-adapter-subsystem/**</include>
             </includes>
             <excludes>
                 <exclude>**/*.war</exclude>
diff --git a/examples/demo-template/admin-access-app/src/main/java/org/keycloak/example/AdminClient.java b/examples/demo-template/admin-access-app/src/main/java/org/keycloak/example/AdminClient.java
index 8b3d40c..24a5cd8 100755
--- a/examples/demo-template/admin-access-app/src/main/java/org/keycloak/example/AdminClient.java
+++ b/examples/demo-template/admin-access-app/src/main/java/org/keycloak/example/AdminClient.java
@@ -7,10 +7,10 @@ import org.apache.http.client.HttpClient;
 import org.apache.http.client.entity.UrlEncodedFormEntity;
 import org.apache.http.client.methods.HttpGet;
 import org.apache.http.client.methods.HttpPost;
+import org.apache.http.impl.client.DefaultHttpClient;
 import org.apache.http.message.BasicNameValuePair;
 import org.keycloak.OAuth2Constants;
 import org.keycloak.constants.ServiceUrlConstants;
-import org.keycloak.adapters.HttpClientBuilder;
 import org.keycloak.representations.AccessTokenResponse;
 import org.keycloak.representations.idm.RoleRepresentation;
 import org.keycloak.util.HostUtils;
@@ -70,8 +70,7 @@ public class AdminClient {
 
     public static AccessTokenResponse getToken(HttpServletRequest request) throws IOException {
 
-        HttpClient client = new HttpClientBuilder()
-                .disableTrustManager().build();
+        HttpClient client = new DefaultHttpClient();
 
 
         try {
@@ -104,8 +103,7 @@ public class AdminClient {
 
     public static void logout(HttpServletRequest request, AccessTokenResponse res) throws IOException {
 
-        HttpClient client = new HttpClientBuilder()
-                .disableTrustManager().build();
+        HttpClient client = new DefaultHttpClient();
 
 
         try {
@@ -135,8 +133,7 @@ public class AdminClient {
 
     public static List<RoleRepresentation> getRealmRoles(HttpServletRequest request, AccessTokenResponse res) throws Failure {
 
-        HttpClient client = new HttpClientBuilder()
-                .disableTrustManager().build();
+        HttpClient client = new DefaultHttpClient();
         try {
             HttpGet get = new HttpGet(getBaseUrl(request) + "/auth/admin/realms/demo/roles");
             get.addHeader("Authorization", "Bearer " + res.getToken());
diff --git a/examples/demo-template/customer-app/src/main/java/org/keycloak/example/AdminClient.java b/examples/demo-template/customer-app/src/main/java/org/keycloak/example/AdminClient.java
index 7b17e18..c111f36 100755
--- a/examples/demo-template/customer-app/src/main/java/org/keycloak/example/AdminClient.java
+++ b/examples/demo-template/customer-app/src/main/java/org/keycloak/example/AdminClient.java
@@ -4,9 +4,9 @@ import org.apache.http.HttpEntity;
 import org.apache.http.HttpResponse;
 import org.apache.http.client.HttpClient;
 import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.client.DefaultHttpClient;
 import org.keycloak.KeycloakSecurityContext;
 import org.keycloak.adapters.AdapterUtils;
-import org.keycloak.adapters.HttpClientBuilder;
 import org.keycloak.representations.idm.RoleRepresentation;
 import org.keycloak.util.JsonSerialization;
 
@@ -40,8 +40,7 @@ public class AdminClient {
     public static List<RoleRepresentation> getRealmRoles(HttpServletRequest req) throws Failure {
         KeycloakSecurityContext session = (KeycloakSecurityContext) req.getAttribute(KeycloakSecurityContext.class.getName());
 
-        HttpClient client = new HttpClientBuilder()
-                .disableTrustManager().build();
+        HttpClient client = new DefaultHttpClient();
         try {
             HttpGet get = new HttpGet(AdapterUtils.getOriginForRestCalls(req.getRequestURL().toString(), session) + "/auth/admin/realms/demo/roles");
             get.addHeader("Authorization", "Bearer " + session.getTokenString());
diff --git a/examples/demo-template/customer-app/src/main/java/org/keycloak/example/CustomerDatabaseClient.java b/examples/demo-template/customer-app/src/main/java/org/keycloak/example/CustomerDatabaseClient.java
index 3a0409b..e9dcbb1 100755
--- a/examples/demo-template/customer-app/src/main/java/org/keycloak/example/CustomerDatabaseClient.java
+++ b/examples/demo-template/customer-app/src/main/java/org/keycloak/example/CustomerDatabaseClient.java
@@ -4,13 +4,11 @@ import org.apache.http.HttpEntity;
 import org.apache.http.HttpResponse;
 import org.apache.http.client.HttpClient;
 import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.client.DefaultHttpClient;
 import org.keycloak.KeycloakSecurityContext;
 import org.keycloak.adapters.AdapterUtils;
-import org.keycloak.adapters.HttpClientBuilder;
-import org.keycloak.constants.ServiceUrlConstants;
 import org.keycloak.representations.IDToken;
 import org.keycloak.util.JsonSerialization;
-import org.keycloak.util.KeycloakUriBuilder;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpSession;
@@ -49,8 +47,7 @@ public class CustomerDatabaseClient {
     public static List<String> getCustomers(HttpServletRequest req) throws Failure {
         KeycloakSecurityContext session = (KeycloakSecurityContext) req.getAttribute(KeycloakSecurityContext.class.getName());
 
-        HttpClient client = new HttpClientBuilder()
-                .disableTrustManager().build();
+        HttpClient client = new DefaultHttpClient();
         try {
             HttpGet get = new HttpGet(AdapterUtils.getOriginForRestCalls(req.getRequestURL().toString(), session) + "/database/customers");
             get.addHeader("Authorization", "Bearer " + session.getTokenString());
diff --git a/examples/demo-template/product-app/src/main/java/org/keycloak/example/oauth/ProductDatabaseClient.java b/examples/demo-template/product-app/src/main/java/org/keycloak/example/oauth/ProductDatabaseClient.java
index f259d0c..3e86343 100755
--- a/examples/demo-template/product-app/src/main/java/org/keycloak/example/oauth/ProductDatabaseClient.java
+++ b/examples/demo-template/product-app/src/main/java/org/keycloak/example/oauth/ProductDatabaseClient.java
@@ -4,9 +4,9 @@ import org.apache.http.HttpEntity;
 import org.apache.http.HttpResponse;
 import org.apache.http.client.HttpClient;
 import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.client.DefaultHttpClient;
 import org.keycloak.KeycloakSecurityContext;
 import org.keycloak.adapters.AdapterUtils;
-import org.keycloak.adapters.HttpClientBuilder;
 import org.keycloak.util.JsonSerialization;
 
 import javax.servlet.http.HttpServletRequest;
@@ -37,8 +37,8 @@ public class ProductDatabaseClient
 
     public static List<String> getProducts(HttpServletRequest req) throws Failure {
         KeycloakSecurityContext session = (KeycloakSecurityContext)req.getAttribute(KeycloakSecurityContext.class.getName());
-        HttpClient client = new HttpClientBuilder()
-                .disableTrustManager().build();
+
+        HttpClient client = new DefaultHttpClient();
         try {
             HttpGet get = new HttpGet(AdapterUtils.getOriginForRestCalls(req.getRequestURL().toString(), session) + "/database/products");
             get.addHeader("Authorization", "Bearer " + session.getTokenString());
diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProviderFactory.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProviderFactory.java
index 166c5bf..c020f51 100755
--- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProviderFactory.java
+++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProviderFactory.java
@@ -24,7 +24,6 @@ import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserFederationEventAwareProviderFactory;
 import org.keycloak.models.UserFederationMapperModel;
 import org.keycloak.models.UserFederationProvider;
-import org.keycloak.models.UserFederationProviderFactory;
 import org.keycloak.models.UserFederationProviderModel;
 import org.keycloak.models.UserFederationSyncResult;
 import org.keycloak.models.UserModel;
@@ -89,7 +88,7 @@ public class LDAPFederationProviderFactory extends UserFederationEventAwareProvi
         String usernameLdapAttribute = ldapConfig.getUsernameLdapAttribute();
 
         UserFederationMapperModel mapperModel;
-        mapperModel = KeycloakModelUtils.createUserFederationMapperModel("usernameMapper", newProviderModel.getId(), UserAttributeLDAPFederationMapperFactory.ID,
+        mapperModel = KeycloakModelUtils.createUserFederationMapperModel("username", newProviderModel.getId(), UserAttributeLDAPFederationMapperFactory.PROVIDER_ID,
                 UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE, UserModel.USERNAME,
                 UserAttributeLDAPFederationMapper.LDAP_ATTRIBUTE, usernameLdapAttribute,
                 UserAttributeLDAPFederationMapper.READ_ONLY, readOnly);
@@ -97,25 +96,25 @@ public class LDAPFederationProviderFactory extends UserFederationEventAwareProvi
 
         // For AD deployments with sAMAccountName is probably more common to map "cn" to full name of user
         if (activeDirectory && usernameLdapAttribute.equalsIgnoreCase(LDAPConstants.SAM_ACCOUNT_NAME)) {
-            mapperModel = KeycloakModelUtils.createUserFederationMapperModel("fullNameMapper", newProviderModel.getId(), FullNameLDAPFederationMapperFactory.ID,
+            mapperModel = KeycloakModelUtils.createUserFederationMapperModel("full name", newProviderModel.getId(), FullNameLDAPFederationMapperFactory.PROVIDER_ID,
                     FullNameLDAPFederationMapper.LDAP_FULL_NAME_ATTRIBUTE, LDAPConstants.CN,
                     UserAttributeLDAPFederationMapper.READ_ONLY, readOnly);
             realm.addUserFederationMapper(mapperModel);
         } else {
-            mapperModel = KeycloakModelUtils.createUserFederationMapperModel("firstNameMapper", newProviderModel.getId(), UserAttributeLDAPFederationMapperFactory.ID,
+            mapperModel = KeycloakModelUtils.createUserFederationMapperModel("first name", newProviderModel.getId(), UserAttributeLDAPFederationMapperFactory.PROVIDER_ID,
                     UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE, UserModel.FIRST_NAME,
                     UserAttributeLDAPFederationMapper.LDAP_ATTRIBUTE, LDAPConstants.CN,
                     UserAttributeLDAPFederationMapper.READ_ONLY, readOnly);
             realm.addUserFederationMapper(mapperModel);
         }
 
-        mapperModel = KeycloakModelUtils.createUserFederationMapperModel("lastNameMapper", newProviderModel.getId(), UserAttributeLDAPFederationMapperFactory.ID,
+        mapperModel = KeycloakModelUtils.createUserFederationMapperModel("last name", newProviderModel.getId(), UserAttributeLDAPFederationMapperFactory.PROVIDER_ID,
                 UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE, UserModel.LAST_NAME,
                 UserAttributeLDAPFederationMapper.LDAP_ATTRIBUTE, LDAPConstants.SN,
                 UserAttributeLDAPFederationMapper.READ_ONLY, readOnly);
         realm.addUserFederationMapper(mapperModel);
 
-        mapperModel = KeycloakModelUtils.createUserFederationMapperModel("emailMapper", newProviderModel.getId(), UserAttributeLDAPFederationMapperFactory.ID,
+        mapperModel = KeycloakModelUtils.createUserFederationMapperModel("email", newProviderModel.getId(), UserAttributeLDAPFederationMapperFactory.PROVIDER_ID,
                 UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE, UserModel.EMAIL,
                 UserAttributeLDAPFederationMapper.LDAP_ATTRIBUTE, LDAPConstants.EMAIL,
                 UserAttributeLDAPFederationMapper.READ_ONLY, readOnly);
@@ -125,14 +124,14 @@ public class LDAPFederationProviderFactory extends UserFederationEventAwareProvi
         String modifyTimestampLdapAttrName = activeDirectory ? "whenChanged" : LDAPConstants.MODIFY_TIMESTAMP;
 
         // map createTimeStamp as read-only
-        mapperModel = KeycloakModelUtils.createUserFederationMapperModel("creationDateMapper", newProviderModel.getId(), UserAttributeLDAPFederationMapperFactory.ID,
+        mapperModel = KeycloakModelUtils.createUserFederationMapperModel("creation date", newProviderModel.getId(), UserAttributeLDAPFederationMapperFactory.PROVIDER_ID,
                 UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE, LDAPConstants.CREATE_TIMESTAMP,
                 UserAttributeLDAPFederationMapper.LDAP_ATTRIBUTE, createTimestampLdapAttrName,
                 UserAttributeLDAPFederationMapper.READ_ONLY, "true");
         realm.addUserFederationMapper(mapperModel);
 
         // map modifyTimeStamp as read-only
-        mapperModel = KeycloakModelUtils.createUserFederationMapperModel("modifyDateMapper", newProviderModel.getId(), UserAttributeLDAPFederationMapperFactory.ID,
+        mapperModel = KeycloakModelUtils.createUserFederationMapperModel("modify date", newProviderModel.getId(), UserAttributeLDAPFederationMapperFactory.PROVIDER_ID,
                 UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE, LDAPConstants.MODIFY_TIMESTAMP,
                 UserAttributeLDAPFederationMapper.LDAP_ATTRIBUTE, modifyTimestampLdapAttrName,
                 UserAttributeLDAPFederationMapper.READ_ONLY, "true");
diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/AbstractLDAPFederationMapperFactory.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/AbstractLDAPFederationMapperFactory.java
index 6b8f186..33ace78 100644
--- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/AbstractLDAPFederationMapperFactory.java
+++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/AbstractLDAPFederationMapperFactory.java
@@ -1,25 +1,65 @@
 package org.keycloak.federation.ldap.mappers;
 
+import java.util.List;
+import java.util.Map;
+
 import org.keycloak.Config;
+import org.keycloak.federation.ldap.LDAPFederationProviderFactory;
+import org.keycloak.mappers.MapperConfigValidationException;
 import org.keycloak.mappers.UserFederationMapperFactory;
 import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.models.UserFederationMapperModel;
+import org.keycloak.provider.ProviderConfigProperty;
 
 /**
  * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
  */
 public abstract class AbstractLDAPFederationMapperFactory implements UserFederationMapperFactory {
 
+    // Used to map attributes from LDAP to UserModel attributes
+    public static final String ATTRIBUTE_MAPPER_CATEGORY = "Attribute Mapper";
+
+    // Used to map roles from LDAP to UserModel users
+    public static final String ROLE_MAPPER_CATEGORY = "Role Mapper";
+
     @Override
     public void init(Config.Scope config) {
     }
 
     @Override
+    public String getFederationProviderType() {
+        return LDAPFederationProviderFactory.PROVIDER_NAME;
+    }
+
+    @Override
     public void postInit(KeycloakSessionFactory factory) {
     }
 
     @Override
+    public List<ProviderConfigProperty> getConfigProperties() {
+        throw new IllegalStateException("Method not supported for this implementation");
+    }
+
+    @Override
     public void close() {
     }
 
+    public static ProviderConfigProperty createConfigProperty(String name, String label, String helpText, String type, Object defaultValue) {
+        ProviderConfigProperty configProperty = new ProviderConfigProperty();
+        configProperty.setName(name);
+        configProperty.setLabel(label);
+        configProperty.setHelpText(helpText);
+        configProperty.setType(type);
+        configProperty.setDefaultValue(defaultValue);
+        return configProperty;
+    }
+
+    protected void checkMandatoryConfigAttribute(String name, String displayName, UserFederationMapperModel mapperModel) throws MapperConfigValidationException {
+        String attrConfigValue = mapperModel.getConfig().get(name);
+        if (attrConfigValue == null || attrConfigValue.trim().isEmpty()) {
+            throw new MapperConfigValidationException("Missing configuration for '" + displayName + "'");
+        }
+    }
+
 
 }
diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/FullNameLDAPFederationMapperFactory.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/FullNameLDAPFederationMapperFactory.java
index 6ef6979..d0d6230 100644
--- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/FullNameLDAPFederationMapperFactory.java
+++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/FullNameLDAPFederationMapperFactory.java
@@ -1,9 +1,14 @@
 package org.keycloak.federation.ldap.mappers;
 
+import java.util.ArrayList;
 import java.util.List;
 
+import org.keycloak.mappers.MapperConfigValidationException;
 import org.keycloak.mappers.UserFederationMapper;
 import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.LDAPConstants;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserFederationMapperModel;
 import org.keycloak.provider.ProviderConfigProperty;
 
 /**
@@ -11,21 +16,48 @@ import org.keycloak.provider.ProviderConfigProperty;
  */
 public class FullNameLDAPFederationMapperFactory extends AbstractLDAPFederationMapperFactory {
 
-    public static final String ID =  "full-name-ldap-mapper";
+    public static final String PROVIDER_ID =  "full-name-ldap-mapper";
+
+    protected static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
+
+    static {
+        ProviderConfigProperty userModelAttribute = createConfigProperty(FullNameLDAPFederationMapper.LDAP_FULL_NAME_ATTRIBUTE, "LDAP Full Name Attribute",
+                "Name of LDAP attribute, which contains fullName of user. In most cases it will be 'cn' ", ProviderConfigProperty.STRING_TYPE, LDAPConstants.CN);
+        configProperties.add(userModelAttribute);
+
+        ProviderConfigProperty readOnly = createConfigProperty(UserAttributeLDAPFederationMapper.READ_ONLY, "Read Only",
+                "For Read-only is data imported from LDAP to Keycloak DB, but it's not saved back to LDAP when user is updated in Keycloak.", ProviderConfigProperty.BOOLEAN_TYPE, "false");
+        configProperties.add(readOnly);
+    }
 
     @Override
     public String getHelpText() {
-        return "Some help text - full name mapper - TODO";
+        return "Used to map full-name of user from single attribute in LDAP (usually 'cn' attribute) to firstName and lastName attributes of UserModel in Keycloak DB";
+    }
+
+    @Override
+    public String getDisplayCategory() {
+        return ATTRIBUTE_MAPPER_CATEGORY;
     }
 
     @Override
-    public List<ProviderConfigProperty> getConfigProperties() {
-        return null;
+    public String getDisplayType() {
+        return "Full Name";
+    }
+
+    @Override
+    public List<ProviderConfigProperty> getConfigProperties(RealmModel realm) {
+        return configProperties;
     }
 
     @Override
     public String getId() {
-        return ID;
+        return PROVIDER_ID;
+    }
+
+    @Override
+    public void validateConfig(UserFederationMapperModel mapperModel) throws MapperConfigValidationException {
+        checkMandatoryConfigAttribute(FullNameLDAPFederationMapper.LDAP_FULL_NAME_ATTRIBUTE, "LDAP Full Name Attribute", mapperModel);
     }
 
     @Override
diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/RoleLDAPFederationMapper.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/RoleLDAPFederationMapper.java
index 47f288d..084b255 100644
--- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/RoleLDAPFederationMapper.java
+++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/RoleLDAPFederationMapper.java
@@ -178,7 +178,6 @@ public class RoleLDAPFederationMapper extends AbstractLDAPFederationMapper {
         }
         String[] objClasses = objectClasses.split(",");
 
-        // TODO: util method for trim and convert array to collection?
         Set<String> trimmed = new HashSet<String>();
         for (String objectClass : objClasses) {
             objectClass = objectClass.trim();
diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/RoleLDAPFederationMapperFactory.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/RoleLDAPFederationMapperFactory.java
index cbae850..a5eadd9 100644
--- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/RoleLDAPFederationMapperFactory.java
+++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/RoleLDAPFederationMapperFactory.java
@@ -1,9 +1,18 @@
 package org.keycloak.federation.ldap.mappers;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.LinkedList;
 import java.util.List;
+import java.util.Map;
 
+import org.keycloak.mappers.MapperConfigValidationException;
 import org.keycloak.mappers.UserFederationMapper;
+import org.keycloak.models.ClientModel;
 import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.LDAPConstants;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserFederationMapperModel;
 import org.keycloak.provider.ProviderConfigProperty;
 
 /**
@@ -11,21 +20,95 @@ import org.keycloak.provider.ProviderConfigProperty;
  */
 public class RoleLDAPFederationMapperFactory extends AbstractLDAPFederationMapperFactory {
 
-    public static final String ID = "role-ldap-mapper";
+    public static final String PROVIDER_ID = "role-ldap-mapper";
+
+    protected static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
+
+    static {
+        ProviderConfigProperty rolesDn = createConfigProperty(RoleLDAPFederationMapper.ROLES_DN, "LDAP Roles DN",
+                "LDAP DN where are roles of this tree saved. For example 'ou=finance,dc=example,dc=org' ", ProviderConfigProperty.STRING_TYPE, null);
+        configProperties.add(rolesDn);
+
+        ProviderConfigProperty roleNameLDAPAttribute = createConfigProperty(RoleLDAPFederationMapper.ROLE_NAME_LDAP_ATTRIBUTE, "Role Name LDAP Attribute",
+                "Name of LDAP attribute, which is used in role objects for name and RDN of role. Usually it will be 'cn' . In this case typical group/role object may have DN like 'cn=role1,ou=finance,dc=example,dc=org' ",
+                ProviderConfigProperty.STRING_TYPE, LDAPConstants.CN);
+        configProperties.add(roleNameLDAPAttribute);
+
+        ProviderConfigProperty membershipLDAPAttribute = createConfigProperty(RoleLDAPFederationMapper.MEMBERSHIP_LDAP_ATTRIBUTE, "Membership LDAP Attribute",
+                "Name of LDAP attribute on role, which is used for membership mappings. Usually it will be 'member' ",
+                ProviderConfigProperty.STRING_TYPE, LDAPConstants.MEMBER);
+        configProperties.add(membershipLDAPAttribute);
+
+        ProviderConfigProperty roleObjectClasses = createConfigProperty(RoleLDAPFederationMapper.ROLE_OBJECT_CLASSES, "Role Object Classes",
+                "Object classes of the role object divided by comma (if more values needed). In typical LDAP deployment it could be 'groupOfNames' or 'groupOfEntries' ",
+                ProviderConfigProperty.STRING_TYPE, LDAPConstants.GROUP_OF_NAMES);
+        configProperties.add(roleObjectClasses);
+
+        List<String> modes = new LinkedList<String>();
+        for (RoleLDAPFederationMapper.Mode mode : RoleLDAPFederationMapper.Mode.values()) {
+            modes.add(mode.toString());
+        }
+        ProviderConfigProperty mode = createConfigProperty(RoleLDAPFederationMapper.MODE, "Mode",
+                "LDAP_ONLY means that all role mappings are retrieved from LDAP and saved into LDAP. READ_ONLY is Read-only LDAP mode where role mappings are " +
+                        "retrieved from both LDAP and DB and merged together. New role grants are not saved to LDAP but to DB. IMPORT is Read-only LDAP mode where role mappings are retrieved from LDAP just at the time when user is imported from LDAP and then " +
+                        "they are saved to local keycloak DB.",
+                ProviderConfigProperty.LIST_TYPE, modes);
+        configProperties.add(mode);
+
+        ProviderConfigProperty useRealmRolesMappings = createConfigProperty(RoleLDAPFederationMapper.USE_REALM_ROLES_MAPPING, "Use Realm Roles Mapping",
+                "If true, then LDAP role mappings will be mapped to realm role mappings in Keycloak. Otherwise it will be mapped to client role mappings", ProviderConfigProperty.BOOLEAN_TYPE, "true");
+        configProperties.add(useRealmRolesMappings);
+
+        // NOTE: ClientID will be computed dynamically from available clients
+    }
 
     @Override
     public String getHelpText() {
-        return "Some help text - role mapper - TODO";
+        return "Used to map role mappings of roles from some LDAP DN to Keycloak role mappings of either realm roles or client roles of particular client";
+    }
+
+    @Override
+    public String getDisplayCategory() {
+        return ROLE_MAPPER_CATEGORY;
     }
 
     @Override
-    public List<ProviderConfigProperty> getConfigProperties() {
-        return null;
+    public String getDisplayType() {
+        return "Role mappings";
+    }
+
+    @Override
+    public List<ProviderConfigProperty> getConfigProperties(RealmModel realm) {
+        List<ProviderConfigProperty> props = new ArrayList<ProviderConfigProperty>(configProperties);
+
+        Map<String, ClientModel> clients = realm.getClientNameMap();
+        List<String> clientIds = new ArrayList<String>(clients.keySet());
+
+        ProviderConfigProperty clientIdProperty = createConfigProperty(RoleLDAPFederationMapper.CLIENT_ID, "Client ID",
+                "Client ID of client to which LDAP role mappings will be mapped. Applicable just if 'Use Realm Roles Mapping' is false",
+                ProviderConfigProperty.LIST_TYPE, clientIds);
+        props.add(clientIdProperty);
+
+        return props;
     }
 
     @Override
     public String getId() {
-        return ID ;
+        return PROVIDER_ID;
+    }
+
+    @Override
+    public void validateConfig(UserFederationMapperModel mapperModel) throws MapperConfigValidationException {
+        checkMandatoryConfigAttribute(RoleLDAPFederationMapper.ROLES_DN, "LDAP Roles DN", mapperModel);
+
+        String realmMappings = mapperModel.getConfig().get(RoleLDAPFederationMapper.USE_REALM_ROLES_MAPPING);
+        boolean useRealmMappings = Boolean.parseBoolean(realmMappings);
+        if (!useRealmMappings) {
+            String clientId = mapperModel.getConfig().get(RoleLDAPFederationMapper.CLIENT_ID);
+            if (clientId == null || clientId.trim().isEmpty()) {
+                throw new MapperConfigValidationException("Client ID needs to be provided in config when Realm Roles Mapping is not used");
+            }
+        }
     }
 
     @Override
diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/UserAttributeLDAPFederationMapperFactory.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/UserAttributeLDAPFederationMapperFactory.java
index 564b012..c0b9d79 100644
--- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/UserAttributeLDAPFederationMapperFactory.java
+++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/UserAttributeLDAPFederationMapperFactory.java
@@ -1,9 +1,13 @@
 package org.keycloak.federation.ldap.mappers;
 
+import java.util.ArrayList;
 import java.util.List;
 
+import org.keycloak.mappers.MapperConfigValidationException;
 import org.keycloak.mappers.UserFederationMapper;
 import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserFederationMapperModel;
 import org.keycloak.provider.ProviderConfigProperty;
 
 /**
@@ -11,21 +15,52 @@ import org.keycloak.provider.ProviderConfigProperty;
  */
 public class UserAttributeLDAPFederationMapperFactory extends AbstractLDAPFederationMapperFactory {
 
-    public static final String ID = "user-attribute-ldap-mapper";
+    public static final String PROVIDER_ID = "user-attribute-ldap-mapper";
+    protected static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
+
+    static {
+        ProviderConfigProperty userModelAttribute = createConfigProperty(UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE, "User Model Attribute",
+                "Name of mapped UserModel property or UserModel attribute in Keycloak DB. For example 'firstName', 'lastName, 'email', 'street' etc.", ProviderConfigProperty.STRING_TYPE, null);
+        configProperties.add(userModelAttribute);
+
+        ProviderConfigProperty ldapAttribute = createConfigProperty(UserAttributeLDAPFederationMapper.LDAP_ATTRIBUTE, "LDAP Attribute",
+                "Name of mapped attribute on LDAP object. For example 'cn', 'sn, 'mail', 'street' etc.", ProviderConfigProperty.STRING_TYPE, null);
+        configProperties.add(ldapAttribute);
+
+        ProviderConfigProperty readOnly = createConfigProperty(UserAttributeLDAPFederationMapper.READ_ONLY, "Read Only",
+                "Read-only attribute is imported from LDAP to Keycloak DB, but it's not saved back to LDAP when user is updated in Keycloak.", ProviderConfigProperty.BOOLEAN_TYPE, "false");
+        configProperties.add(readOnly);
+    }
 
     @Override
     public String getHelpText() {
-        return "Some help text TODO";
+        return "Used to map single attribute from LDAP user to attribute of UserModel in Keycloak DB";
+    }
+
+    @Override
+    public String getDisplayCategory() {
+        return ATTRIBUTE_MAPPER_CATEGORY;
     }
 
     @Override
-    public List<ProviderConfigProperty> getConfigProperties() {
-        return null;
+    public String getDisplayType() {
+        return "User Attribute";
+    }
+
+    @Override
+    public List<ProviderConfigProperty> getConfigProperties(RealmModel realm) {
+        return configProperties;
     }
 
     @Override
     public String getId() {
-        return ID;
+        return PROVIDER_ID;
+    }
+
+    @Override
+    public void validateConfig(UserFederationMapperModel mapperModel) throws MapperConfigValidationException {
+        checkMandatoryConfigAttribute(UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE, "User Model Attribute", mapperModel);
+        checkMandatoryConfigAttribute(UserAttributeLDAPFederationMapper.LDAP_ATTRIBUTE, "LDAP Attribute", mapperModel);
     }
 
     @Override
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/app.js b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/app.js
index 56f6008..7a0ae1a 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/app.js
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/app.js
@@ -952,6 +952,58 @@ module.config([ '$routeProvider', function($routeProvider) {
             },
             controller : 'GenericUserFederationCtrl'
         })
+        .when('/realms/:realm/user-federation/providers/:provider/:instance/mappers', {
+            templateUrl : function(params){ return resourceUrl + '/partials/federated-mappers.html'; },
+            resolve : {
+                realm : function(RealmLoader) {
+                    return RealmLoader();
+                },
+                provider : function(UserFederationInstanceLoader) {
+                    return UserFederationInstanceLoader();
+                },
+                mapperTypes : function(UserFederationMapperTypesLoader) {
+                    return UserFederationMapperTypesLoader();
+                },
+                mappers : function(UserFederationMappersLoader) {
+                    return UserFederationMappersLoader();
+                }
+            },
+            controller : 'UserFederationMapperListCtrl'
+        })
+        .when('/realms/:realm/user-federation/providers/:provider/:instance/mappers/:mapperId', {
+            templateUrl : function(params){ return resourceUrl + '/partials/federated-mapper-detail.html'; },
+            resolve : {
+                realm : function(RealmLoader) {
+                    return RealmLoader();
+                },
+                provider : function(UserFederationInstanceLoader) {
+                    return UserFederationInstanceLoader();
+                },
+                mapperTypes : function(UserFederationMapperTypesLoader) {
+                    return UserFederationMapperTypesLoader();
+                },
+                mapper : function(UserFederationMapperLoader) {
+                    return UserFederationMapperLoader();
+                }
+            },
+            controller : 'UserFederationMapperCtrl'
+        })
+        .when('/create/user-federation-mappers/:realm/:provider/:instance', {
+            templateUrl : function(params){ return resourceUrl + '/partials/federated-mapper-detail.html'; },
+            resolve : {
+                realm : function(RealmLoader) {
+                    return RealmLoader();
+                },
+                provider : function(UserFederationInstanceLoader) {
+                    return UserFederationInstanceLoader();
+                },
+                mapperTypes : function(UserFederationMapperTypesLoader) {
+                    return UserFederationMapperTypesLoader();
+                },
+            },
+            controller : 'UserFederationMapperCreateCtrl'
+        })
+
         .when('/realms/:realm/defense/headers', {
             templateUrl : resourceUrl + '/partials/defense-headers.html',
             resolve : {
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js
index 2444be4..bc723f3 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js
@@ -511,8 +511,8 @@ module.controller('GenericUserFederationCtrl', function($scope, $location, Notif
     }
 
     function triggerSync(action) {
-        UserFederationSync.get({ action: action, realm: $scope.realm.realm, provider: $scope.instance.id }, function() {
-            Notifications.success("Sync of users finished successfully");
+        UserFederationSync.save({ action: action, realm: $scope.realm.realm, provider: $scope.instance.id }, {}, function(syncResult) {
+            Notifications.success("Sync of users finished successfully. " + syncResult.status);
         }, function() {
             Notifications.error("Error during sync of users");
         });
@@ -734,3 +734,128 @@ module.controller('LDAPCtrl', function($scope, $location, $route, Notifications,
 
 });
 
+
+module.controller('UserFederationMapperListCtrl', function($scope, $location, Notifications, $route, Dialog, realm, provider, mapperTypes, mappers) {
+    console.log('UserFederationMapperListCtrl');
+
+    $scope.realm = realm;
+    $scope.provider = provider;
+
+    $scope.mapperTypes = mapperTypes;
+    $scope.mappers = mappers;
+
+    $scope.hasAnyMapperTypes = false;
+    for (var property in mapperTypes) {
+        if (!(property.startsWith('$'))) {
+            $scope.hasAnyMapperTypes = true;
+            break;
+        }
+    }
+
+});
+
+module.controller('UserFederationMapperCtrl', function($scope, realm,  provider, mapperTypes, mapper, UserFederationMapper, Notifications, Dialog, $location) {
+    console.log('UserFederationMapperCtrl');
+    $scope.realm = realm;
+    $scope.provider = provider;
+    $scope.create = false;
+    $scope.mapper = angular.copy(mapper);
+    $scope.changed = false;
+    $scope.mapperType = mapperTypes[mapper.federationMapperType];
+
+    $scope.$watch('mapper', function() {
+        if (!angular.equals($scope.mapper, mapper)) {
+            $scope.changed = true;
+        }
+    }, true);
+
+    $scope.save = function() {
+        UserFederationMapper.update({
+            realm : realm.realm,
+            provider: provider.id,
+            mapperId : mapper.id
+        }, $scope.mapper, function() {
+            $scope.changed = false;
+            mapper = angular.copy($scope.mapper);
+            $location.url("/realms/" + realm.realm + '/user-federation/providers/' + provider.providerName + '/' + provider.id + '/mappers/' + mapper.id);
+            Notifications.success("Your changes have been saved.");
+        }, function(error) {
+            if (error.status == 400) {
+                Notifications.error('Error in configuration of mapper: ' + error.data.error_description);
+            } else {
+                Notification.error('Unexpected error when creating mapper');
+            }
+        });
+    };
+
+    $scope.reset = function() {
+        $scope.mapper = angular.copy(mapper);
+        $scope.changed = false;
+    };
+
+    $scope.cancel = function() {
+        window.history.back();
+    };
+
+    $scope.remove = function() {
+        Dialog.confirmDelete($scope.mapper.name, 'mapper', function() {
+            UserFederationMapper.remove({ realm: realm.realm, provider: provider.id, mapperId : $scope.mapper.id }, function() {
+                Notifications.success("The mapper has been deleted.");
+                $location.url("/realms/" + realm.realm + '/user-federation/providers/' + provider.providerName + '/' + provider.id + '/mappers');
+            });
+        });
+    };
+
+});
+
+module.controller('UserFederationMapperCreateCtrl', function($scope, realm, provider, mapperTypes, UserFederationMapper, Notifications, Dialog, $location) {
+    console.log('UserFederationMapperCreateCtrl');
+    $scope.realm = realm;
+    $scope.provider = provider;
+    $scope.create = true;
+    $scope.mapper = { federationProviderDisplayName: provider.displayName, config: {}};
+    $scope.mapperTypes = mapperTypes;
+    $scope.mapperType = null;
+
+    $scope.$watch('mapperType', function() {
+        if ($scope.mapperType != null) {
+            $scope.mapper.config = {};
+            for ( var i = 0; i < $scope.mapperType.properties.length; i++) {
+                var property = $scope.mapperType.properties[i];
+                if (property.type === 'String' || property.type === 'boolean') {
+                    $scope.mapper.config[ property.name ] = property.defaultValue;
+                }
+            }
+        }
+    }, true);
+
+    $scope.save = function() {
+        if ($scope.mapperType == null) {
+            Notifications.error("You need to select mapper type!");
+            return;
+        }
+
+        $scope.mapper.federationMapperType = $scope.mapperType.id;
+        UserFederationMapper.save({
+            realm : realm.realm, provider: provider.id
+        }, $scope.mapper, function(data, headers) {
+            var l = headers().location;
+            var id = l.substring(l.lastIndexOf("/") + 1);
+            $location.url('/realms/' + realm.realm +'/user-federation/providers/' + provider.providerName + '/' + provider.id + '/mappers/' + id);
+            Notifications.success("Mapper has been created.");
+        }, function(error) {
+            if (error.status == 400) {
+                Notifications.error('Error in configuration of mapper: ' + error.data.error_description);
+            } else {
+                Notification.error('Unexpected error when creating mapper');
+            }
+        });
+    };
+
+    $scope.cancel = function() {
+        window.history.back();
+    };
+
+
+});
+
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/loaders.js b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/loaders.js
index 3f72ffe..3a492bb 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/loaders.js
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/loaders.js
@@ -116,6 +116,34 @@ module.factory('UserFederationFactoryLoader', function(Loader, UserFederationPro
     });
 });
 
+module.factory('UserFederationMapperTypesLoader', function(Loader, UserFederationMapperTypes, $route, $q) {
+    return Loader.get(UserFederationMapperTypes, function () {
+        return {
+            realm: $route.current.params.realm,
+            provider: $route.current.params.instance
+        }
+    });
+});
+
+module.factory('UserFederationMappersLoader', function(Loader, UserFederationMappers, $route, $q) {
+    return Loader.query(UserFederationMappers, function () {
+        return {
+            realm: $route.current.params.realm,
+            provider: $route.current.params.instance
+        }
+    });
+});
+
+module.factory('UserFederationMapperLoader', function(Loader, UserFederationMapper, $route, $q) {
+    return Loader.get(UserFederationMapper, function () {
+        return {
+            realm: $route.current.params.realm,
+            provider: $route.current.params.instance,
+            mapperId: $route.current.params.mapperId
+        }
+    });
+});
+
 
 module.factory('UserSessionStatsLoader', function(Loader, UserSessionStats, $route, $q) {
     return Loader.get(UserSessionStats, function() {
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/services.js b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/services.js
index e9f09a2..f192516 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/services.js
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/services.js
@@ -238,7 +238,33 @@ module.factory('UserFederationProviders', function($resource) {
 });
 
 module.factory('UserFederationSync', function($resource) {
-    return $resource(authUrl + '/admin/realms/:realm/user-federation/sync/:provider');
+    return $resource(authUrl + '/admin/realms/:realm/user-federation/instances/:provider/sync');
+});
+
+module.factory('UserFederationMapperTypes', function($resource) {
+    return $resource(authUrl + '/admin/realms/:realm/user-federation/instances/:provider/mapper-types', {
+        realm : '@realm',
+        provider : '@provider'
+    });
+});
+
+module.factory('UserFederationMappers', function($resource) {
+    return $resource(authUrl + '/admin/realms/:realm/user-federation/instances/:provider/mappers', {
+        realm : '@realm',
+        provider : '@provider'
+    });
+});
+
+module.factory('UserFederationMapper', function($resource) {
+    return $resource(authUrl + '/admin/realms/:realm/user-federation/instances/:provider/mappers/:mapperId', {
+        realm : '@realm',
+        provider : '@provider',
+        mapperId: '@mapperId'
+    }, {
+        update: {
+            method : 'PUT'
+        }
+    });
 });
 
 
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/federated-generic.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/federated-generic.html
index b2c7da1..f0d8774 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/federated-generic.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/federated-generic.html
@@ -5,8 +5,13 @@
         <li data-ng-show="create">Add User Federation Provider</li>
     </ol>
 
-    <h1 data-ng-hide="create"><strong>User Federation Provider</strong> {{instance.displayName|capitalize}}</h1>
-    <h1 data-ng-show="create"><strong>Add User Federation Provider</strong></h1>
+    <h1 data-ng-hide="create"><strong>{{instance.providerName|capitalize}} User Federation Provider</strong> {{instance.displayName|capitalize}}</h1>
+    <h1 data-ng-show="create"><strong>Add {{instance.providerName|capitalize}} User Federation Provider</strong></h1>
+
+    <ul class="nav nav-tabs" data-ng-hide="create">
+        <li class="active"><a href="#/realms/{{realm.realm}}/user-federation/providers/{{instance.providerName}}/{{instance.id}}">Settings</a></li>
+        <li><a href="#/realms/{{realm.realm}}/user-federation/providers/{{instance.providerName}}/{{instance.id}}/mappers">Mappers</a></li>
+    </ul>
 
     <form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageRealm">
         <fieldset>
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/federated-kerberos.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/federated-kerberos.html
index 2294abb..b2f4701 100644
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/federated-kerberos.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/federated-kerberos.html
@@ -8,6 +8,11 @@
     <h1 data-ng-hide="create"><strong>Kerberos User Federation Provider</strong> {{instance.displayName|capitalize}}</h1>
     <h1 data-ng-show="create"><strong>Add Kerberos User Federation Provider</strong></h1>
 
+    <ul class="nav nav-tabs" data-ng-hide="create">
+        <li class="active"><a href="#/realms/{{realm.realm}}/user-federation/providers/{{instance.providerName}}/{{instance.id}}">Settings</a></li>
+        <li><a href="#/realms/{{realm.realm}}/user-federation/providers/{{instance.providerName}}/{{instance.id}}/mappers">Mappers</a></li>
+    </ul>
+
     <form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageRealm">
         <fieldset>
             <legend><span class="text">Required Settings</span></legend>
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/federated-ldap.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/federated-ldap.html
index ec916a0..2b86f09 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/federated-ldap.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/federated-ldap.html
@@ -8,6 +8,11 @@
     <h1 data-ng-hide="create"><strong>LDAP User Federation Provider</strong> {{instance.displayName|capitalize}}</h1>
     <h1 data-ng-show="create"><strong>Add LDAP User Federation Provider</strong></h1>
 
+    <ul class="nav nav-tabs" data-ng-hide="create">
+        <li class="active"><a href="#/realms/{{realm.realm}}/user-federation/providers/{{instance.providerName}}/{{instance.id}}">Settings</a></li>
+        <li><a href="#/realms/{{realm.realm}}/user-federation/providers/{{instance.providerName}}/{{instance.id}}/mappers">Mappers</a></li>
+    </ul>
+
     <form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageRealm">
 
         <fieldset>
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/federated-mapper-detail.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/federated-mapper-detail.html
new file mode 100644
index 0000000..0b9c144
--- /dev/null
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/federated-mapper-detail.html
@@ -0,0 +1,78 @@
+<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
+    <ol class="breadcrumb">
+        <li><a href="#/realms/{{realm.realm}}/user-federation">User Federation</a></li>
+        <li><a href="#/realms/{{realm.realm}}/user-federation/providers/{{provider.providerName}}/{{provider.id}}">{{provider.displayName|capitalize}}</a></li>
+        <li><a href="#/realms/{{realm.realm}}/user-federation/providers/{{provider.providerName}}/{{provider.id}}/mappers">User Federation Mappers</a></li>
+        <li class="active" data-ng-show="create">Create User Federation Mapper</li>
+        <li class="active" data-ng-hide="create">{{mapper.name}}</li>
+    </ol>
+
+    <h1 data-ng-hide="create"><strong>User Federation Mapper</strong> {{mapper.name}}</h1>
+    <h1 data-ng-show="create"><strong>Add User Federation Mapper</strong></h1>
+
+    <form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageRealm">
+        <fieldset>
+            <div class="form-group clearfix" data-ng-show="!create">
+                <label class="col-md-2 control-label" for="mapperId">ID </label>
+                <div class="col-md-6">
+                    <input class="form-control" id="mapperId" type="text" ng-model="mapper.id" readonly>
+                </div>
+            </div>
+            <div class="form-group clearfix">
+                <label class="col-md-2 control-label" for="name">Name <span class="required">*</span></label>
+                <div class="col-md-6">
+                    <input class="form-control" id="name" type="text" ng-model="mapper.name" data-ng-readonly="!create" required>
+                </div>
+                <kc-tooltip>Name of the mapper.</kc-tooltip>
+            </div>
+            <div class="form-group" data-ng-show="create">
+                <label class="col-md-2 control-label" for="mapperTypeCreate">Mapper Type</label>
+                <div class="col-sm-6">
+                    <div>
+                        <select class="form-control" id="mapperTypeCreate"
+                                ng-model="mapperType"
+                                ng-options="mapperType.name for (mapperKey, mapperType) in mapperTypes">
+                        </select>
+                    </div>
+                </div>
+                <kc-tooltip>{{mapperType.helpText}}</kc-tooltip>
+            </div>
+            <div class="form-group clearfix" data-ng-hide="create">
+                <label class="col-md-2 control-label" for="mapperType">Mapper Type</label>
+                <div class="col-md-6">
+                    <input class="form-control" id="mapperType" type="text" ng-model="mapperType.name" data-ng-readonly="true">
+                </div>
+                <kc-tooltip>{{mapperType.helpText}}</kc-tooltip>
+            </div>
+            <div data-ng-repeat="option in mapperType.properties" class="form-group">
+                <label class="col-md-2 control-label">{{option.label}}</label>
+
+                <div class="col-sm-4" data-ng-hide="option.type == 'boolean' || option.type == 'List'">
+                    <input class="form-control" type="text" data-ng-model="mapper.config[ option.name ]">
+                </div>
+                <div class="col-sm-4" data-ng-show="option.type == 'boolean'">
+                    <input ng-model="mapper.config[ option.name ]" value="'true'" name="option.name" id="option.name" onoffswitchmodel />
+                </div>
+                <div class="col-sm-4" data-ng-show="option.type == 'List'">
+                    <select ng-model="mapper.config[ option.name ]" ng-options="data for data in option.defaultValue">
+                        <option value="" selected> Select one... </option>
+                    </select>
+                </div>
+                <kc-tooltip>{{option.helpText}}</kc-tooltip>
+            </div>
+
+        </fieldset>
+        <div class="pull-right form-actions" data-ng-show="create && access.manageRealm">
+            <button kc-cancel data-ng-click="cancel()">Cancel</button>
+            <button kc-save>Save</button>
+        </div>
+
+        <div class="pull-right form-actions" data-ng-show="!create && access.manageRealm">
+            <button kc-reset data-ng-show="changed">Clear changes</button>
+            <button kc-save  data-ng-show="changed">Save</button>
+            <button kc-delete data-ng-click="remove()" data-ng-hide="changed">Delete</button>
+        </div>
+    </form>
+</div>
+
+<kc-menu></kc-menu>
\ No newline at end of file
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/federated-mappers.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/federated-mappers.html
new file mode 100644
index 0000000..d650100
--- /dev/null
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/federated-mappers.html
@@ -0,0 +1,53 @@
+<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
+    <ol class="breadcrumb">
+        <li><a href="#/realms/{{realm.realm}}/user-federation">User Federation</a></li>
+        <li><a href="#/realms/{{realm.realm}}/user-federation/providers/{{provider.providerName}}/{{provider.id}}">{{provider.displayName|capitalize}}</a></li>
+        <li>User Federation Mappers</li>
+    </ol>
+
+    <h1><strong>{{provider.providerName === 'ldap' ? 'LDAP' : (provider.providerName|capitalize)}} User Federation Provider</strong> {{provider.displayName|capitalize}}</h1>
+
+    <ul class="nav nav-tabs" data-ng-hide="create">
+        <li><a href="#/realms/{{realm.realm}}/user-federation/providers/{{provider.providerName}}/{{provider.id}}">Settings</a></li>
+        <li class="active"><a href="#/realms/{{realm.realm}}/user-federation/providers/{{provider.providerName}}/{{provider.id}}/mappers">Mappers</a></li>
+    </ul>
+
+    <table class="table table-striped table-bordered">
+        <thead>
+        <tr>
+            <th class="kc-table-actions" colspan="4">
+                <div class="form-inline">
+                    <div class="form-group">
+                        <div class="input-group">
+                            <input type="text" placeholder="Search..." data-ng-model="search.name" class="form-control search" onkeyup="if(event.keyCode == 13){$(this).next('I').click();}">
+                            <div class="input-group-addon">
+                                <i class="fa fa-search" type="submit"></i>
+                            </div>
+                        </div>
+                    </div>
+                    <div class="pull-right" data-ng-show="hasAnyMapperTypes">
+                        <a class="btn btn-primary" href="#/create/user-federation-mappers/{{realm.realm}}/{{provider.providerName}}/{{provider.id}}">Create</a>
+                    </div>
+                </div>
+            </th>
+        </tr>
+        <tr data-ng-hide="mappers.length == 0">
+            <th>Name</th>
+            <th>Category</th>
+            <th>Type</th>
+        </tr>
+        </thead>
+        <tbody>
+        <tr ng-repeat="mapper in mappers | filter:search">
+            <td><a href="#/realms/{{realm.realm}}/user-federation/providers/{{provider.providerName}}/{{provider.id}}/mappers/{{mapper.id}}">{{mapper.name}}</a></td>
+            <td>{{mapperTypes[mapper.federationMapperType].category}}</td>
+            <td>{{mapperTypes[mapper.federationMapperType].name}}</td>
+        </tr>
+        <tr data-ng-show="mappers.length == 0">
+            <td>No mappers available</td>
+        </tr>
+        </tbody>
+    </table>
+</div>
+
+<kc-menu></kc-menu>
\ No newline at end of file
diff --git a/integration/wildfly/pom.xml b/integration/wildfly/pom.xml
index 16a4d03..3e370c6 100644
--- a/integration/wildfly/pom.xml
+++ b/integration/wildfly/pom.xml
@@ -18,5 +18,6 @@
         <module>wildfly-extensions</module>
         <module>wildfly-server-subsystem</module>
         <module>wildfly-adapter-subsystem</module>
+        <module>wf8-subsystem</module>
     </modules>
 </project>
\ No newline at end of file
diff --git a/integration/wildfly/wf8-subsystem/pom.xml b/integration/wildfly/wf8-subsystem/pom.xml
new file mode 100755
index 0000000..1d72912
--- /dev/null
+++ b/integration/wildfly/wf8-subsystem/pom.xml
@@ -0,0 +1,115 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+~ Copyright 2013 JBoss Inc
+~
+~ Licensed under the Apache License, Version 2.0 (the "License");
+~ you may not use this file except in compliance with the License.
+~ You may obtain a copy of the License at
+~
+~       http://www.apache.org/licenses/LICENSE-2.0
+~
+~ Unless required by applicable law or agreed to in writing, software
+~ distributed under the License is distributed on an "AS IS" BASIS,
+~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+~ See the License for the specific language governing permissions and
+~ limitations under the License.
+-->
+<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/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.keycloak</groupId>
+        <artifactId>keycloak-parent</artifactId>
+        <version>1.3.0.Final-SNAPSHOT</version>
+        <relativePath>../../../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>keycloak-wf8-subsystem</artifactId>
+    <name>Keycloak Adapter Subsystem</name>
+    <description/>
+    <packaging>jar</packaging>
+
+    <properties>
+        <wildfly.version>8.2.0.Final</wildfly.version>
+        <wildfly.core.version>8.2.0.Final</wildfly.core.version>
+    </properties>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <redirectTestOutputToFile>false</redirectTestOutputToFile>
+                    <enableAssertions>true</enableAssertions>
+                    <systemProperties>
+                        <property>
+                            <name>jboss.home</name>
+                            <value>${jboss.home}</value>
+                        </property>
+                    </systemProperties>
+                    <includes>
+                        <include>**/*TestCase.java</include>
+                    </includes>
+                </configuration>
+            </plugin>            
+        </plugins>
+    </build>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.wildfly</groupId>
+            <artifactId>wildfly-controller</artifactId>
+            <version>${wildfly.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.wildfly</groupId>
+            <artifactId>wildfly-server</artifactId>
+            <version>${wildfly.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.wildfly</groupId>
+            <artifactId>wildfly-web-common</artifactId>
+            <version>${wildfly.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.jboss.logging</groupId>
+            <artifactId>jboss-logging-annotations</artifactId>
+            <version>${jboss-logging-tools.version}</version>
+            <!-- This is a compile-time dependency of this project, but is not needed at compile or runtime by other
+            projects that depend on this project.-->
+            <scope>provided</scope>
+            <optional>true</optional>
+        </dependency>
+
+        <dependency>
+            <groupId>org.jboss.logging</groupId>
+            <artifactId>jboss-logging-processor</artifactId>
+            <version>${jboss-logging-tools.version}</version>
+            <!-- This is a compile-time dependency of this project, but is not needed at compile or runtime by other
+            projects that depend on this project.-->
+            <scope>provided</scope>
+            <optional>true</optional>
+        </dependency>
+
+        <dependency>
+            <groupId>org.wildfly</groupId>
+            <artifactId>wildfly-subsystem-test-framework</artifactId>
+            <version>${wildfly.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-wildfly-adapter</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/integration/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/extension/CredentialAddHandler.java b/integration/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/extension/CredentialAddHandler.java
new file mode 100755
index 0000000..56b8ef1
--- /dev/null
+++ b/integration/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/extension/CredentialAddHandler.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2014 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package org.keycloak.subsystem.wf8.extension;
+
+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;
+
+/**
+ * Add a credential to a deployment.
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2014 Red Hat Inc.
+ */
+public class CredentialAddHandler extends AbstractAddStepHandler {
+
+    public CredentialAddHandler(AttributeDefinition... attributes) {
+        super(attributes);
+    }
+
+    @Override
+    protected void performRuntime(OperationContext context, ModelNode operation, ModelNode model, ServiceVerificationHandler verificationHandler, List<ServiceController<?>> newControllers) throws OperationFailedException {
+        KeycloakAdapterConfigService ckService = KeycloakAdapterConfigService.getInstance();
+        ckService.addCredential(operation, context.resolveExpressions(model));
+    }
+
+}
diff --git a/integration/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/extension/CredentialDefinition.java b/integration/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/extension/CredentialDefinition.java
new file mode 100755
index 0000000..6083e93
--- /dev/null
+++ b/integration/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/extension/CredentialDefinition.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.keycloak.subsystem.wf8.extension;
+
+import org.jboss.as.controller.AttributeDefinition;
+import org.jboss.as.controller.PathElement;
+import org.jboss.as.controller.SimpleAttributeDefinitionBuilder;
+import org.jboss.as.controller.SimpleResourceDefinition;
+import org.jboss.as.controller.operations.common.GenericSubsystemDescribeHandler;
+import org.jboss.as.controller.operations.validation.StringLengthValidator;
+import org.jboss.as.controller.registry.ManagementResourceRegistration;
+import org.jboss.dmr.ModelType;
+
+/**
+ * Defines attributes and operations for a credential.
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc.
+ */
+public class CredentialDefinition extends SimpleResourceDefinition {
+
+    public static final String TAG_NAME = "credential";
+
+    protected static final AttributeDefinition VALUE =
+            new SimpleAttributeDefinitionBuilder("value", ModelType.STRING, false)
+            .setAllowExpression(true)
+            .setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, false, true))
+            .build();
+
+    public CredentialDefinition() {
+        super(PathElement.pathElement(TAG_NAME),
+                KeycloakExtension.getResourceDescriptionResolver(TAG_NAME),
+                new CredentialAddHandler(VALUE),
+                CredentialRemoveHandler.INSTANCE);
+    }
+
+    @Override
+    public void registerOperations(ManagementResourceRegistration resourceRegistration) {
+        super.registerOperations(resourceRegistration);
+        resourceRegistration.registerOperationHandler(GenericSubsystemDescribeHandler.DEFINITION, GenericSubsystemDescribeHandler.INSTANCE);
+    }
+
+    @Override
+    public void registerAttributes(ManagementResourceRegistration resourceRegistration) {
+        super.registerAttributes(resourceRegistration);
+        resourceRegistration.registerReadWriteAttribute(VALUE, null, new CredentialReadWriteAttributeHandler());
+    }
+}
diff --git a/integration/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/extension/CredentialReadWriteAttributeHandler.java b/integration/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/extension/CredentialReadWriteAttributeHandler.java
new file mode 100644
index 0000000..510f3ed
--- /dev/null
+++ b/integration/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/extension/CredentialReadWriteAttributeHandler.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2014 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package org.keycloak.subsystem.wf8.extension;
+
+import org.jboss.as.controller.AbstractWriteAttributeHandler;
+import org.jboss.as.controller.OperationContext;
+import org.jboss.as.controller.OperationFailedException;
+import org.jboss.dmr.ModelNode;
+
+/**
+ * Update a credential value.
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2014 Red Hat Inc.
+ */
+public class CredentialReadWriteAttributeHandler extends AbstractWriteAttributeHandler<KeycloakAdapterConfigService> {
+
+    @Override
+    protected boolean applyUpdateToRuntime(OperationContext context, ModelNode operation, String attributeName,
+                                           ModelNode resolvedValue, ModelNode currentValue, AbstractWriteAttributeHandler.HandbackHolder<KeycloakAdapterConfigService> hh) throws OperationFailedException {
+
+        KeycloakAdapterConfigService ckService = KeycloakAdapterConfigService.getInstance();
+        ckService.updateCredential(operation, attributeName, resolvedValue);
+
+        hh.setHandback(ckService);
+
+        return false;
+    }
+
+    @Override
+    protected void revertUpdateToRuntime(OperationContext context, ModelNode operation, String attributeName,
+                                         ModelNode valueToRestore, ModelNode valueToRevert, KeycloakAdapterConfigService ckService) throws OperationFailedException {
+        ckService.updateCredential(operation, attributeName, valueToRestore);
+    }
+
+}
diff --git a/integration/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/extension/CredentialRemoveHandler.java b/integration/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/extension/CredentialRemoveHandler.java
new file mode 100644
index 0000000..f3f7818
--- /dev/null
+++ b/integration/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/extension/CredentialRemoveHandler.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2014 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package org.keycloak.subsystem.wf8.extension;
+
+import org.jboss.as.controller.AbstractRemoveStepHandler;
+import org.jboss.as.controller.OperationContext;
+import org.jboss.as.controller.OperationFailedException;
+import org.jboss.dmr.ModelNode;
+
+/**
+ * Remove a credential from a deployment.
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2014 Red Hat Inc.
+ */
+public final class CredentialRemoveHandler extends AbstractRemoveStepHandler {
+
+    public static CredentialRemoveHandler INSTANCE = new CredentialRemoveHandler();
+
+    private CredentialRemoveHandler() {}
+
+    @Override
+    protected void performRuntime(OperationContext context, ModelNode operation, ModelNode model) throws OperationFailedException {
+        KeycloakAdapterConfigService ckService = KeycloakAdapterConfigService.getInstance();
+        ckService.removeCredential(operation);
+    }
+
+}
diff --git a/integration/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/extension/KeycloakAdapterConfigDeploymentProcessor.java b/integration/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/extension/KeycloakAdapterConfigDeploymentProcessor.java
new file mode 100755
index 0000000..4399291
--- /dev/null
+++ b/integration/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/extension/KeycloakAdapterConfigDeploymentProcessor.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2014 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package org.keycloak.subsystem.wf8.extension;
+
+import org.jboss.as.server.deployment.DeploymentPhaseContext;
+import org.jboss.as.server.deployment.DeploymentUnit;
+import org.jboss.as.server.deployment.DeploymentUnitProcessingException;
+import org.jboss.as.server.deployment.DeploymentUnitProcessor;
+import org.jboss.as.web.common.WarMetaData;
+import org.jboss.logging.Logger;
+import org.jboss.metadata.javaee.spec.ParamValueMetaData;
+import org.jboss.metadata.web.jboss.JBossWebMetaData;
+import org.jboss.metadata.web.spec.LoginConfigMetaData;
+import org.keycloak.subsystem.wf8.logging.KeycloakLogger;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Pass authentication data (keycloak.json) as a servlet context param so it can be read by the KeycloakServletExtension.
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2014 Red Hat Inc.
+ */
+public class KeycloakAdapterConfigDeploymentProcessor implements DeploymentUnitProcessor {
+    protected Logger log = Logger.getLogger(KeycloakAdapterConfigDeploymentProcessor.class);
+
+    // This param name is defined again in Keycloak Undertow Integration class
+    // org.keycloak.adapters.undertow.KeycloakServletExtension.  We have this value in
+    // two places to avoid dependency between Keycloak Subsystem and Keyclaok Undertow Integration.
+    public static final String AUTH_DATA_PARAM_NAME = "org.keycloak.json.adapterConfig";
+
+    // not sure if we need this yet, keeping here just in case
+    protected void addSecurityDomain(DeploymentUnit deploymentUnit, KeycloakAdapterConfigService service) {
+        String deploymentName = deploymentUnit.getName();
+        if (!service.isSecureDeployment(deploymentName)) {
+            return;
+        }
+        WarMetaData warMetaData = deploymentUnit.getAttachment(WarMetaData.ATTACHMENT_KEY);
+        if (warMetaData == null) return;
+        JBossWebMetaData webMetaData = warMetaData.getMergedJBossWebMetaData();
+        if (webMetaData == null) return;
+
+        LoginConfigMetaData loginConfig = webMetaData.getLoginConfig();
+        if (loginConfig == null || !loginConfig.getAuthMethod().equalsIgnoreCase("KEYCLOAK")) {
+            return;
+        }
+
+        webMetaData.setSecurityDomain("keycloak");
+    }
+
+    @Override
+    public void deploy(DeploymentPhaseContext phaseContext) throws DeploymentUnitProcessingException {
+        DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit();
+
+        String deploymentName = deploymentUnit.getName();
+        KeycloakAdapterConfigService service = KeycloakAdapterConfigService.getInstance();
+        if (service.isSecureDeployment(deploymentName)) {
+            addKeycloakAuthData(phaseContext, deploymentName, service);
+        }
+
+        // FYI, Undertow Extension will find deployments that have auth-method set to KEYCLOAK
+
+        // todo notsure if we need this
+        // addSecurityDomain(deploymentUnit, service);
+    }
+
+    private void addKeycloakAuthData(DeploymentPhaseContext phaseContext, String deploymentName, KeycloakAdapterConfigService service) throws DeploymentUnitProcessingException {
+        DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit();
+        WarMetaData warMetaData = deploymentUnit.getAttachment(WarMetaData.ATTACHMENT_KEY);
+        if (warMetaData == null) {
+            throw new DeploymentUnitProcessingException("WarMetaData not found for " + deploymentName + ".  Make sure you have specified a WAR as your secure-deployment in the Keycloak subsystem.");
+        }
+
+        addJSONData(service.getJSON(deploymentName), warMetaData);
+        JBossWebMetaData webMetaData = warMetaData.getMergedJBossWebMetaData();
+        if (webMetaData == null) {
+            webMetaData = new JBossWebMetaData();
+            warMetaData.setMergedJBossWebMetaData(webMetaData);
+        }
+
+        LoginConfigMetaData loginConfig = webMetaData.getLoginConfig();
+        if (loginConfig == null) {
+            loginConfig = new LoginConfigMetaData();
+            webMetaData.setLoginConfig(loginConfig);
+        }
+        loginConfig.setAuthMethod("KEYCLOAK");
+        loginConfig.setRealmName(service.getRealmName(deploymentName));
+        KeycloakLogger.ROOT_LOGGER.deploymentSecured(deploymentName);
+    }
+
+    private void addJSONData(String json, WarMetaData warMetaData) {
+        JBossWebMetaData webMetaData = warMetaData.getMergedJBossWebMetaData();
+        if (webMetaData == null) {
+            webMetaData = new JBossWebMetaData();
+            warMetaData.setMergedJBossWebMetaData(webMetaData);
+        }
+
+        List<ParamValueMetaData> contextParams = webMetaData.getContextParams();
+        if (contextParams == null) {
+            contextParams = new ArrayList<ParamValueMetaData>();
+        }
+
+        ParamValueMetaData param = new ParamValueMetaData();
+        param.setParamName(AUTH_DATA_PARAM_NAME);
+        param.setParamValue(json);
+        contextParams.add(param);
+
+        webMetaData.setContextParams(contextParams);
+    }
+
+    @Override
+    public void undeploy(DeploymentUnit du) {
+
+    }
+
+}
diff --git a/integration/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/extension/KeycloakAdapterConfigService.java b/integration/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/extension/KeycloakAdapterConfigService.java
new file mode 100755
index 0000000..4843534
--- /dev/null
+++ b/integration/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/extension/KeycloakAdapterConfigService.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package org.keycloak.subsystem.wf8.extension;
+
+import org.jboss.dmr.ModelNode;
+import org.jboss.dmr.Property;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ADDRESS;
+
+/**
+ * This service keeps track of the entire Keycloak management model so as to provide
+ * adapter configuration to each deployment at deploy time.
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc.
+ */
+public final class KeycloakAdapterConfigService {
+
+    private static final String CREDENTIALS_JSON_NAME = "credentials";
+
+    private static final KeycloakAdapterConfigService INSTANCE = new KeycloakAdapterConfigService();
+
+    public static KeycloakAdapterConfigService getInstance() {
+        return INSTANCE;
+    }
+
+    private final Map<String, ModelNode> realms = new HashMap<String, ModelNode>();
+
+    // keycloak-secured deployments
+    private final Map<String, ModelNode> secureDeployments = new HashMap<String, ModelNode>();
+
+
+    private KeycloakAdapterConfigService() {
+    }
+
+    public void addRealm(ModelNode operation, ModelNode model) {
+        this.realms.put(realmNameFromOp(operation), model.clone());
+    }
+
+    public void updateRealm(ModelNode operation, String attrName, ModelNode resolvedValue) {
+        ModelNode realm = this.realms.get(realmNameFromOp(operation));
+        realm.get(attrName).set(resolvedValue);
+    }
+
+    public void removeRealm(ModelNode operation) {
+        this.realms.remove(realmNameFromOp(operation));
+    }
+
+    public void addSecureDeployment(ModelNode operation, ModelNode model) {
+        ModelNode deployment = model.clone();
+        this.secureDeployments.put(deploymentNameFromOp(operation), deployment);
+    }
+
+    public void updateSecureDeployment(ModelNode operation, String attrName, ModelNode resolvedValue) {
+        ModelNode deployment = this.secureDeployments.get(deploymentNameFromOp(operation));
+        deployment.get(attrName).set(resolvedValue);
+    }
+
+    public void removeSecureDeployment(ModelNode operation) {
+        this.secureDeployments.remove(deploymentNameFromOp(operation));
+    }
+
+    public void addCredential(ModelNode operation, ModelNode model) {
+        ModelNode credentials = credentialsFromOp(operation);
+        if (!credentials.isDefined()) {
+            credentials = new ModelNode();
+        }
+
+        String credentialName = credentialNameFromOp(operation);
+        credentials.get(credentialName).set(model.get("value").asString());
+
+        ModelNode deployment = this.secureDeployments.get(deploymentNameFromOp(operation));
+        deployment.get(CREDENTIALS_JSON_NAME).set(credentials);
+    }
+
+    public void removeCredential(ModelNode operation) {
+        ModelNode credentials = credentialsFromOp(operation);
+        if (!credentials.isDefined()) {
+            throw new RuntimeException("Can not remove credential.  No credential defined for deployment in op " + operation.toString());
+        }
+
+        String credentialName = credentialNameFromOp(operation);
+        credentials.remove(credentialName);
+    }
+
+    public void updateCredential(ModelNode operation, String attrName, ModelNode resolvedValue) {
+        ModelNode credentials = credentialsFromOp(operation);
+        if (!credentials.isDefined()) {
+            throw new RuntimeException("Can not update credential.  No credential defined for deployment in op " + operation.toString());
+        }
+
+        String credentialName = credentialNameFromOp(operation);
+        credentials.get(credentialName).set(resolvedValue);
+    }
+
+    private ModelNode credentialsFromOp(ModelNode operation) {
+        ModelNode deployment = this.secureDeployments.get(deploymentNameFromOp(operation));
+        return deployment.get(CREDENTIALS_JSON_NAME);
+    }
+
+    private String realmNameFromOp(ModelNode operation) {
+        return valueFromOpAddress(RealmDefinition.TAG_NAME, operation);
+    }
+
+    private String deploymentNameFromOp(ModelNode operation) {
+        return valueFromOpAddress(SecureDeploymentDefinition.TAG_NAME, operation);
+    }
+
+    private String credentialNameFromOp(ModelNode operation) {
+        return valueFromOpAddress(CredentialDefinition.TAG_NAME, operation);
+    }
+
+    private String valueFromOpAddress(String addrElement, ModelNode operation) {
+        String deploymentName = getValueOfAddrElement(operation.get(ADDRESS), addrElement);
+        if (deploymentName == null) throw new RuntimeException("Can't find '" + addrElement + "' in address " + operation.toString());
+        return deploymentName;
+    }
+
+    private String getValueOfAddrElement(ModelNode address, String elementName) {
+        for (ModelNode element : address.asList()) {
+            if (element.has(elementName)) return element.get(elementName).asString();
+        }
+
+        return null;
+    }
+
+    public String getRealmName(String deploymentName) {
+        ModelNode deployment = this.secureDeployments.get(deploymentName);
+        return deployment.get(RealmDefinition.TAG_NAME).asString();
+
+    }
+
+    public String getJSON(String deploymentName) {
+        ModelNode deployment = this.secureDeployments.get(deploymentName);
+        String realmName = deployment.get(RealmDefinition.TAG_NAME).asString();
+        ModelNode realm = this.realms.get(realmName);
+
+        ModelNode json = new ModelNode();
+        json.get(RealmDefinition.TAG_NAME).set(realmName);
+
+        // Realm values set first.  Some can be overridden by deployment values.
+        if (realm != null) setJSONValues(json, realm);
+        setJSONValues(json, deployment);
+        return json.toJSONString(true);
+    }
+
+    private void setJSONValues(ModelNode json, ModelNode values) {
+        for (Property prop : values.asPropertyList()) {
+            String name = prop.getName();
+            ModelNode value = prop.getValue();
+            if (value.isDefined()) {
+                json.get(name).set(value);
+            }
+        }
+    }
+
+    public boolean isSecureDeployment(String deploymentName) {
+        //log.info("********* CHECK KEYCLOAK DEPLOYMENT: deployments.size()" + deployments.size());
+
+        return this.secureDeployments.containsKey(deploymentName);
+    }
+}
diff --git a/integration/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/extension/KeycloakDependencyProcessor.java b/integration/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/extension/KeycloakDependencyProcessor.java
new file mode 100755
index 0000000..894f662
--- /dev/null
+++ b/integration/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/extension/KeycloakDependencyProcessor.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package org.keycloak.subsystem.wf8.extension;
+
+import org.jboss.as.server.deployment.Attachments;
+import org.jboss.as.server.deployment.DeploymentPhaseContext;
+import org.jboss.as.server.deployment.DeploymentUnit;
+import org.jboss.as.server.deployment.DeploymentUnitProcessingException;
+import org.jboss.as.server.deployment.DeploymentUnitProcessor;
+import org.jboss.as.server.deployment.module.ModuleDependency;
+import org.jboss.as.server.deployment.module.ModuleSpecification;
+import org.jboss.modules.Module;
+import org.jboss.modules.ModuleIdentifier;
+import org.jboss.modules.ModuleLoader;
+
+/**
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc.
+ */
+public abstract class KeycloakDependencyProcessor implements DeploymentUnitProcessor {
+
+    private static final ModuleIdentifier KEYCLOAK_JBOSS_CORE_ADAPTER = ModuleIdentifier.create("org.keycloak.keycloak-jboss-adapter-core");
+    private static final ModuleIdentifier KEYCLOAK_CORE_ADAPTER = ModuleIdentifier.create("org.keycloak.keycloak-adapter-core");
+    private static final ModuleIdentifier KEYCLOAK_CORE = ModuleIdentifier.create("org.keycloak.keycloak-core");
+
+    @Override
+    public void deploy(DeploymentPhaseContext phaseContext) throws DeploymentUnitProcessingException {
+        final DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit();
+
+        // Next phase, need to detect if this is a Keycloak deployment.  If not, don't add the modules.
+
+        final ModuleSpecification moduleSpecification = deploymentUnit.getAttachment(Attachments.MODULE_SPECIFICATION);
+        final ModuleLoader moduleLoader = Module.getBootModuleLoader();
+        addCommonModules(moduleSpecification, moduleLoader);
+        addPlatformSpecificModules(moduleSpecification, moduleLoader);
+    }
+
+    private void addCommonModules(ModuleSpecification moduleSpecification, ModuleLoader moduleLoader) {
+        // ModuleDependency(ModuleLoader moduleLoader, ModuleIdentifier identifier, boolean optional, boolean export, boolean importServices, boolean userSpecified)
+        moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_JBOSS_CORE_ADAPTER, false, false, false, false));
+        moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_CORE_ADAPTER, false, false, false, false));
+        moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_CORE, false, false, false, false));
+    }
+
+    abstract protected void addPlatformSpecificModules(ModuleSpecification moduleSpecification, ModuleLoader moduleLoader);
+
+    @Override
+    public void undeploy(DeploymentUnit du) {
+
+    }
+
+}
diff --git a/integration/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/extension/KeycloakDependencyProcessorWildFly.java b/integration/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/extension/KeycloakDependencyProcessorWildFly.java
new file mode 100755
index 0000000..7008fb6
--- /dev/null
+++ b/integration/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/extension/KeycloakDependencyProcessorWildFly.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package org.keycloak.subsystem.wf8.extension;
+
+import org.jboss.as.server.deployment.module.ModuleDependency;
+import org.jboss.as.server.deployment.module.ModuleSpecification;
+import org.jboss.modules.ModuleIdentifier;
+import org.jboss.modules.ModuleLoader;
+
+/**
+ * Add platform-specific modules for WildFly.
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2014 Red Hat Inc.
+ */
+public class KeycloakDependencyProcessorWildFly extends KeycloakDependencyProcessor {
+
+    private static final ModuleIdentifier KEYCLOAK_WILDFLY_ADAPTER = ModuleIdentifier.create("org.keycloak.keycloak-wildfly-adapter");
+    private static final ModuleIdentifier KEYCLOAK_UNDERTOW_ADAPTER = ModuleIdentifier.create("org.keycloak.keycloak-undertow-adapter");
+
+    @Override
+    protected void addPlatformSpecificModules(ModuleSpecification moduleSpecification, ModuleLoader moduleLoader) {
+        // ModuleDependency(ModuleLoader moduleLoader, ModuleIdentifier identifier, boolean optional, boolean export, boolean importServices, boolean userSpecified)
+        moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_WILDFLY_ADAPTER, false, false, true, false));
+        moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_UNDERTOW_ADAPTER, false, false, false, false));
+    }
+}
diff --git a/integration/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/extension/KeycloakExtension.java b/integration/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/extension/KeycloakExtension.java
new file mode 100755
index 0000000..6049f10
--- /dev/null
+++ b/integration/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/extension/KeycloakExtension.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.keycloak.subsystem.wf8.extension;
+
+import org.jboss.as.controller.Extension;
+import org.jboss.as.controller.ExtensionContext;
+import org.jboss.as.controller.ModelVersion;
+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.keycloak.subsystem.wf8.logging.KeycloakLogger;
+
+import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SUBSYSTEM;
+
+
+/**
+ * Main Extension class for the subsystem.
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc.
+ */
+public class KeycloakExtension implements Extension {
+
+    public static final String SUBSYSTEM_NAME = "keycloak";
+    public static final String NAMESPACE = "urn:jboss:domain:keycloak:1.1";
+    private static final KeycloakSubsystemParser PARSER = new KeycloakSubsystemParser();
+    static final PathElement PATH_SUBSYSTEM = PathElement.pathElement(SUBSYSTEM, SUBSYSTEM_NAME);
+    private static final String RESOURCE_NAME = KeycloakExtension.class.getPackage().getName() + ".LocalDescriptions";
+    private static final int MANAGEMENT_API_MAJOR_VERSION = 1;
+    private static final int MANAGEMENT_API_MINOR_VERSION = 0;
+    private static final int MANAGEMENT_API_MICRO_VERSION = 0;
+    static final PathElement SUBSYSTEM_PATH = PathElement.pathElement(SUBSYSTEM, SUBSYSTEM_NAME);
+    private static final ResourceDefinition KEYCLOAK_SUBSYSTEM_RESOURCE = new KeycloakSubsystemDefinition();
+    static final RealmDefinition REALM_DEFINITION = new RealmDefinition();
+    static final SecureDeploymentDefinition SECURE_DEPLOYMENT_DEFINITION = new SecureDeploymentDefinition();
+    static final CredentialDefinition CREDENTIAL_DEFINITION = new CredentialDefinition();
+
+    public static StandardResourceDescriptionResolver getResourceDescriptionResolver(final String... keyPrefix) {
+        StringBuilder prefix = new StringBuilder(SUBSYSTEM_NAME);
+        for (String kp : keyPrefix) {
+            prefix.append('.').append(kp);
+        }
+        return new StandardResourceDescriptionResolver(prefix.toString(), RESOURCE_NAME, KeycloakExtension.class.getClassLoader(), true, false);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void initializeParsers(final ExtensionParsingContext context) {
+        context.setSubsystemXmlMapping(SUBSYSTEM_NAME, KeycloakExtension.NAMESPACE, PARSER);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void initialize(final ExtensionContext context) {
+        KeycloakLogger.ROOT_LOGGER.debug("Activating Keycloak Extension");
+        final SubsystemRegistration subsystem = context.registerSubsystem(SUBSYSTEM_NAME, MANAGEMENT_API_MAJOR_VERSION, MANAGEMENT_API_MINOR_VERSION, MANAGEMENT_API_MICRO_VERSION);
+
+        ManagementResourceRegistration registration = subsystem.registerSubsystemModel(KEYCLOAK_SUBSYSTEM_RESOURCE);
+        registration.registerSubModel(REALM_DEFINITION);
+        ManagementResourceRegistration secureDeploymentRegistration = registration.registerSubModel(SECURE_DEPLOYMENT_DEFINITION);
+        secureDeploymentRegistration.registerSubModel(CREDENTIAL_DEFINITION);
+
+        subsystem.registerXMLElementWriter(PARSER);
+    }
+}
diff --git a/integration/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/extension/KeycloakSubsystemAdd.java b/integration/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/extension/KeycloakSubsystemAdd.java
new file mode 100755
index 0000000..3be483e
--- /dev/null
+++ b/integration/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/extension/KeycloakSubsystemAdd.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.keycloak.subsystem.wf8.extension;
+
+
+import org.jboss.as.controller.AbstractBoottimeAddStepHandler;
+import org.jboss.as.controller.OperationContext;
+import org.jboss.as.controller.ServiceVerificationHandler;
+import org.jboss.as.server.AbstractDeploymentChainStep;
+import org.jboss.as.server.DeploymentProcessorTarget;
+import org.jboss.as.server.deployment.Phase;
+import org.jboss.dmr.ModelNode;
+
+import org.jboss.as.server.deployment.DeploymentUnitProcessor;
+import org.jboss.msc.service.ServiceController;
+
+import java.util.List;
+
+/**
+ * The Keycloak subsystem add update handler.
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc.
+ */
+class KeycloakSubsystemAdd extends AbstractBoottimeAddStepHandler {
+
+    static final KeycloakSubsystemAdd INSTANCE = new KeycloakSubsystemAdd();
+
+    @Override
+    protected void performBoottime(final OperationContext context, ModelNode operation, final ModelNode model, ServiceVerificationHandler verificationHandler, List<ServiceController<?>> newControllers) {
+        context.addStep(new AbstractDeploymentChainStep() {
+            @Override
+            protected void execute(DeploymentProcessorTarget processorTarget) {
+                processorTarget.addDeploymentProcessor(KeycloakExtension.SUBSYSTEM_NAME, Phase.DEPENDENCIES, 0, chooseDependencyProcessor());
+                processorTarget.addDeploymentProcessor(KeycloakExtension.SUBSYSTEM_NAME,
+                        Phase.POST_MODULE, // PHASE
+                        Phase.POST_MODULE_VALIDATOR_FACTORY - 1, // PRIORITY
+                        chooseConfigDeploymentProcessor());
+            }
+        }, OperationContext.Stage.RUNTIME);
+    }
+
+    private DeploymentUnitProcessor chooseDependencyProcessor() {
+        return new KeycloakDependencyProcessorWildFly();
+    }
+
+    private DeploymentUnitProcessor chooseConfigDeploymentProcessor() {
+        return new KeycloakAdapterConfigDeploymentProcessor();
+    }
+}
diff --git a/integration/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/extension/KeycloakSubsystemDefinition.java b/integration/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/extension/KeycloakSubsystemDefinition.java
new file mode 100644
index 0000000..a6093cf
--- /dev/null
+++ b/integration/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/extension/KeycloakSubsystemDefinition.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2014 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package org.keycloak.subsystem.wf8.extension;
+
+import org.jboss.as.controller.ReloadRequiredRemoveStepHandler;
+import org.jboss.as.controller.SimpleResourceDefinition;
+import org.jboss.as.controller.operations.common.GenericSubsystemDescribeHandler;
+import org.jboss.as.controller.registry.ManagementResourceRegistration;
+
+/**
+ * Definition of subsystem=keycloak.
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc.
+ */
+public class KeycloakSubsystemDefinition extends SimpleResourceDefinition {
+    protected KeycloakSubsystemDefinition() {
+        super(KeycloakExtension.SUBSYSTEM_PATH,
+                KeycloakExtension.getResourceDescriptionResolver("subsystem"),
+                KeycloakSubsystemAdd.INSTANCE,
+                ReloadRequiredRemoveStepHandler.INSTANCE
+        );
+    }
+
+    @Override
+    public void registerOperations(ManagementResourceRegistration resourceRegistration) {
+        super.registerOperations(resourceRegistration);
+        resourceRegistration.registerOperationHandler(GenericSubsystemDescribeHandler.DEFINITION, GenericSubsystemDescribeHandler.INSTANCE);
+    }
+
+}
diff --git a/integration/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/extension/KeycloakSubsystemParser.java b/integration/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/extension/KeycloakSubsystemParser.java
new file mode 100755
index 0000000..efa260b
--- /dev/null
+++ b/integration/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/extension/KeycloakSubsystemParser.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright 2014 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.keycloak.subsystem.wf8.extension;
+
+import org.jboss.as.controller.AttributeDefinition;
+import org.jboss.as.controller.PathAddress;
+import org.jboss.as.controller.PathElement;
+import org.jboss.as.controller.SimpleAttributeDefinition;
+import org.jboss.as.controller.descriptions.ModelDescriptionConstants;
+import org.jboss.as.controller.operations.common.Util;
+import org.jboss.as.controller.parsing.ParseUtils;
+import org.jboss.as.controller.persistence.SubsystemMarshallingContext;
+import org.jboss.dmr.ModelNode;
+import org.jboss.dmr.Property;
+import org.jboss.staxmapper.XMLElementReader;
+import org.jboss.staxmapper.XMLElementWriter;
+import org.jboss.staxmapper.XMLExtendedStreamReader;
+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
+ */
+class KeycloakSubsystemParser implements XMLStreamConstants, XMLElementReader<List<ModelNode>>, XMLElementWriter<SubsystemMarshallingContext> {
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void readElement(final XMLExtendedStreamReader reader, final List<ModelNode> list) throws XMLStreamException {
+        // Require no attributes
+        ParseUtils.requireNoAttributes(reader);
+        ModelNode addKeycloakSub = Util.createAddOperation(PathAddress.pathAddress(KeycloakExtension.PATH_SUBSYSTEM));
+        list.add(addKeycloakSub);
+
+        while (reader.hasNext() && nextTag(reader) != END_ELEMENT) {
+            if (reader.getLocalName().equals(RealmDefinition.TAG_NAME)) {
+                readRealm(reader, list);
+            }
+            else if (reader.getLocalName().equals(SecureDeploymentDefinition.TAG_NAME)) {
+                readDeployment(reader, list);
+            }
+        }
+    }
+
+    // used for debugging
+    private int nextTag(XMLExtendedStreamReader reader) throws XMLStreamException {
+        return reader.nextTag();
+    }
+
+    private void readRealm(XMLExtendedStreamReader reader, List<ModelNode> list) throws XMLStreamException {
+        String realmName = readNameAttribute(reader);
+        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());
+
+        while (reader.hasNext() && nextTag(reader) != END_ELEMENT) {
+            String tagName = reader.getLocalName();
+            SimpleAttributeDefinition def = RealmDefinition.lookup(tagName);
+            if (def == null) throw new XMLStreamException("Unknown realm tag " + tagName);
+            def.parseAndSetParameter(reader.getElementText(), addRealm, reader);
+        }
+
+        if (!SharedAttributeDefinitons.validateTruststoreSetIfRequired(addRealm)) {
+            //TODO: externalize the message
+            throw new XMLStreamException("truststore and truststore-password must be set if ssl-required is not none and disable-trust-maanger is false.");
+        }
+
+        list.add(addRealm);
+    }
+
+    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(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) {
+            String tagName = reader.getLocalName();
+            if (tagName.equals(CredentialDefinition.TAG_NAME)) {
+                readCredential(reader, addr, credentialsToAdd);
+                continue;
+            }
+
+            SimpleAttributeDefinition def = SecureDeploymentDefinition.lookup(tagName);
+            if (def == null) throw new XMLStreamException("Unknown secure-deployment tag " + tagName);
+            def.parseAndSetParameter(reader.getElementText(), addSecureDeployment, reader);
+        }
+
+
+        /**
+         * 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 ssl-required is not none  and disable-trust-maanger is false.");
+        }
+         */
+
+        // Must add credentials after the deployment is added.
+        resourcesToAdd.add(addSecureDeployment);
+        resourcesToAdd.addAll(credentialsToAdd);
+    }
+
+    public void readCredential(XMLExtendedStreamReader reader, PathAddress parent, List<ModelNode> credentialsToAdd) throws XMLStreamException {
+        String name = readNameAttribute(reader);
+        ModelNode addCredential = new ModelNode();
+        addCredential.get(ModelDescriptionConstants.OP).set(ModelDescriptionConstants.ADD);
+        PathAddress addr = PathAddress.pathAddress(parent, PathElement.pathElement(CredentialDefinition.TAG_NAME, name));
+        addCredential.get(ModelDescriptionConstants.OP_ADDR).set(addr.toModelNode());
+        addCredential.get(CredentialDefinition.VALUE.getName()).set(reader.getElementText());
+        credentialsToAdd.add(addCredential);
+    }
+
+    // expects that the current tag will have one single attribute called "name"
+    private String readNameAttribute(XMLExtendedStreamReader reader) throws XMLStreamException {
+        String name = null;
+        for (int i = 0; i < reader.getAttributeCount(); i++) {
+            String attr = reader.getAttributeLocalName(i);
+            if (attr.equals("name")) {
+                name = reader.getAttributeValue(i);
+                continue;
+            }
+            throw ParseUtils.unexpectedAttribute(reader, i);
+        }
+        if (name == null) {
+            throw ParseUtils.missingRequired(reader, Collections.singleton("name"));
+        }
+        return name;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void writeContent(final XMLExtendedStreamWriter writer, final SubsystemMarshallingContext context) throws XMLStreamException {
+        context.startSubsystemElement(KeycloakExtension.NAMESPACE, false);
+        writeRealms(writer, context);
+        writeSecureDeployments(writer, context);
+        writer.writeEndElement();
+    }
+
+    private void writeRealms(XMLExtendedStreamWriter writer, SubsystemMarshallingContext context) throws XMLStreamException {
+        if (!context.getModelNode().get(RealmDefinition.TAG_NAME).isDefined()) {
+            return;
+        }
+        for (Property realm : context.getModelNode().get(RealmDefinition.TAG_NAME).asPropertyList()) {
+            writer.writeStartElement(RealmDefinition.TAG_NAME);
+            writer.writeAttribute("name", realm.getName());
+            ModelNode realmElements = realm.getValue();
+            for (AttributeDefinition element : RealmDefinition.ALL_ATTRIBUTES) {
+                element.marshallAsElement(realmElements, writer);
+            }
+
+            writer.writeEndElement();
+        }
+    }
+
+    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();
+            for (AttributeDefinition element : SecureDeploymentDefinition.ALL_ATTRIBUTES) {
+                element.marshallAsElement(deploymentElements, writer);
+            }
+
+            ModelNode credentials = deploymentElements.get(CredentialDefinition.TAG_NAME);
+            if (credentials.isDefined()) {
+                writeCredentials(writer, credentials);
+            }
+
+            writer.writeEndElement();
+        }
+    }
+
+    private void writeCredentials(XMLExtendedStreamWriter writer, ModelNode credentials) throws XMLStreamException {
+        for (Property credential : credentials.asPropertyList()) {
+            writer.writeStartElement(CredentialDefinition.TAG_NAME);
+            writer.writeAttribute("name", credential.getName());
+            String credentialValue = credential.getValue().get(CredentialDefinition.VALUE.getName()).asString();
+            writeCharacters(writer, credentialValue);
+            writer.writeEndElement();
+        }
+    }
+
+    // code taken from org.jboss.as.controller.AttributeMarshaller
+    private void writeCharacters(XMLExtendedStreamWriter writer, String content) throws XMLStreamException {
+        if (content.indexOf('\n') > -1) {
+            // Multiline content. Use the overloaded variant that staxmapper will format
+            writer.writeCharacters(content);
+        } else {
+            // Staxmapper will just output the chars without adding newlines if this is used
+            char[] chars = content.toCharArray();
+            writer.writeCharacters(chars, 0, chars.length);
+        }
+    }
+
+}
diff --git a/integration/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/extension/RealmAddHandler.java b/integration/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/extension/RealmAddHandler.java
new file mode 100755
index 0000000..fef809e
--- /dev/null
+++ b/integration/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/extension/RealmAddHandler.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package org.keycloak.subsystem.wf8.extension;
+
+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;
+
+/**
+ * Add a new realm.
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc.
+ */
+public final class RealmAddHandler extends AbstractAddStepHandler {
+
+    public static RealmAddHandler INSTANCE = new RealmAddHandler();
+
+    private RealmAddHandler() {}
+
+    @Override
+    protected void populateModel(ModelNode operation, ModelNode model) throws OperationFailedException {
+        // TODO: localize exception. get id number
+        if (!operation.get(OP).asString().equals(ADD)) {
+            throw new OperationFailedException("Unexpected operation for add realm. operation=" + operation.toString());
+        }
+
+        for (AttributeDefinition attrib : RealmDefinition.ALL_ATTRIBUTES) {
+            attrib.validateAndSet(operation, model);
+        }
+
+        if (!SharedAttributeDefinitons.validateTruststoreSetIfRequired(model.clone())) {
+            //TODO: externalize message
+            throw new OperationFailedException("truststore and truststore-password must be set if ssl-required is not none and disable-trust-maanger is false.");
+        }
+    }
+
+    @Override
+    protected void performRuntime(OperationContext context, ModelNode operation, ModelNode model, ServiceVerificationHandler verificationHandler, List<ServiceController<?>> newControllers) throws OperationFailedException {
+        KeycloakAdapterConfigService ckService = KeycloakAdapterConfigService.getInstance();
+        ckService.addRealm(operation, context.resolveExpressions(model));
+    }
+}
diff --git a/integration/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/extension/RealmDefinition.java b/integration/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/extension/RealmDefinition.java
new file mode 100755
index 0000000..628a5d7
--- /dev/null
+++ b/integration/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/extension/RealmDefinition.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.keycloak.subsystem.wf8.extension;
+
+import org.jboss.as.controller.AttributeDefinition;
+import org.jboss.as.controller.PathElement;
+import org.jboss.as.controller.SimpleAttributeDefinition;
+import org.jboss.as.controller.SimpleResourceDefinition;
+import org.jboss.as.controller.operations.common.GenericSubsystemDescribeHandler;
+import org.jboss.as.controller.registry.ManagementResourceRegistration;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Defines attributes and operations for the Realm
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc.
+ */
+public class RealmDefinition extends SimpleResourceDefinition {
+
+    public static final String TAG_NAME = "realm";
+
+
+    protected static final List<SimpleAttributeDefinition> REALM_ONLY_ATTRIBUTES = new ArrayList<SimpleAttributeDefinition>();
+    static {
+    }
+
+    protected static final List<SimpleAttributeDefinition> ALL_ATTRIBUTES = new ArrayList<SimpleAttributeDefinition>();
+    static {
+        ALL_ATTRIBUTES.addAll(REALM_ONLY_ATTRIBUTES);
+        ALL_ATTRIBUTES.addAll(SharedAttributeDefinitons.ATTRIBUTES);
+    }
+
+    private static final Map<String, SimpleAttributeDefinition> DEFINITION_LOOKUP = new HashMap<String, SimpleAttributeDefinition>();
+    static {
+        for (SimpleAttributeDefinition def : ALL_ATTRIBUTES) {
+            DEFINITION_LOOKUP.put(def.getXmlName(), def);
+        }
+    }
+
+    private static final RealmWriteAttributeHandler realmAttrHandler = new RealmWriteAttributeHandler(ALL_ATTRIBUTES.toArray(new SimpleAttributeDefinition[0]));
+
+    public RealmDefinition() {
+        super(PathElement.pathElement("realm"),
+                KeycloakExtension.getResourceDescriptionResolver("realm"),
+                RealmAddHandler.INSTANCE,
+                RealmRemoveHandler.INSTANCE);
+    }
+
+    @Override
+    public void registerOperations(ManagementResourceRegistration resourceRegistration) {
+        super.registerOperations(resourceRegistration);
+        resourceRegistration.registerOperationHandler(GenericSubsystemDescribeHandler.DEFINITION, GenericSubsystemDescribeHandler.INSTANCE);
+    }
+
+    @Override
+    public void registerAttributes(ManagementResourceRegistration resourceRegistration) {
+        super.registerAttributes(resourceRegistration);
+
+        for (AttributeDefinition attrDef : ALL_ATTRIBUTES) {
+            //TODO: use subclass of realmAttrHandler that can call RealmDefinition.validateTruststoreSetIfRequired
+            resourceRegistration.registerReadWriteAttribute(attrDef, null, realmAttrHandler);
+        }
+    }
+
+
+    public static SimpleAttributeDefinition lookup(String name) {
+        return DEFINITION_LOOKUP.get(name);
+    }
+}
diff --git a/integration/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/extension/RealmRemoveHandler.java b/integration/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/extension/RealmRemoveHandler.java
new file mode 100644
index 0000000..84d8ab0
--- /dev/null
+++ b/integration/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/extension/RealmRemoveHandler.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package org.keycloak.subsystem.wf8.extension;
+
+import org.jboss.as.controller.AbstractRemoveStepHandler;
+import org.jboss.as.controller.OperationContext;
+import org.jboss.as.controller.OperationFailedException;
+import org.jboss.dmr.ModelNode;
+
+/**
+ * Remove a realm.
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc.
+ */
+public final class RealmRemoveHandler extends AbstractRemoveStepHandler {
+
+    public static RealmRemoveHandler INSTANCE = new RealmRemoveHandler();
+
+    private RealmRemoveHandler() {}
+
+    @Override
+    protected void performRuntime(OperationContext context, ModelNode operation, ModelNode model) throws OperationFailedException {
+        KeycloakAdapterConfigService ckService = KeycloakAdapterConfigService.getInstance();
+        ckService.removeRealm(operation);
+    }
+}
diff --git a/integration/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/extension/RealmWriteAttributeHandler.java b/integration/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/extension/RealmWriteAttributeHandler.java
new file mode 100755
index 0000000..b1062c6
--- /dev/null
+++ b/integration/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/extension/RealmWriteAttributeHandler.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package org.keycloak.subsystem.wf8.extension;
+
+import org.jboss.as.controller.AbstractWriteAttributeHandler;
+import org.jboss.as.controller.AttributeDefinition;
+import org.jboss.as.controller.OperationContext;
+import org.jboss.as.controller.OperationFailedException;
+import org.jboss.dmr.ModelNode;
+
+/**
+ * Update an attribute on a realm.
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc.
+ */
+public class RealmWriteAttributeHandler extends AbstractWriteAttributeHandler<KeycloakAdapterConfigService> {
+
+    public RealmWriteAttributeHandler(AttributeDefinition... definitions) {
+        super(definitions);
+    }
+
+    @Override
+    protected boolean applyUpdateToRuntime(OperationContext context, ModelNode operation, String attributeName,
+                                           ModelNode resolvedValue, ModelNode currentValue, HandbackHolder<KeycloakAdapterConfigService> hh) throws OperationFailedException {
+        KeycloakAdapterConfigService ckService = KeycloakAdapterConfigService.getInstance();
+        ckService.updateRealm(operation, attributeName, resolvedValue);
+
+        hh.setHandback(ckService);
+
+        return false;
+    }
+
+    @Override
+    protected void revertUpdateToRuntime(OperationContext context, ModelNode operation, String attributeName,
+                                         ModelNode valueToRestore, ModelNode valueToRevert, KeycloakAdapterConfigService ckService) throws OperationFailedException {
+        ckService.updateRealm(operation, attributeName, valueToRestore);
+    }
+
+}
diff --git a/integration/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/extension/SecureDeploymentAddHandler.java b/integration/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/extension/SecureDeploymentAddHandler.java
new file mode 100755
index 0000000..66cb8a7
--- /dev/null
+++ b/integration/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/extension/SecureDeploymentAddHandler.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package org.keycloak.subsystem.wf8.extension;
+
+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;
+
+/**
+ * Add a deployment to a realm.
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc.
+ */
+public final class SecureDeploymentAddHandler extends AbstractAddStepHandler {
+
+    public static SecureDeploymentAddHandler INSTANCE = new SecureDeploymentAddHandler();
+
+    private SecureDeploymentAddHandler() {}
+
+    @Override
+    protected void populateModel(ModelNode operation, ModelNode model) throws OperationFailedException {
+        // TODO: localize exception. get id number
+        if (!operation.get(OP).asString().equals(ADD)) {
+            throw new OperationFailedException("Unexpected operation for add secure deployment. operation=" + operation.toString());
+        }
+
+        for (AttributeDefinition attr : SecureDeploymentDefinition.ALL_ATTRIBUTES) {
+            attr.validateAndSet(operation, model);
+        }
+    }
+
+    @Override
+    protected void performRuntime(OperationContext context, ModelNode operation, ModelNode model, ServiceVerificationHandler verificationHandler, List<ServiceController<?>> newControllers) throws OperationFailedException {
+        KeycloakAdapterConfigService ckService = KeycloakAdapterConfigService.getInstance();
+        ckService.addSecureDeployment(operation, context.resolveExpressions(model));
+    }
+}
diff --git a/integration/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/extension/SecureDeploymentDefinition.java b/integration/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/extension/SecureDeploymentDefinition.java
new file mode 100755
index 0000000..d16a25e
--- /dev/null
+++ b/integration/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/extension/SecureDeploymentDefinition.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.keycloak.subsystem.wf8.extension;
+
+import org.jboss.as.controller.AttributeDefinition;
+import org.jboss.as.controller.PathElement;
+import org.jboss.as.controller.SimpleAttributeDefinition;
+import org.jboss.as.controller.SimpleAttributeDefinitionBuilder;
+import org.jboss.as.controller.SimpleResourceDefinition;
+import org.jboss.as.controller.operations.common.GenericSubsystemDescribeHandler;
+import org.jboss.as.controller.operations.validation.StringLengthValidator;
+import org.jboss.as.controller.registry.ManagementResourceRegistration;
+import org.jboss.dmr.ModelNode;
+import org.jboss.dmr.ModelType;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Defines attributes and operations for a secure-deployment.
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc.
+ */
+public class SecureDeploymentDefinition extends SimpleResourceDefinition {
+
+    public static final String TAG_NAME = "secure-deployment";
+
+    protected static final SimpleAttributeDefinition 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();
+    protected static final SimpleAttributeDefinition USE_RESOURCE_ROLE_MAPPINGS =
+            new SimpleAttributeDefinitionBuilder("use-resource-role-mappings", ModelType.BOOLEAN, true)
+            .setXmlName("use-resource-role-mappings")
+            .setAllowExpression(true)
+            .setDefaultValue(new ModelNode(false))
+            .build();
+    protected static final SimpleAttributeDefinition BEARER_ONLY =
+            new SimpleAttributeDefinitionBuilder("bearer-only", ModelType.BOOLEAN, true)
+                    .setXmlName("bearer-only")
+                    .setAllowExpression(true)
+                    .setDefaultValue(new ModelNode(false))
+                    .build();
+    protected static final SimpleAttributeDefinition ENABLE_BASIC_AUTH =
+            new SimpleAttributeDefinitionBuilder("enable-basic-auth", ModelType.BOOLEAN, true)
+                    .setXmlName("enable-basic-auth")
+                    .setAllowExpression(true)
+                    .setDefaultValue(new ModelNode(false))
+                    .build();
+    protected static final SimpleAttributeDefinition PUBLIC_CLIENT =
+            new SimpleAttributeDefinitionBuilder("public-client", ModelType.BOOLEAN, true)
+                    .setXmlName("public-client")
+                    .setAllowExpression(true)
+                    .setDefaultValue(new ModelNode(false))
+                    .build();
+
+    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);
+        DEPLOYMENT_ONLY_ATTRIBUTES.add(ENABLE_BASIC_AUTH);
+        DEPLOYMENT_ONLY_ATTRIBUTES.add(PUBLIC_CLIENT);
+    }
+
+    protected static final List<SimpleAttributeDefinition> ALL_ATTRIBUTES = new ArrayList<SimpleAttributeDefinition>();
+    static {
+        ALL_ATTRIBUTES.addAll(DEPLOYMENT_ONLY_ATTRIBUTES);
+        ALL_ATTRIBUTES.addAll(SharedAttributeDefinitons.ATTRIBUTES);
+    }
+
+    private static final Map<String, SimpleAttributeDefinition> DEFINITION_LOOKUP = new HashMap<String, SimpleAttributeDefinition>();
+    static {
+        for (SimpleAttributeDefinition def : ALL_ATTRIBUTES) {
+            DEFINITION_LOOKUP.put(def.getXmlName(), def);
+        }
+    }
+
+    private static SecureDeploymentWriteAttributeHandler attrHandler = new SecureDeploymentWriteAttributeHandler(ALL_ATTRIBUTES);
+
+    public SecureDeploymentDefinition() {
+        super(PathElement.pathElement(TAG_NAME),
+                KeycloakExtension.getResourceDescriptionResolver(TAG_NAME),
+                SecureDeploymentAddHandler.INSTANCE,
+                SecureDeploymentRemoveHandler.INSTANCE);
+    }
+
+    @Override
+    public void registerOperations(ManagementResourceRegistration resourceRegistration) {
+        super.registerOperations(resourceRegistration);
+        resourceRegistration.registerOperationHandler(GenericSubsystemDescribeHandler.DEFINITION, GenericSubsystemDescribeHandler.INSTANCE);
+    }
+
+    @Override
+    public void registerAttributes(ManagementResourceRegistration resourceRegistration) {
+        super.registerAttributes(resourceRegistration);
+        for (AttributeDefinition attrDef : ALL_ATTRIBUTES) {
+            resourceRegistration.registerReadWriteAttribute(attrDef, null, attrHandler);
+        }
+    }
+
+    public static SimpleAttributeDefinition lookup(String name) {
+        return DEFINITION_LOOKUP.get(name);
+    }
+}
diff --git a/integration/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/extension/SecureDeploymentRemoveHandler.java b/integration/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/extension/SecureDeploymentRemoveHandler.java
new file mode 100644
index 0000000..6629d08
--- /dev/null
+++ b/integration/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/extension/SecureDeploymentRemoveHandler.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package org.keycloak.subsystem.wf8.extension;
+
+import org.jboss.as.controller.AbstractRemoveStepHandler;
+import org.jboss.as.controller.OperationContext;
+import org.jboss.as.controller.OperationFailedException;
+import org.jboss.dmr.ModelNode;
+
+/**
+ * Remove a secure-deployment from a realm.
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc.
+ */
+public final class SecureDeploymentRemoveHandler extends AbstractRemoveStepHandler {
+
+    public static SecureDeploymentRemoveHandler INSTANCE = new SecureDeploymentRemoveHandler();
+
+    private SecureDeploymentRemoveHandler() {}
+
+    @Override
+    protected void performRuntime(OperationContext context, ModelNode operation, ModelNode model) throws OperationFailedException {
+        KeycloakAdapterConfigService ckService = KeycloakAdapterConfigService.getInstance();
+        ckService.removeSecureDeployment(operation);
+    }
+}
diff --git a/integration/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/extension/SecureDeploymentWriteAttributeHandler.java b/integration/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/extension/SecureDeploymentWriteAttributeHandler.java
new file mode 100755
index 0000000..3788c1b
--- /dev/null
+++ b/integration/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/extension/SecureDeploymentWriteAttributeHandler.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package org.keycloak.subsystem.wf8.extension;
+
+import org.jboss.as.controller.AbstractWriteAttributeHandler;
+import org.jboss.as.controller.AttributeDefinition;
+import org.jboss.as.controller.OperationContext;
+import org.jboss.as.controller.OperationFailedException;
+import org.jboss.as.controller.SimpleAttributeDefinition;
+import org.jboss.dmr.ModelNode;
+
+import java.util.List;
+
+/**
+ * Update an attribute on a secure-deployment.
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc.
+ */
+public class SecureDeploymentWriteAttributeHandler extends AbstractWriteAttributeHandler<KeycloakAdapterConfigService> {
+
+    public SecureDeploymentWriteAttributeHandler(List<SimpleAttributeDefinition> definitions) {
+        this(definitions.toArray(new AttributeDefinition[definitions.size()]));
+    }
+
+    public SecureDeploymentWriteAttributeHandler(AttributeDefinition... definitions) {
+        super(definitions);
+    }
+
+    @Override
+    protected boolean applyUpdateToRuntime(OperationContext context, ModelNode operation, String attributeName,
+                                           ModelNode resolvedValue, ModelNode currentValue, HandbackHolder<KeycloakAdapterConfigService> hh) throws OperationFailedException {
+        KeycloakAdapterConfigService ckService = KeycloakAdapterConfigService.getInstance();
+        hh.setHandback(ckService);
+        ckService.updateSecureDeployment(operation, attributeName, resolvedValue);
+        return false;
+    }
+
+    @Override
+    protected void revertUpdateToRuntime(OperationContext context, ModelNode operation, String attributeName,
+                                         ModelNode valueToRestore, ModelNode valueToRevert, KeycloakAdapterConfigService ckService) throws OperationFailedException {
+        ckService.updateSecureDeployment(operation, attributeName, valueToRestore);
+    }
+
+}
diff --git a/integration/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/extension/SharedAttributeDefinitons.java b/integration/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/extension/SharedAttributeDefinitons.java
new file mode 100755
index 0000000..35c4b3a
--- /dev/null
+++ b/integration/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/extension/SharedAttributeDefinitons.java
@@ -0,0 +1,228 @@
+/*
+ * Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.keycloak.subsystem.wf8.extension;
+
+import org.jboss.as.controller.SimpleAttributeDefinition;
+import org.jboss.as.controller.SimpleAttributeDefinitionBuilder;
+import org.jboss.as.controller.operations.validation.IntRangeValidator;
+import org.jboss.as.controller.operations.validation.StringLengthValidator;
+import org.jboss.dmr.ModelNode;
+import org.jboss.dmr.ModelType;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Defines attributes that can be present in both a realm and an application (secure-deployment).
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc.
+ */
+public class SharedAttributeDefinitons {
+
+    protected static final SimpleAttributeDefinition 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_REQUIRED =
+            new SimpleAttributeDefinitionBuilder("ssl-required", ModelType.STRING, true)
+                    .setXmlName("ssl-required")
+                    .setAllowExpression(true)
+                    .setDefaultValue(new ModelNode("external"))
+                    .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")
+            .setAllowExpression(true)
+            .setDefaultValue(new ModelNode(false))
+            .build();
+    protected static final SimpleAttributeDefinition CLIENT_KEYSTORE =
+            new SimpleAttributeDefinitionBuilder("client-keystore", ModelType.STRING, true)
+            .setXmlName("client-keystore")
+            .setAllowExpression(true)
+            .setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, true, true))
+            .build();
+    protected static final SimpleAttributeDefinition CLIENT_KEYSTORE_PASSWORD =
+            new SimpleAttributeDefinitionBuilder("client-keystore-password", ModelType.STRING, true)
+            .setXmlName("client-keystore-password")
+            .setAllowExpression(true)
+            .setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, true, true))
+            .build();
+    protected static final SimpleAttributeDefinition CLIENT_KEY_PASSWORD =
+            new SimpleAttributeDefinitionBuilder("client-key-password", ModelType.STRING, true)
+            .setXmlName("client-key-password")
+            .setAllowExpression(true)
+            .setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, true, true))
+            .build();
+    protected static final SimpleAttributeDefinition CORS_MAX_AGE =
+            new SimpleAttributeDefinitionBuilder("cors-max-age", ModelType.INT, true)
+            .setXmlName("cors-max-age")
+            .setAllowExpression(true)
+            .setValidator(new IntRangeValidator(-1, true))
+            .build();
+    protected static final SimpleAttributeDefinition CORS_ALLOWED_HEADERS =
+            new SimpleAttributeDefinitionBuilder("cors-allowed-headers", ModelType.STRING, true)
+            .setXmlName("cors-allowed-headers")
+            .setAllowExpression(true)
+            .setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, true, true))
+            .build();
+    protected static final SimpleAttributeDefinition CORS_ALLOWED_METHODS =
+            new SimpleAttributeDefinitionBuilder("cors-allowed-methods", ModelType.STRING, true)
+            .setXmlName("cors-allowed-methods")
+            .setAllowExpression(true)
+            .setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, true, true))
+            .build();
+    protected static final SimpleAttributeDefinition EXPOSE_TOKEN =
+            new SimpleAttributeDefinitionBuilder("expose-token", ModelType.BOOLEAN, true)
+                    .setXmlName("expose-token")
+                    .setAllowExpression(true)
+                    .setDefaultValue(new ModelNode(false))
+                    .build();
+    protected static final SimpleAttributeDefinition AUTH_SERVER_URL_FOR_BACKEND_REQUESTS =
+            new SimpleAttributeDefinitionBuilder("auth-server-url-for-backend-requests", ModelType.STRING, true)
+                    .setXmlName("auth-server-url-for-backend-requests")
+                    .setAllowExpression(true)
+                    .setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, true, true))
+                    .build();
+    protected static final SimpleAttributeDefinition ALWAYS_REFRESH_TOKEN =
+            new SimpleAttributeDefinitionBuilder("always-refresh-token", ModelType.BOOLEAN, true)
+                    .setXmlName("always-refresh-token")
+                    .setAllowExpression(true)
+                    .setDefaultValue(new ModelNode(false))
+                    .build();
+    protected static final SimpleAttributeDefinition REGISTER_NODE_AT_STARTUP =
+            new SimpleAttributeDefinitionBuilder("register-node-at-startup", ModelType.BOOLEAN, true)
+                    .setXmlName("register-node-at-startup")
+                    .setAllowExpression(true)
+                    .setDefaultValue(new ModelNode(false))
+                    .build();
+    protected static final SimpleAttributeDefinition REGISTER_NODE_PERIOD =
+            new SimpleAttributeDefinitionBuilder("register-node-period", ModelType.INT, true)
+                    .setXmlName("register-node-period")
+                    .setAllowExpression(true)
+                    .setValidator(new IntRangeValidator(-1, true))
+                    .build();
+    protected static final SimpleAttributeDefinition TOKEN_STORE =
+            new SimpleAttributeDefinitionBuilder("token-store", ModelType.STRING, true)
+                    .setXmlName("token-store")
+                    .setAllowExpression(true)
+                    .setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, true, true))
+                    .build();
+    protected static final SimpleAttributeDefinition PRINCIPAL_ATTRIBUTE =
+            new SimpleAttributeDefinitionBuilder("principal-attribute", ModelType.STRING, true)
+                    .setXmlName("principal-attribute")
+                    .setAllowExpression(true)
+                    .setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, true, true))
+                    .build();
+
+
+
+    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_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);
+        ATTRIBUTES.add(CLIENT_KEY_PASSWORD);
+        ATTRIBUTES.add(CORS_MAX_AGE);
+        ATTRIBUTES.add(CORS_ALLOWED_HEADERS);
+        ATTRIBUTES.add(CORS_ALLOWED_METHODS);
+        ATTRIBUTES.add(EXPOSE_TOKEN);
+        ATTRIBUTES.add(AUTH_SERVER_URL_FOR_BACKEND_REQUESTS);
+        ATTRIBUTES.add(ALWAYS_REFRESH_TOKEN);
+        ATTRIBUTES.add(REGISTER_NODE_AT_STARTUP);
+        ATTRIBUTES.add(REGISTER_NODE_PERIOD);
+        ATTRIBUTES.add(TOKEN_STORE);
+        ATTRIBUTES.add(PRINCIPAL_ATTRIBUTE);
+    }
+
+    /**
+     * truststore and truststore-password must be set if ssl-required is not none and disable-trust-manager is 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, DISABLE_TRUST_MANAGER)) {
+            return true;
+        }
+
+        if (isSet(attributes, SSL_REQUIRED) && attributes.get(SSL_REQUIRED.getName()).asString().equals("none")) {
+            return true;
+        }
+
+        return isSet(attributes, TRUSTSTORE) && isSet(attributes, TRUSTSTORE_PASSWORD);
+    }
+
+    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();
+    }
+
+
+}
diff --git a/integration/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/logging/KeycloakLogger.java b/integration/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/logging/KeycloakLogger.java
new file mode 100755
index 0000000..292fa65
--- /dev/null
+++ b/integration/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/logging/KeycloakLogger.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.keycloak.subsystem.wf8.logging;
+
+import org.jboss.logging.BasicLogger;
+import org.jboss.logging.Logger;
+import org.jboss.logging.annotations.LogMessage;
+import org.jboss.logging.annotations.Message;
+import org.jboss.logging.annotations.MessageLogger;
+
+import static org.jboss.logging.Logger.Level.INFO;
+
+/**
+ * This interface to be fleshed out later when error messages are fully externalized.
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc.
+ */
+@MessageLogger(projectCode = "KEYCLOAK")
+public interface KeycloakLogger extends BasicLogger {
+
+    /**
+     * A logger with a category of the package name.
+     */
+    KeycloakLogger ROOT_LOGGER = Logger.getMessageLogger(KeycloakLogger.class, "org.jboss.keycloak");
+
+    @LogMessage(level = INFO)
+    @Message(value = "Keycloak subsystem override for deployment %s")
+    void deploymentSecured(String deployment);
+
+
+}
diff --git a/integration/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/logging/KeycloakMessages.java b/integration/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/logging/KeycloakMessages.java
new file mode 100755
index 0000000..9d456c8
--- /dev/null
+++ b/integration/wildfly/wf8-subsystem/src/main/java/org/keycloak/subsystem/wf8/logging/KeycloakMessages.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.keycloak.subsystem.wf8.logging;
+
+import org.jboss.logging.Messages;
+import org.jboss.logging.annotations.MessageBundle;
+
+/**
+ * This interface to be fleshed out later when error messages are fully externalized.
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2012 Red Hat Inc.
+ */
+@MessageBundle(projectCode = "KEYCLOAK")
+public interface KeycloakMessages {
+
+    /**
+     * The messages
+     */
+    KeycloakMessages MESSAGES = Messages.getBundle(KeycloakMessages.class);
+}
diff --git a/integration/wildfly/wf8-subsystem/src/main/resources/META-INF/services/org.jboss.as.controller.Extension b/integration/wildfly/wf8-subsystem/src/main/resources/META-INF/services/org.jboss.as.controller.Extension
new file mode 100644
index 0000000..1f25766
--- /dev/null
+++ b/integration/wildfly/wf8-subsystem/src/main/resources/META-INF/services/org.jboss.as.controller.Extension
@@ -0,0 +1 @@
+org.keycloak.subsystem.wf8.extension.KeycloakExtension
diff --git a/integration/wildfly/wf8-subsystem/src/main/resources/org/keycloak/subsystem/wf8/extension/LocalDescriptions.properties b/integration/wildfly/wf8-subsystem/src/main/resources/org/keycloak/subsystem/wf8/extension/LocalDescriptions.properties
new file mode 100755
index 0000000..c00bd8d
--- /dev/null
+++ b/integration/wildfly/wf8-subsystem/src/main/resources/org/keycloak/subsystem/wf8/extension/LocalDescriptions.properties
@@ -0,0 +1,72 @@
+keycloak.subsystem=Keycloak adapter subsystem
+keycloak.subsystem.add=Operation Adds Keycloak adapter subsystem
+keycloak.subsystem.remove=Operation removes Keycloak adapter 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.
+keycloak.realm.remove=Remove a realm from the subsystem.
+keycloak.realm.realm-public-key=Public key of the realm
+keycloak.realm.auth-server-url=Base URL of the Realm Auth Server
+keycloak.realm.disable-trust-manager=Adapter will not use a trust manager when making adapter HTTPS requests
+keycloak.realm.ssl-required=Specify if SSL is required (valid values are all, external and none)
+keycloak.realm.allow-any-hostname=SSL Setting
+keycloak.realm.truststore=Truststore used for adapter client HTTPS requests
+keycloak.realm.truststore-password=Password of the Truststore
+keycloak.realm.connection-pool-size=Connection pool size for the client used by the adapter
+keycloak.realm.enable-cors=Enable Keycloak CORS support
+keycloak.realm.client-keystore=n/a
+keycloak.realm.client-keystore-password=n/a
+keycloak.realm.client-key-password=n/a
+keycloak.realm.cors-max-age=CORS max-age header
+keycloak.realm.cors-allowed-headers=CORS allowed headers
+keycloak.realm.cors-allowed-methods=CORS allowed methods
+keycloak.realm.expose-token=Enable secure URL that exposes access token
+keycloak.realm.auth-server-url-for-backend-requests=URL to use to make background calls to auth server
+keycloak.realm.always-refresh-token=Refresh token on every single web request
+keycloak.realm.register-node-at-startup=Cluster setting
+keycloak.realm.register-node-period=how often to re-register node
+keycloak.realm.token-store=cookie or session storage for auth session data
+keycloak.realm.principal-attribute=token attribute to use to set Principal name
+
+
+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=Public key of the realm
+keycloak.secure-deployment.auth-server-url=Base URL of the Realm Auth Server
+keycloak.secure-deployment.disable-trust-manager=Adapter will not use a trust manager when making adapter HTTPS requests
+keycloak.secure-deployment.ssl-required=Specify if SSL is required (valid values are all, external and none)
+keycloak.secure-deployment.allow-any-hostname=SSL Setting
+keycloak.secure-deployment.truststore=Truststore used for adapter client HTTPS requests
+keycloak.secure-deployment.truststore-password=Password of the Truststore
+keycloak.secure-deployment.connection-pool-size=Connection pool size for the client used by the adapter
+keycloak.secure-deployment.resource=Application name
+keycloak.secure-deployment.use-resource-role-mappings=Use resource level permissions from token
+keycloak.secure-deployment.credentials=Adapter credentials
+keycloak.secure-deployment.bearer-only=Bearer Token Auth only
+keycloak.secure-deployment.enable-basic-auth=Enable Basic Authentication
+keycloak.secure-deployment.public-client=Public client
+keycloak.secure-deployment.enable-cors=Enable Keycloak CORS support
+keycloak.secure-deployment.client-keystore=n/a
+keycloak.secure-deployment.client-keystore-password=n/a
+keycloak.secure-deployment.client-key-password=n/a
+keycloak.secure-deployment.cors-max-age=CORS max-age header
+keycloak.secure-deployment.cors-allowed-headers=CORS allowed headers
+keycloak.secure-deployment.cors-allowed-methods=CORS allowed methods
+keycloak.secure-deployment.expose-token=Enable secure URL that exposes access token
+keycloak.secure-deployment.auth-server-url-for-backend-requests=URL to use to make background calls to auth server
+keycloak.secure-deployment.always-refresh-token=Refresh token on every single web request
+keycloak.secure-deployment.register-node-at-startup=Cluster setting
+keycloak.secure-deployment.register-node-period=how often to re-register node
+keycloak.secure-deployment.token-store=cookie or session storage for auth session data
+keycloak.secure-deployment.principal-attribute=token attribute to use to set Principal name
+
+keycloak.secure-deployment.credential=Credential value
+
+keycloak.credential=Credential
+keycloak.credential.value=Credential value
+keycloak.credential.add=Credential add
+keycloak.credential.remove=Credential remove
\ No newline at end of file
diff --git a/integration/wildfly/wf8-subsystem/src/main/resources/schema/wildfly-keycloak_1_1.xsd b/integration/wildfly/wf8-subsystem/src/main/resources/schema/wildfly-keycloak_1_1.xsd
new file mode 100755
index 0000000..269b323
--- /dev/null
+++ b/integration/wildfly/wf8-subsystem/src/main/resources/schema/wildfly-keycloak_1_1.xsd
@@ -0,0 +1,105 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
+           targetNamespace="urn:jboss:domain:keycloak:1.1"
+           xmlns="urn:jboss:domain:keycloak:1.1"
+           elementFormDefault="qualified"
+           attributeFormDefault="unqualified"
+           version="1.0">
+
+    <!-- The subsystem root element -->
+    <xs:element name="subsystem" type="subsystem-type"/>
+
+    <xs:complexType name="subsystem-type">
+        <xs:annotation>
+            <xs:documentation>
+                <![CDATA[
+                    The Keycloak adapter subsystem, used to register deployments managed by Keycloak
+                ]]>
+            </xs:documentation>
+        </xs:annotation>
+        <xs:choice minOccurs="0" maxOccurs="unbounded">
+            <xs:element name="realm" maxOccurs="unbounded" minOccurs="0" type="realm-type"/>
+            <xs:element name="secure-deployment" maxOccurs="unbounded" minOccurs="0" type="secure-deployment-type"/>
+        </xs:choice>
+    </xs:complexType>
+
+    <xs:complexType name="realm-type">
+        <xs:all>
+            <xs:element name="cors-allowed-headers" type="xs:string" minOccurs="0" maxOccurs="1"/>
+            <xs:element name="client-keystore-password" type="xs:string" minOccurs="0" maxOccurs="1"/>
+            <xs:element name="client-keystore" type="xs:string" 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="enable-cors" type="xs:boolean" minOccurs="0" maxOccurs="1"/>
+            <xs:element name="allow-any-hostname" type="xs:boolean" minOccurs="0" maxOccurs="1" />
+            <xs:element name="client-key-password" type="xs:string" minOccurs="0" maxOccurs="1"/>
+            <xs:element name="connection-pool-size" type="xs:integer" minOccurs="0" maxOccurs="1"/>
+            <xs:element name="cors-max-age" type="xs:integer" minOccurs="0" maxOccurs="1"/>
+            <xs:element name="auth-server-url" type="xs:string" minOccurs="1" maxOccurs="1"/>
+            <xs:element name="expose-token" type="xs:boolean" minOccurs="0" maxOccurs="1"/>
+            <xs:element name="disable-trust-manager" type="xs:boolean" minOccurs="0" maxOccurs="1" />
+            <xs:element name="ssl-required" type="xs:string" minOccurs="0" maxOccurs="1" />
+            <xs:element name="cors-allowed-methods" type="xs:string" minOccurs="0" maxOccurs="1"/>
+            <xs:element name="realm-public-key" type="xs:string" minOccurs="1" maxOccurs="1"/>
+            <xs:element name="auth-server-url-for-backend-requests" type="xs:string" minOccurs="0" maxOccurs="1"/>
+            <xs:element name="always-refresh-token" type="xs:boolean" minOccurs="0" maxOccurs="1"/>
+            <xs:element name="register-node-at-startup" type="xs:boolean" minOccurs="0" maxOccurs="1"/>
+            <xs:element name="register-node-period" type="xs:integer" minOccurs="0" maxOccurs="1"/>
+            <xs:element name="token-store" type="xs:string" minOccurs="0" maxOccurs="1"/>
+            <xs:element name="principal-attribute" type="xs:string" minOccurs="0" maxOccurs="1"/>
+        </xs:all>
+        <xs:attribute name="name" type="xs:string" use="required">
+            <xs:annotation>
+                <xs:documentation>The name of the realm.</xs:documentation>
+            </xs:annotation>
+        </xs:attribute>
+    </xs:complexType>
+    
+    <xs:complexType name="secure-deployment-type">
+        <xs:all>
+            <xs:element name="client-keystore-password" type="xs:string" minOccurs="0" maxOccurs="1"/>
+            <xs:element name="client-keystore" type="xs:string" minOccurs="0" maxOccurs="1"/>
+            <xs:element name="enable-cors" type="xs:boolean" minOccurs="0" maxOccurs="1"/>
+            <xs:element name="allow-any-hostname" type="xs:boolean" minOccurs="0" maxOccurs="1" />
+            <xs:element name="use-resource-role-mappings" type="xs:boolean" minOccurs="0" maxOccurs="1" />
+            <xs:element name="cors-max-age" type="xs:integer" minOccurs="0" maxOccurs="1"/>
+            <xs:element name="auth-server-url" type="xs:string" minOccurs="1" maxOccurs="1"/>
+            <xs:element name="realm" type="xs:string" minOccurs="1" maxOccurs="1"/>
+            <xs:element name="disable-trust-manager" type="xs:boolean" minOccurs="0" maxOccurs="1" />
+            <xs:element name="cors-allowed-methods" type="xs:string" minOccurs="0" maxOccurs="1"/>
+            <xs:element name="bearer-only" type="xs:boolean" minOccurs="0" maxOccurs="1" />
+            <xs:element name="cors-allowed-headers" type="xs:string" minOccurs="0" maxOccurs="1"/>
+            <xs:element name="resource" type="xs:string" 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="client-key-password" type="xs:string" minOccurs="0" maxOccurs="1"/>
+            <xs:element name="public-client" type="xs:boolean" minOccurs="0" maxOccurs="1"/>
+            <xs:element name="connection-pool-size" type="xs:integer" minOccurs="0" maxOccurs="1"/>
+            <xs:element name="expose-token" type="xs:boolean" minOccurs="0" maxOccurs="1"/>
+            <xs:element name="ssl-required" type="xs:string" minOccurs="0" maxOccurs="1" />
+            <xs:element name="realm-public-key" type="xs:string" minOccurs="1" maxOccurs="1"/>
+            <xs:element name="credential" type="credential-type" minOccurs="1" maxOccurs="1"/>
+            <xs:element name="auth-server-url-for-backend-requests" type="xs:string" minOccurs="0" maxOccurs="1"/>
+            <xs:element name="always-refresh-token" type="xs:boolean" minOccurs="0" maxOccurs="1"/>
+            <xs:element name="register-node-at-startup" type="xs:boolean" minOccurs="0" maxOccurs="1"/>
+            <xs:element name="register-node-period" type="xs:integer" minOccurs="0" maxOccurs="1"/>
+            <xs:element name="token-store" type="xs:string" minOccurs="0" maxOccurs="1"/>
+            <xs:element name="principal-attribute" type="xs:string" minOccurs="0" maxOccurs="1"/>
+            <xs:element name="enable-basic-auth" type="xs:boolean" minOccurs="0" maxOccurs="1"/>
+        </xs:all>
+        <xs:attribute name="name" type="xs:string" use="required">
+            <xs:annotation>
+                <xs:documentation>The name of the realm.</xs:documentation>
+            </xs:annotation>
+        </xs:attribute>
+    </xs:complexType>
+    
+    <xs:complexType name="credential-type">
+        <xs:simpleContent>
+            <xs:extension base="xs:string">
+                <xs:attribute name="name" type="xs:string" />
+            </xs:extension>
+        </xs:simpleContent>
+    </xs:complexType>
+</xs:schema>
diff --git a/integration/wildfly/wf8-subsystem/src/main/resources/subsystem-templates/keycloak-adapter.xml b/integration/wildfly/wf8-subsystem/src/main/resources/subsystem-templates/keycloak-adapter.xml
new file mode 100644
index 0000000..0abb124
--- /dev/null
+++ b/integration/wildfly/wf8-subsystem/src/main/resources/subsystem-templates/keycloak-adapter.xml
@@ -0,0 +1,7 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!--  Template used by WildFly build when directed to include Keycloak subsystem in a configuration. -->
+<config>
+   <extension-module>org.keycloak.keycloak-wf8-subsystem</extension-module>
+   <subsystem xmlns="urn:jboss:domain:keycloak:1.1">
+   </subsystem>
+</config>
diff --git a/integration/wildfly/wf8-subsystem/src/test/java/org/keycloak/subsystem/wf8/extension/RealmDefinitionTestCase.java b/integration/wildfly/wf8-subsystem/src/test/java/org/keycloak/subsystem/wf8/extension/RealmDefinitionTestCase.java
new file mode 100755
index 0000000..1afeec4
--- /dev/null
+++ b/integration/wildfly/wf8-subsystem/src/test/java/org/keycloak/subsystem/wf8/extension/RealmDefinitionTestCase.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.keycloak.subsystem.wf8.extension;
+
+import org.jboss.dmr.ModelNode;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc.
+ */
+public class RealmDefinitionTestCase {
+
+    private ModelNode model;
+
+    @Before
+    public void setUp() {
+        model = new ModelNode();
+        model.get("realm").set("demo");
+        model.get("resource").set("customer-portal");
+        model.get("realm-public-key").set("MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB");
+        model.get("auth-url").set("http://localhost:8080/auth-server/rest/realms/demo/protocol/openid-connect/login");
+        model.get("code-url").set("http://localhost:8080/auth-server/rest/realms/demo/protocol/openid-connect/access/codes");
+        model.get("expose-token").set(true);
+        ModelNode credential = new ModelNode();
+        credential.get("password").set("password");
+        model.get("credentials").set(credential);
+    }
+
+    @Test
+    public void testIsTruststoreSetIfRequired() throws Exception {
+        model.get("ssl-required").set("none");
+        model.get("disable-trust-manager").set(true);
+        Assert.assertTrue(SharedAttributeDefinitons.validateTruststoreSetIfRequired(model));
+
+        model.get("ssl-required").set("none");
+        model.get("disable-trust-manager").set(false);
+        Assert.assertTrue(SharedAttributeDefinitons.validateTruststoreSetIfRequired(model));
+
+        model.get("ssl-required").set("all");
+        model.get("disable-trust-manager").set(true);
+        Assert.assertTrue(SharedAttributeDefinitons.validateTruststoreSetIfRequired(model));
+
+        model.get("ssl-required").set("all");
+        model.get("disable-trust-manager").set(false);
+        Assert.assertFalse(SharedAttributeDefinitons.validateTruststoreSetIfRequired(model));
+
+        model.get("ssl-required").set("external");
+        model.get("disable-trust-manager").set(false);
+        Assert.assertFalse(SharedAttributeDefinitons.validateTruststoreSetIfRequired(model));
+
+        model.get("ssl-required").set("all");
+        model.get("disable-trust-manager").set(false);
+        model.get("truststore").set("foo");
+        Assert.assertFalse(SharedAttributeDefinitons.validateTruststoreSetIfRequired(model));
+
+        model.get("ssl-required").set("all");
+        model.get("disable-trust-manager").set(false);
+        model.get("truststore").set("foo");
+        model.get("truststore-password").set("password");
+        Assert.assertTrue(SharedAttributeDefinitons.validateTruststoreSetIfRequired(model));
+
+        model.get("ssl-required").set("external");
+        model.get("disable-trust-manager").set(false);
+        model.get("truststore").set("foo");
+        model.get("truststore-password").set("password");
+        Assert.assertTrue(SharedAttributeDefinitons.validateTruststoreSetIfRequired(model));
+    }
+
+}
diff --git a/integration/wildfly/wf8-subsystem/src/test/java/org/keycloak/subsystem/wf8/extension/SubsystemParsingTestCase.java b/integration/wildfly/wf8-subsystem/src/test/java/org/keycloak/subsystem/wf8/extension/SubsystemParsingTestCase.java
new file mode 100755
index 0000000..93e9a59
--- /dev/null
+++ b/integration/wildfly/wf8-subsystem/src/test/java/org/keycloak/subsystem/wf8/extension/SubsystemParsingTestCase.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.keycloak.subsystem.wf8.extension;
+
+import org.jboss.as.subsystem.test.AbstractSubsystemBaseTest;
+import org.jboss.dmr.ModelNode;
+import org.junit.Test;
+
+import java.io.IOException;
+
+
+/**
+ * Tests all management expects for subsystem, parsing, marshaling, model definition and other
+ * Here is an example that allows you a fine grained controller over what is tested and how. So it can give you ideas what can be done and tested.
+ * If you have no need for advanced testing of subsystem you look at {@link SubsystemBaseParsingTestCase} that testes same stuff but most of the code
+ * is hidden inside of test harness
+ *
+ * @author <a href="kabir.khan@jboss.com">Kabir Khan</a>
+ * @author Tomaz Cerar
+ * @author <a href="marko.strukelj@gmail.com">Marko Strukelj</a>
+ */
+public class SubsystemParsingTestCase extends AbstractSubsystemBaseTest {
+
+    public SubsystemParsingTestCase() {
+        super(KeycloakExtension.SUBSYSTEM_NAME, new KeycloakExtension());
+    }
+
+    @Test
+    public void testJson() throws Exception {
+        ModelNode node = new ModelNode();
+        node.get("realm").set("demo");
+        node.get("resource").set("customer-portal");
+        node.get("realm-public-key").set("MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB");
+        node.get("auth-url").set("http://localhost:8080/auth-server/rest/realms/demo/protocol/openid-connect/login");
+        node.get("code-url").set("http://localhost:8080/auth-server/rest/realms/demo/protocol/openid-connect/access/codes");
+        node.get("ssl-required").set("external");
+        node.get("expose-token").set(true);
+        ModelNode credential = new ModelNode();
+        credential.get("password").set("password");
+        node.get("credentials").set(credential);
+
+        System.out.println("json=" + node.toJSONString(false));
+    }
+
+    @Override
+    protected String getSubsystemXml() throws IOException {
+        return readResource("keycloak-1.1.xml");
+    }
+}
diff --git a/integration/wildfly/wf8-subsystem/src/test/resources/org/keycloak/subsystem/wf8/extension/keycloak-1.1.xml b/integration/wildfly/wf8-subsystem/src/test/resources/org/keycloak/subsystem/wf8/extension/keycloak-1.1.xml
new file mode 100644
index 0000000..2d12d88
--- /dev/null
+++ b/integration/wildfly/wf8-subsystem/src/test/resources/org/keycloak/subsystem/wf8/extension/keycloak-1.1.xml
@@ -0,0 +1,24 @@
+<subsystem xmlns="urn:jboss:domain:keycloak:1.1">
+    <secure-deployment name="web-console">
+        <realm>master</realm>
+        <resource>web-console</resource>
+        <use-resource-role-mappings>true</use-resource-role-mappings>
+        <realm-public-key>
+            MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC4siLKUew0WYxdtq6/rwk4Uj/4amGFFnE/yzIxQVU0PUqz3QBRVkUWpDj0K6ZnS5nzJV/y6DHLEy7hjZTdRDphyF1sq09aDOYnVpzu8o2sIlMM8q5RnUyEfIyUZqwo8pSZDJ90fS0s+IDUJNCSIrAKO3w1lqZDHL6E/YFHXyzkvQIDAQAB
+        </realm-public-key>
+        <auth-server-url>http://localhost:8080/auth</auth-server-url>
+        <ssl-required>EXTERNAL</ssl-required>
+        <credential name="secret">0aa31d98-e0aa-404c-b6e0-e771dba1e798</credential>
+    </secure-deployment>
+    <secure-deployment name="http-endpoint">
+        <realm>master</realm>
+        <resource>http-endpoint</resource>
+        <use-resource-role-mappings>true</use-resource-role-mappings>
+        <realm-public-key>
+            MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC4siLKUew0WYxdtq6/rwk4Uj/4amGFFnE/yzIxQVU0PUqz3QBRVkUWpDj0K6ZnS5nzJV/y6DHLEy7hjZTdRDphyF1sq09aDOYnVpzu8o2sIlMM8q5RnUyEfIyUZqwo8pSZDJ90fS0s+IDUJNCSIrAKO3w1lqZDHL6E/YFHXyzkvQIDAQAB
+        </realm-public-key>
+        <auth-server-url>http://localhost:8080/auth</auth-server-url>
+        <ssl-required>EXTERNAL</ssl-required>
+        <credential name="secret">2769a4a2-5be0-454f-838f-f33b7755b667</credential>
+    </secure-deployment>
+</subsystem>
\ No newline at end of file
diff --git a/model/api/src/main/java/org/keycloak/mappers/MapperConfigValidationException.java b/model/api/src/main/java/org/keycloak/mappers/MapperConfigValidationException.java
new file mode 100644
index 0000000..ca714a9
--- /dev/null
+++ b/model/api/src/main/java/org/keycloak/mappers/MapperConfigValidationException.java
@@ -0,0 +1,15 @@
+package org.keycloak.mappers;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class MapperConfigValidationException extends Exception {
+
+    public MapperConfigValidationException(String message) {
+        super(message);
+    }
+
+    public MapperConfigValidationException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/model/api/src/main/java/org/keycloak/mappers/UserFederationMapperFactory.java b/model/api/src/main/java/org/keycloak/mappers/UserFederationMapperFactory.java
index 386ce13..309036c 100644
--- a/model/api/src/main/java/org/keycloak/mappers/UserFederationMapperFactory.java
+++ b/model/api/src/main/java/org/keycloak/mappers/UserFederationMapperFactory.java
@@ -1,10 +1,36 @@
 package org.keycloak.mappers;
 
+import java.util.List;
+
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserFederationMapperModel;
 import org.keycloak.provider.ConfiguredProvider;
+import org.keycloak.provider.ProviderConfigProperty;
 import org.keycloak.provider.ProviderFactory;
 
 /**
  * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
  */
 public interface UserFederationMapperFactory extends ProviderFactory<UserFederationMapper>, ConfiguredProvider {
+
+    /**
+     * Refers to providerName (type) of the federation provider, which this mapper can be used for. For example "ldap" or "kerberos"
+     *
+     * @return providerName
+     */
+    String getFederationProviderType();
+
+    String getDisplayCategory();
+    String getDisplayType();
+
+    /**
+     * Called when instance of mapperModel is created for this factory through admin endpoint
+     *
+     * @param mapperModel
+     * @throws MapperConfigValidationException if configuration provided in mapperModel is not valid
+     */
+    void validateConfig(UserFederationMapperModel mapperModel) throws MapperConfigValidationException;
+
+    // TODO: Remove this and add realm to the method on ConfiguredProvider?
+    List<ProviderConfigProperty> getConfigProperties(RealmModel realm);
 }
diff --git a/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java b/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java
index 988d9ec..77f825f 100755
--- a/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java
+++ b/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java
@@ -1,6 +1,7 @@
 package org.keycloak.models.utils;
 
 import org.bouncycastle.openssl.PEMWriter;
+import org.keycloak.constants.KerberosConstants;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.KeycloakSessionFactory;
@@ -8,6 +9,7 @@ import org.keycloak.models.KeycloakSessionTask;
 import org.keycloak.models.KeycloakTransaction;
 import org.keycloak.models.ModelDuplicateException;
 import org.keycloak.models.RealmModel;
+import org.keycloak.models.RequiredCredentialModel;
 import org.keycloak.models.RoleModel;
 import org.keycloak.models.UserCredentialModel;
 import org.keycloak.models.UserFederationMapperModel;
@@ -349,4 +351,31 @@ public final class KeycloakModelUtils {
 
         return mapperModel;
     }
+
+    /**
+     * Automatically add "kerberos" to required realm credentials if it's supported by saved provider
+     *
+     * @param realm
+     * @param model
+     * @return true if kerberos credentials were added
+     */
+    public static boolean checkKerberosCredential(RealmModel realm, UserFederationProviderModel model) {
+        String allowKerberosCfg = model.getConfig().get(KerberosConstants.ALLOW_KERBEROS_AUTHENTICATION);
+        if (Boolean.valueOf(allowKerberosCfg)) {
+            boolean found = false;
+            List<RequiredCredentialModel> currentCreds = realm.getRequiredCredentials();
+            for (RequiredCredentialModel cred : currentCreds) {
+                if (cred.getType().equals(UserCredentialModel.KERBEROS)) {
+                    found = true;
+                }
+            }
+
+            if (!found) {
+                realm.addRequiredCredential(UserCredentialModel.KERBEROS);
+                return true;
+            }
+        }
+
+        return false;
+    }
 }

pom.xml 12(+11 -1)

diff --git a/pom.xml b/pom.xml
index 5b846b3..5ba74c6 100755
--- a/pom.xml
+++ b/pom.xml
@@ -1081,6 +1081,12 @@
             </dependency>
             <dependency>
                 <groupId>org.keycloak</groupId>
+                <artifactId>keycloak-wf8-modules</artifactId>
+                <version>${project.version}</version>
+                <type>zip</type>
+            </dependency>
+            <dependency>
+                <groupId>org.keycloak</groupId>
                 <artifactId>keycloak-server-overlay</artifactId>
                 <version>${project.version}</version>
                 <type>zip</type>
@@ -1272,7 +1278,9 @@
         <profile>
             <id>jboss-earlyaccess-repository</id>
             <activation>
-                <activeByDefault>true</activeByDefault>
+                <property>
+                    <name>!no-jboss-ea-repo</name>
+                </property>
             </activation>
             <repositories>
                 <repository>
@@ -1303,6 +1311,7 @@
             <id>distribution</id>
             <modules>
                 <module>distribution</module>
+                <module>testsuite/integration-arquillian</module>
             </modules>
         </profile>
         <profile>
@@ -1310,6 +1319,7 @@
             <modules>
                 <module>docbook</module>
                 <module>distribution</module>
+                <module>testsuite/integration-arquillian</module>
             </modules>
             <build>
                 <plugins>
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
index c2a4730..e2899fe 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
@@ -235,8 +235,8 @@ public class RealmAdminResource {
     }
 
     @Path("user-federation")
-    public UserFederationResource userFederation() {
-        UserFederationResource fed = new UserFederationResource(realm, auth, adminEvent);
+    public UserFederationProvidersResource userFederation() {
+        UserFederationProvidersResource fed = new UserFederationProvidersResource(realm, auth, adminEvent);
         ResteasyProviderFactory.getInstance().injectProperties(fed);
         //resourceContext.initResource(fed);
         return fed;
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/UserFederationProviderResource.java b/services/src/main/java/org/keycloak/services/resources/admin/UserFederationProviderResource.java
new file mode 100644
index 0000000..e5deb3d
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/resources/admin/UserFederationProviderResource.java
@@ -0,0 +1,298 @@
+package org.keycloak.services.resources.admin;
+
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+
+import org.jboss.logging.Logger;
+import org.jboss.resteasy.annotations.cache.NoCache;
+import org.jboss.resteasy.spi.NotFoundException;
+import org.keycloak.events.admin.OperationType;
+import org.keycloak.mappers.MapperConfigValidationException;
+import org.keycloak.mappers.UserFederationMapper;
+import org.keycloak.mappers.UserFederationMapperFactory;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserFederationMapperModel;
+import org.keycloak.models.UserFederationProviderModel;
+import org.keycloak.models.UserFederationSyncResult;
+import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.models.utils.ModelToRepresentation;
+import org.keycloak.models.utils.RepresentationToModel;
+import org.keycloak.provider.ProviderConfigProperty;
+import org.keycloak.provider.ProviderFactory;
+import org.keycloak.representations.idm.ConfigPropertyRepresentation;
+import org.keycloak.representations.idm.UserFederationMapperRepresentation;
+import org.keycloak.representations.idm.UserFederationMapperTypeRepresentation;
+import org.keycloak.representations.idm.UserFederationProviderRepresentation;
+import org.keycloak.services.ErrorResponseException;
+import org.keycloak.services.managers.UsersSyncManager;
+import org.keycloak.timer.TimerProvider;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class UserFederationProviderResource {
+
+    protected static final Logger logger = Logger.getLogger(UserFederationProviderResource.class);
+
+    private final KeycloakSession session;
+    private final RealmModel realm;
+    private final RealmAuth auth;
+    private final UserFederationProviderModel federationProviderModel;
+    private final AdminEventBuilder adminEvent;
+
+    @Context
+    private UriInfo uriInfo;
+
+    public UserFederationProviderResource(KeycloakSession session, RealmModel realm, RealmAuth auth, UserFederationProviderModel federationProviderModel, AdminEventBuilder adminEvent) {
+        this.session = session;
+        this.realm = realm;
+        this.auth = auth;
+        this.federationProviderModel = federationProviderModel;
+        this.adminEvent = adminEvent;
+    }
+
+    /**
+     * Update a provider
+     *
+     * @param rep
+     */
+    @PUT
+    @NoCache
+    @Consumes(MediaType.APPLICATION_JSON)
+    public void updateProviderInstance(UserFederationProviderRepresentation rep) {
+        auth.requireManage();
+        String displayName = rep.getDisplayName();
+        if (displayName != null && displayName.trim().equals("")) {
+            displayName = null;
+        }
+        UserFederationProviderModel model = new UserFederationProviderModel(rep.getId(), rep.getProviderName(), rep.getConfig(), rep.getPriority(), displayName,
+                rep.getFullSyncPeriod(), rep.getChangedSyncPeriod(), rep.getLastSync());
+        realm.updateUserFederationProvider(model);
+        new UsersSyncManager().refreshPeriodicSyncForProvider(session.getKeycloakSessionFactory(), session.getProvider(TimerProvider.class), model, realm.getId());
+        boolean kerberosCredsAdded = KeycloakModelUtils.checkKerberosCredential(realm, model);
+        if (kerberosCredsAdded) {
+            logger.info("Added 'kerberos' to required realm credentials");
+        }
+
+        adminEvent.operation(OperationType.UPDATE).resourcePath(uriInfo).representation(rep).success();
+
+    }
+
+    /**
+     * get a provider
+     *
+     */
+    @GET
+    @NoCache
+    @Produces(MediaType.APPLICATION_JSON)
+    public UserFederationProviderRepresentation getProviderInstance() {
+        auth.requireView();
+        return ModelToRepresentation.toRepresentation(this.federationProviderModel);
+    }
+
+    /**
+     * Delete a provider
+     *
+     */
+    @DELETE
+    @NoCache
+    public void deleteProviderInstance() {
+        auth.requireManage();
+
+        realm.removeUserFederationProvider(this.federationProviderModel);
+        new UsersSyncManager().removePeriodicSyncForProvider(session.getProvider(TimerProvider.class), this.federationProviderModel);
+
+        adminEvent.operation(OperationType.DELETE).resourcePath(uriInfo).success();
+
+    }
+
+    /**
+     * trigger sync of users
+     *
+     * @return
+     */
+    @POST
+    @Path("sync")
+    @NoCache
+    public UserFederationSyncResult syncUsers(@QueryParam("action") String action) {
+        logger.debug("Syncing users");
+        auth.requireManage();
+
+        UsersSyncManager syncManager = new UsersSyncManager();
+        UserFederationSyncResult syncResult = null;
+        if ("triggerFullSync".equals(action)) {
+            syncResult = syncManager.syncAllUsers(session.getKeycloakSessionFactory(), realm.getId(), this.federationProviderModel);
+        } else if ("triggerChangedUsersSync".equals(action)) {
+            syncResult = syncManager.syncChangedUsers(session.getKeycloakSessionFactory(), realm.getId(), this.federationProviderModel);
+        }
+
+        adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo).success();
+        return syncResult;
+    }
+
+    /**
+     * List of available User Federation mapper types
+     *
+     * @return
+     */
+    @GET
+    @Path("mapper-types")
+    @NoCache
+    public Map<String, UserFederationMapperTypeRepresentation> getMapperTypes() {
+        this.auth.requireView();
+        KeycloakSessionFactory sessionFactory = session.getKeycloakSessionFactory();
+        Map<String, UserFederationMapperTypeRepresentation> types = new HashMap<>();
+        List<ProviderFactory> factories = sessionFactory.getProviderFactories(UserFederationMapper.class);
+
+        for (ProviderFactory factory : factories) {
+            UserFederationMapperFactory mapperFactory = (UserFederationMapperFactory)factory;
+            if (mapperFactory.getFederationProviderType().equals(this.federationProviderModel.getProviderName())) {
+
+                UserFederationMapperTypeRepresentation rep = new UserFederationMapperTypeRepresentation();
+                rep.setId(mapperFactory.getId());
+                rep.setCategory(mapperFactory.getDisplayCategory());
+                rep.setName(mapperFactory.getDisplayType());
+                rep.setHelpText(mapperFactory.getHelpText());
+                List<ProviderConfigProperty> configProperties = mapperFactory.getConfigProperties(realm);
+                for (ProviderConfigProperty prop : configProperties) {
+                    ConfigPropertyRepresentation propRep = new ConfigPropertyRepresentation();
+                    propRep.setName(prop.getName());
+                    propRep.setLabel(prop.getLabel());
+                    propRep.setType(prop.getType());
+                    propRep.setDefaultValue(prop.getDefaultValue());
+                    propRep.setHelpText(prop.getHelpText());
+                    rep.getProperties().add(propRep);
+                }
+                types.put(rep.getId(), rep);
+            }
+        }
+        return types;
+    }
+
+    /**
+     * Get mappers configured for this provider
+     *
+     * @return
+     */
+    @GET
+    @Path("mappers")
+    @Produces(MediaType.APPLICATION_JSON)
+    @NoCache
+    public List<UserFederationMapperRepresentation> getMappers() {
+        this.auth.requireView();
+        List<UserFederationMapperRepresentation> mappers = new LinkedList<>();
+        for (UserFederationMapperModel model : realm.getUserFederationMappersByFederationProvider(this.federationProviderModel.getId())) {
+            mappers.add(ModelToRepresentation.toRepresentation(realm, model));
+        }
+        return mappers;
+    }
+
+    /**
+     * Create mapper
+     *
+     * @param mapper
+     * @return
+     */
+    @POST
+    @Path("mappers")
+    @Consumes(MediaType.APPLICATION_JSON)
+    public Response addMapper(UserFederationMapperRepresentation mapper) {
+        auth.requireManage();
+        UserFederationMapperModel model = RepresentationToModel.toModel(realm, mapper);
+
+        validateModel(model);
+
+        model = realm.addUserFederationMapper(model);
+
+        adminEvent.operation(OperationType.CREATE).resourcePath(uriInfo, model.getId())
+                .representation(mapper).success();
+
+        return Response.created(uriInfo.getAbsolutePathBuilder().path(model.getId()).build()).build();
+
+    }
+
+    /**
+     * Get mapper
+     *
+     * @param id mapperId
+     * @return
+     */
+    @GET
+    @NoCache
+    @Path("mappers/{id}")
+    @Produces(MediaType.APPLICATION_JSON)
+    public UserFederationMapperRepresentation getMapperById(@PathParam("id") String id) {
+        auth.requireView();
+        UserFederationMapperModel model = realm.getUserFederationMapperById(id);
+        if (model == null) throw new NotFoundException("Model not found");
+        return ModelToRepresentation.toRepresentation(realm, model);
+    }
+
+    /**
+     * Update mapper
+     *
+     * @param id
+     * @param rep
+     */
+    @PUT
+    @NoCache
+    @Path("mappers/{id}")
+    @Consumes(MediaType.APPLICATION_JSON)
+    public void update(@PathParam("id") String id, UserFederationMapperRepresentation rep) {
+        auth.requireManage();
+        UserFederationMapperModel model = realm.getUserFederationMapperById(id);
+        if (model == null) throw new NotFoundException("Model not found");
+        model = RepresentationToModel.toModel(realm, rep);
+
+        validateModel(model);
+
+        realm.updateUserFederationMapper(model);
+        adminEvent.operation(OperationType.UPDATE).resourcePath(uriInfo).representation(rep).success();
+
+    }
+
+    /**
+     * Delete mapper with given ID
+     *
+     * @param id
+     */
+    @DELETE
+    @NoCache
+    @Path("mappers/{id}")
+    public void delete(@PathParam("id") String id) {
+        auth.requireManage();
+        UserFederationMapperModel model = realm.getUserFederationMapperById(id);
+        if (model == null) throw new NotFoundException("Model not found");
+        realm.removeUserFederationMapper(model);
+        adminEvent.operation(OperationType.DELETE).resourcePath(uriInfo).success();
+
+    }
+
+    private void validateModel(UserFederationMapperModel model) {
+        try {
+            UserFederationMapperFactory mapperFactory = (UserFederationMapperFactory) session.getKeycloakSessionFactory().getProviderFactory(UserFederationMapper.class, model.getFederationMapperType());
+            mapperFactory.validateConfig(model);
+        } catch (MapperConfigValidationException ex) {
+            throw new ErrorResponseException("Validation error", ex.getMessage(), Response.Status.BAD_REQUEST);
+        }
+    }
+
+}
diff --git a/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java b/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java
index 6daf649..d1545f2 100755
--- a/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java
+++ b/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java
@@ -892,6 +892,13 @@ public class LoginActionsService {
         ClientSessionModel clientSession = accessCode.getClientSession();
 
         String username = formData.getFirst("username");
+        if(username == null || username.isEmpty()) {
+            event.error(Errors.USERNAME_MISSING);
+            return session.getProvider(LoginFormsProvider.class)
+                    .setError(Messages.MISSING_USERNAME)
+                    .setClientSessionCode(accessCode.getCode())
+                    .createPasswordReset();
+        }
 
         ClientModel client = clientSession.getClient();
         if (client == null) {
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/FederationTestUtils.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/FederationTestUtils.java
index ddda4ff..d3dd9ab 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/FederationTestUtils.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/FederationTestUtils.java
@@ -91,7 +91,7 @@ class FederationTestUtils {
     }
 
     public static void addZipCodeLDAPMapper(RealmModel realm, UserFederationProviderModel providerModel) {
-        UserFederationMapperModel mapperModel = KeycloakModelUtils.createUserFederationMapperModel("zipCodeMapper", providerModel.getId(), UserAttributeLDAPFederationMapperFactory.ID,
+        UserFederationMapperModel mapperModel = KeycloakModelUtils.createUserFederationMapperModel("zipCodeMapper", providerModel.getId(), UserAttributeLDAPFederationMapperFactory.PROVIDER_ID,
                 UserAttributeLDAPFederationMapper.USER_MODEL_ATTRIBUTE, "postal_code",
                 UserAttributeLDAPFederationMapper.LDAP_ATTRIBUTE, LDAPConstants.POSTAL_CODE,
                 UserAttributeLDAPFederationMapper.READ_ONLY, "false");
@@ -104,7 +104,7 @@ class FederationTestUtils {
             mapperModel.getConfig().put(RoleLDAPFederationMapper.MODE, mode.toString());
             realm.updateUserFederationMapper(mapperModel);
         } else {
-            mapperModel = KeycloakModelUtils.createUserFederationMapperModel("realmRolesMapper", providerModel.getId(), RoleLDAPFederationMapperFactory.ID,
+            mapperModel = KeycloakModelUtils.createUserFederationMapperModel("realmRolesMapper", providerModel.getId(), RoleLDAPFederationMapperFactory.PROVIDER_ID,
                     RoleLDAPFederationMapper.ROLES_DN, "ou=RealmRoles,dc=keycloak,dc=org",
                     RoleLDAPFederationMapper.USE_REALM_ROLES_MAPPING, "true",
                     RoleLDAPFederationMapper.MODE, mode.toString());
@@ -116,7 +116,7 @@ class FederationTestUtils {
             mapperModel.getConfig().put(RoleLDAPFederationMapper.MODE, mode.toString());
             realm.updateUserFederationMapper(mapperModel);
         } else {
-            mapperModel = KeycloakModelUtils.createUserFederationMapperModel("financeRolesMapper", providerModel.getId(), RoleLDAPFederationMapperFactory.ID,
+            mapperModel = KeycloakModelUtils.createUserFederationMapperModel("financeRolesMapper", providerModel.getId(), RoleLDAPFederationMapperFactory.PROVIDER_ID,
                     RoleLDAPFederationMapper.ROLES_DN, "ou=FinanceRoles,dc=keycloak,dc=org",
                     RoleLDAPFederationMapper.USE_REALM_ROLES_MAPPING, "false",
                     RoleLDAPFederationMapper.CLIENT_ID, "finance",
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/ResetPasswordTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/ResetPasswordTest.java
index 68abb4a..ed94502 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/ResetPasswordTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/ResetPasswordTest.java
@@ -324,6 +324,25 @@ public class ResetPasswordTest {
 
         events.expectRequiredAction(EventType.SEND_RESET_PASSWORD).user((String) null).session((String) null).detail(Details.USERNAME, "invalid").removeDetail(Details.EMAIL).removeDetail(Details.CODE_ID).error("user_not_found").assertEvent();
     }
+    
+    @Test
+    public void resetPasswordMissingUsername() throws IOException, MessagingException, InterruptedException {
+        loginPage.open();
+        loginPage.resetPassword();
+
+        resetPasswordPage.assertCurrent();
+
+        resetPasswordPage.changePassword("");
+
+        resetPasswordPage.assertCurrent();
+
+        assertEquals("Please specify username.", resetPasswordPage.getErrorMessage());
+
+        assertEquals(0, greenMail.getReceivedMessages().length);
+        
+        events.expectRequiredAction(EventType.SEND_RESET_PASSWORD).client((String) null).user((String) null).session((String) null).clearDetails().error("username_missing").assertEvent();
+        
+    }
 
     @Test
     public void resetPasswordExpiredCode() throws IOException, MessagingException, InterruptedException {
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/ImportTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/ImportTest.java
index 4d20bc8..276698d 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/ImportTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/ImportTest.java
@@ -233,7 +233,7 @@ public class ImportTest extends AbstractModelTest {
         Assert.assertTrue(fedMappers1.size() == 1);
         UserFederationMapperModel fullNameMapper = fedMappers1.iterator().next();
         Assert.assertEquals("FullNameMapper", fullNameMapper.getName());
-        Assert.assertEquals(FullNameLDAPFederationMapperFactory.ID, fullNameMapper.getFederationMapperType());
+        Assert.assertEquals(FullNameLDAPFederationMapperFactory.PROVIDER_ID, fullNameMapper.getFederationMapperType());
         Assert.assertEquals(ldap1.getId(), fullNameMapper.getFederationProviderId());
         Assert.assertEquals("cn", fullNameMapper.getConfig().get(FullNameLDAPFederationMapper.LDAP_FULL_NAME_ATTRIBUTE));
 
diff --git a/testsuite/integration-arquillian/pom.xml b/testsuite/integration-arquillian/pom.xml
new file mode 100644
index 0000000..2435a1a
--- /dev/null
+++ b/testsuite/integration-arquillian/pom.xml
@@ -0,0 +1,182 @@
+<?xml version="1.0"?>
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+    <parent>
+        <artifactId>keycloak-testsuite-pom</artifactId>
+        <groupId>org.keycloak</groupId>
+        <version>1.3.0.Final-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>arquillian-integration</artifactId>
+    <name>KeyCloak Admin UI TestSuite</name>
+
+    <properties>
+        <browser>phantomjs</browser>
+		
+        <arquillian-core.version>1.1.5.Final</arquillian-core.version>
+        <selenium.version>2.45.0</selenium.version>
+        <arquillian-drone.version>1.3.1.Final</arquillian-drone.version>
+        <arquillian-phantomjs.version>1.1.4.Final</arquillian-phantomjs.version>
+        <arquillian-graphene.version>2.0.3.Final</arquillian-graphene.version>
+        <arquillian-wildfly-container.version>8.1.0.Final</arquillian-wildfly-container.version>
+        
+        <!-- Used in profile "wildfly-8-remote".
+        Set to "false" if admin password has already been updated after first login. -->
+        <firstAdminLogin>true</firstAdminLogin>
+    </properties>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.jboss.arquillian.selenium</groupId>
+                <artifactId>selenium-bom</artifactId>
+                <version>${selenium.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+            <dependency>
+                <groupId>org.jboss.arquillian</groupId>
+                <artifactId>arquillian-bom</artifactId>
+                <version>${arquillian-core.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+            <dependency>
+                <groupId>org.jboss.arquillian.extension</groupId>
+                <artifactId>arquillian-drone-bom</artifactId>
+                <version>${arquillian-drone.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+            <dependency>
+                <groupId>org.wildfly</groupId>
+                <artifactId>wildfly-arquillian-container-remote</artifactId>
+                <version>${arquillian-wildfly-container.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.wildfly</groupId>
+                <artifactId>wildfly-arquillian-container-managed</artifactId>
+                <version>${arquillian-wildfly-container.version}</version>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.jboss.arquillian.junit</groupId>
+            <artifactId>arquillian-junit-container</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.jboss.arquillian.graphene</groupId>
+            <artifactId>graphene-webdriver</artifactId>
+            <version>${arquillian-graphene.version}</version>
+            <type>pom</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.jboss.arquillian.extension</groupId>
+            <artifactId>arquillian-phantom-driver</artifactId>
+            <version>${arquillian-phantomjs.version}</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <profiles>
+        <profile>
+            <id>wildfly-8-remote</id>
+            <dependencies>
+                <dependency>
+                    <groupId>org.wildfly</groupId>
+                    <artifactId>wildfly-arquillian-container-remote</artifactId>
+                    <scope>test</scope>
+                </dependency>
+            </dependencies>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-surefire-plugin</artifactId>
+                        <configuration>
+                            <systemPropertyVariables>
+                                <shouldDeploy>false</shouldDeploy>
+                                <arquillian.launch>wildfly-8-remote</arquillian.launch>
+                                <browser>${browser}</browser>
+                                <firstAdminLogin>${first.login}</firstAdminLogin>
+                            </systemPropertyVariables>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+		
+        <profile>
+            <id>wildfly-8-managed</id>
+            <activation>
+                <activeByDefault>true</activeByDefault>
+            </activation>
+            <dependencies>
+                <dependency>
+                    <groupId>org.wildfly</groupId>
+                    <artifactId>wildfly-arquillian-container-managed</artifactId>
+                    <scope>test</scope>
+                </dependency>
+            </dependencies>
+            <properties>
+                <install.directory>${project.build.directory}/install</install.directory>
+                <jbossHome>${install.directory}/keycloak-${project.version}</jbossHome>
+            </properties>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-dependency-plugin</artifactId>
+                        <version>2.10</version>
+                        <executions>
+                            <execution>
+                                <id>unpack</id>
+                                <phase>process-test-resources</phase>
+                                <goals>
+                                    <goal>unpack</goal>
+                                </goals>
+                                <configuration>
+                                    <artifactItems>
+                                        <artifactItem>
+                                            <groupId>org.keycloak</groupId>
+                                            <artifactId>keycloak-server-dist</artifactId>
+                                            <version>${project.version}</version>
+                                            <type>zip</type>
+                                            <overWrite>false</overWrite>
+                                        </artifactItem>
+                                    </artifactItems>
+                                    <outputDirectory>${install.directory}</outputDirectory>
+                                    <overWriteReleases>false</overWriteReleases>
+                                    <overWriteSnapshots>true</overWriteSnapshots>
+                                </configuration>
+                            </execution>
+                        </executions>
+                    </plugin>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-surefire-plugin</artifactId>
+                        <configuration>
+                            <systemPropertyVariables>
+                                <shouldDeploy>false</shouldDeploy>
+                                <arquillian.launch>wildfly-8-managed</arquillian.launch>
+                                <browser>${browser}</browser>
+                                <jbossHome>${jbossHome}</jbossHome>
+                            </systemPropertyVariables>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+</project>
diff --git a/testsuite/integration-arquillian/README.md b/testsuite/integration-arquillian/README.md
new file mode 100644
index 0000000..a81701d
--- /dev/null
+++ b/testsuite/integration-arquillian/README.md
@@ -0,0 +1,21 @@
+Testing admin console with Arquillian
+=====================================
+
+There are currently two ways of running the tests with help of Arquillian.
+
+Remote mode
+----------
+
+Just simply typle `mvn verify` and you are all set. This requires the instance of Wildfly with embedded Keycloak to be already running.
+
+Managed mode
+------------
+
+You need to pass two arguments to Maven, first is location of your Wildfly server with embedded Keycloak and the other is name of the profile.
+
+    mvn verify -Pwildfly-8-managed -DjbossHome=/your/server/location
+
+Browser
+-------
+
+There are currently two supported browsers - PhantomJS and Firefox. PhantomJS is the default one, in order to use Firefox just specify `-Dbrowser=firefox` parameter in the Maven command. 
diff --git a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/AbstractKeyCloakTest.java b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/AbstractKeyCloakTest.java
new file mode 100644
index 0000000..aa1be23
--- /dev/null
+++ b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/AbstractKeyCloakTest.java
@@ -0,0 +1,48 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.ui;
+
+import java.util.concurrent.TimeUnit;
+import org.jboss.arquillian.graphene.page.Page;
+import org.junit.After;
+import org.junit.Before;
+import org.keycloak.testsuite.ui.page.AbstractPage;
+
+/**
+ *
+ * @author Petr Mensik
+ * @param <P>
+ */
+public abstract class AbstractKeyCloakTest<P extends AbstractPage> extends AbstractTest {
+	
+    @Page
+    protected P page;
+	
+	@Before
+	public void before() {
+        driver.manage().timeouts().setScriptTimeout(10, TimeUnit.SECONDS);
+		driver.manage().window().maximize();
+		loginAsAdmin();
+	}
+	
+	@After
+	public void after() {
+		logOut();
+	}
+}
diff --git a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/AbstractTest.java b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/AbstractTest.java
new file mode 100644
index 0000000..b05331e
--- /dev/null
+++ b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/AbstractTest.java
@@ -0,0 +1,73 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.ui;
+
+import org.jboss.arquillian.drone.api.annotation.Drone;
+import org.jboss.arquillian.graphene.page.Page;
+import org.jboss.arquillian.junit.Arquillian;
+import org.junit.runner.RunWith;
+import org.keycloak.testsuite.ui.fragment.Navigation;
+import org.keycloak.testsuite.ui.fragment.MenuPage;
+import org.keycloak.testsuite.ui.page.LoginPage;
+import org.keycloak.testsuite.ui.page.account.PasswordPage;
+import static org.keycloak.testsuite.ui.util.Constants.ADMIN_PSSWD;
+
+import static org.keycloak.testsuite.ui.util.URL.BASE_URL;
+import org.openqa.selenium.WebDriver;
+
+/**
+ *
+ * @author Petr Mensik
+ */
+@RunWith(Arquillian.class)
+public abstract class AbstractTest {
+
+    private static boolean firstAdminLogin = Boolean.parseBoolean(
+            System.getProperty("firstAdminLogin", "true"));
+
+    @Page
+    protected LoginPage loginPage;
+
+    @Page
+    protected PasswordPage passwordPage;
+
+    @Page
+    protected MenuPage menuPage;
+
+    @Page
+    protected Navigation navigation;
+
+    @Drone
+    protected WebDriver driver;
+
+    public void logOut() {
+        menuPage.logOut();
+    }
+
+    public void loginAsAdmin() {
+        driver.get(BASE_URL);
+        loginPage.loginAsAdmin();
+        if (firstAdminLogin) {
+            passwordPage.confirmNewPassword(ADMIN_PSSWD);
+            passwordPage.submit();
+            firstAdminLogin = false;
+        }
+    }
+
+}
diff --git a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/fragment/CreateRealm.java b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/fragment/CreateRealm.java
new file mode 100644
index 0000000..bae764f
--- /dev/null
+++ b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/fragment/CreateRealm.java
@@ -0,0 +1,52 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.ui.fragment;
+
+import org.jboss.arquillian.drone.api.annotation.Drone;
+import static org.openqa.selenium.By.id;
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ *
+ * @author Petr Mensik
+ */
+public class CreateRealm {
+	
+	@FindBy(css = ".btn-primary")
+	private WebElement primaryButton;
+	
+	@Drone
+	private WebDriver driver;
+	
+	public void importRealm(String filePath) {
+		driver.findElement(id("import-file")).sendKeys(filePath);
+		primaryButton.click();
+	}
+	
+	public void createRealm(String name, boolean on) {
+		driver.findElement(id("name")).sendKeys(name);
+		primaryButton.click();
+	}
+	
+	public void createRealm(String name) {
+		createRealm(name, true);
+	}
+}
diff --git a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/fragment/FlashMessage.java b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/fragment/FlashMessage.java
new file mode 100644
index 0000000..33a84af
--- /dev/null
+++ b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/fragment/FlashMessage.java
@@ -0,0 +1,67 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.ui.fragment;
+
+import static org.jboss.arquillian.graphene.Graphene.waitGui;
+import org.jboss.arquillian.graphene.fragment.Root;
+
+import static org.keycloak.testsuite.ui.util.SeleniumUtils.waitGuiForElement;
+import org.openqa.selenium.WebElement;
+
+/**
+ *
+ * @author Petr Mensik
+ */
+public class FlashMessage {
+
+    @Root
+    private WebElement root;
+
+    public boolean isSuccess() {
+		waitGui().until("Flash message should be success")
+			.element(root)
+			.attribute("class")
+			.contains("success");
+		return root.getAttribute("class").contains("success");
+    }
+
+    public boolean isError() {
+		waitGui().until("Flash message should be error")
+			.element(root)
+			.attribute("class")
+			.contains("error");
+        return root.getAttribute("class").contains("error");
+    }
+
+    public boolean isDanger() {
+		waitGui().until("Flash message should be danger")
+			.element(root)
+			.attribute("class")
+			.contains("danger");
+		return root.getAttribute("class").contains("danger"); 
+	}
+
+    public String getText() {
+        return root.getText();
+    }
+
+    public void waitUntilPresent() {
+		waitGuiForElement(root, "Flash message should be visible.");
+    }
+}
diff --git a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/fragment/MenuPage.java b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/fragment/MenuPage.java
new file mode 100644
index 0000000..2f53658
--- /dev/null
+++ b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/fragment/MenuPage.java
@@ -0,0 +1,81 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.ui.fragment;
+
+import java.util.List;
+import static org.keycloak.testsuite.ui.util.SeleniumUtils.waitGuiForElement;
+import org.openqa.selenium.By;
+
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ *
+ * @author Petr Mensik
+ */
+public class MenuPage {
+
+	private static final String MENU_LOCATOR = "ul[class='dropdown-menu']";
+	
+    @FindBy(css = MENU_LOCATOR)
+	private List<WebElement> menuList;
+	
+    @FindBy(css = ".dropdown-toggle")
+    private List<WebElement> toggle;
+
+    public void logOut() {
+		clickOnMenuElement(Menu.USER, "Sign Out");
+	}
+    
+    public void goToAccountManagement() {
+		clickOnMenuElement(Menu.USER, "Manage Account");
+	}
+	
+	public void switchRealm(String realmName) {
+		clickOnMenuElement(Menu.REALM, realmName);
+	}
+	
+	public String getCurrentRealm() {
+		waitGuiForElement(By.cssSelector(MENU_LOCATOR));
+		return toggle.get(1).getText();
+	}
+
+	private void clickOnMenuElement(Menu menuType, String linkText) {
+		int menuOrder = 0;
+		switch(menuType) {
+			case REALM: menuOrder = 1; break;
+			case USER: menuOrder = 0; break;
+		}
+		waitGuiForElement(By.cssSelector(MENU_LOCATOR));
+        if (!menuList.get(menuOrder).isDisplayed()) 
+			toggle.get(menuOrder).click();
+        for (WebElement item : menuList.get(menuOrder).findElements(By.cssSelector(MENU_LOCATOR + " a"))) {
+            if (item.getText().contains(linkText)) {
+                item.click();
+                return;
+            }
+        }
+        throw new RuntimeException("Could not find menu item containing \"" + linkText + "\"");
+    }
+	
+	private enum Menu {
+		USER, REALM
+	}
+
+}
diff --git a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/fragment/Navigation.java b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/fragment/Navigation.java
new file mode 100644
index 0000000..4175c45
--- /dev/null
+++ b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/fragment/Navigation.java
@@ -0,0 +1,157 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.ui.fragment;
+
+import org.jboss.arquillian.drone.api.annotation.Drone;
+import static org.jboss.arquillian.graphene.Graphene.waitModel;
+import static org.keycloak.testsuite.ui.util.SeleniumUtils.waitGuiForElement;
+
+import org.jboss.arquillian.graphene.findby.FindByJQuery;
+import org.openqa.selenium.support.FindBy;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.WebElement;
+
+/**
+ *
+ * @author Petr Mensik
+ */
+public class Navigation {
+	
+	@Drone
+	private WebDriver driver;
+
+    @FindByJQuery("a:contains('Settings')")
+    private WebElement settingsLink;
+
+    @FindByJQuery("a:contains('Users')")
+    private WebElement usersLink;
+
+    @FindByJQuery("a:contains('Roles')")
+    private WebElement rolesLink;
+
+    @FindByJQuery("a:contains('Clients')")
+    private WebElement clientsLink;
+
+    @FindByJQuery("a:contains('OAuth')")
+    private WebElement oauthLink;
+
+    @FindByJQuery("a:contains('Tokens')")
+    private WebElement tokensLink;
+	
+	@FindByJQuery("a:contains('Sessions')")
+    private WebElement sessionLink;
+
+    @FindByJQuery("a:contains('Security Defenses')")
+    private WebElement securityLink;
+
+    @FindByJQuery("a:contains('Events')")
+    private WebElement eventsLink;
+	
+	@FindByJQuery("a:contains('Login')")
+    private WebElement loginLink;
+
+    @FindByJQuery("a:contains('Themes')")
+    private WebElement themesLink;
+
+	@FindByJQuery("a:contains('Role Mappings')")
+    private WebElement usersRoleMappings;
+	
+	@FindByJQuery("a:contains('Add Realm')")
+    private WebElement addRealm;
+	
+	@FindByJQuery("a:contains('Credentials')")
+    private WebElement credentials;
+	
+	@FindByJQuery("a:contains('Attributes')")
+    private WebElement attributes;
+	
+    @FindBy(css = "div h1")
+    private WebElement currentHeader;
+
+	public void selectRealm(String realmName) {
+		driver.findElement(By.linkText(realmName)).click();
+	}
+	
+    public void settings() {
+        openPage(settingsLink, "Settings");
+    }
+
+    public void users() {
+        openPage(usersLink, "Users");
+    }
+
+    public void roles() {
+        openPage(rolesLink, "Roles");
+    }
+
+    public void clients() {
+        openPage(clientsLink, "Clients");
+    }
+	
+    public void oauth() {
+        openPage(oauthLink, "OAuth Clients");
+    }
+
+    public void tokens() {
+        openPage(tokensLink, "Settings");
+    }
+	
+	public void sessions() {
+		openPage(sessionLink, "Sessions");
+	}
+
+    public void security() {
+        openPage(securityLink, "Settings");
+    }
+
+    public void events() {
+        openPage(eventsLink, "Events");
+    }
+	
+	public void login() {
+		openPage(loginLink, "Settings");
+	}
+
+    public void themes() { 
+		openPage(themesLink, "Settings"); 
+	}
+
+    public void roleMappings() {
+		openPage(usersRoleMappings, "User");
+	}
+	
+	public void addRealm() {
+		openPage(addRealm, "Add Realm");
+	}
+	
+	public void credentials() {
+		openPage(credentials, "Settings");
+	}
+	
+	public void attributes() {
+		openPage(attributes, "Attributes");
+	}
+
+    private void openPage(WebElement page, String headerText) {
+        waitGuiForElement(page);
+        page.click();
+        waitModel().until().element(currentHeader).text().contains(headerText);
+    }
+}
diff --git a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/fragment/OnOffSwitch.java b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/fragment/OnOffSwitch.java
new file mode 100644
index 0000000..3fc3a90
--- /dev/null
+++ b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/fragment/OnOffSwitch.java
@@ -0,0 +1,64 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.ui.fragment;
+
+import org.jboss.arquillian.graphene.fragment.Root;
+import org.jboss.arquillian.test.api.ArquillianResource;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.interactions.Actions;
+
+/**
+ *
+ * @author Petr Mensik
+ */
+
+public class OnOffSwitch {
+
+	@Root
+	private WebElement root;
+	
+	@ArquillianResource
+	private Actions actions;
+	
+	public boolean isEnabled() {
+		return root.findElement(By.tagName("input")).isSelected();
+	}
+	
+	private void click() {
+		actions.moveToElement(root.findElements(By.tagName("span")).get(0))
+				.click().build().perform();
+	}
+	
+	public void toggle() {
+		click();
+	}
+	
+	public void enable() {
+		if(!isEnabled()) {
+			click();
+		}
+	}
+	
+	public void disable() {
+		if(isEnabled()) {
+			click();
+		}
+	}
+}
diff --git a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/fragment/PickList.java b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/fragment/PickList.java
new file mode 100644
index 0000000..7711585
--- /dev/null
+++ b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/fragment/PickList.java
@@ -0,0 +1,62 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.ui.fragment;
+
+import org.jboss.arquillian.graphene.fragment.Root;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+import org.openqa.selenium.support.ui.Select;
+
+/**
+ *
+ * @author Petr Mensik
+ */
+
+public class PickList {
+
+    @Root
+    private WebElement root;
+
+    private Select firstSelect;
+    private Select secondSelect;
+    
+    @FindBy(className = "kc-icon-arrow-right")
+    private WebElement rightArrow;
+    
+    @FindBy(className = "kc-icon-arrow-left")
+    private WebElement leftArrow;
+    
+    public void addItems(String... values) {
+        for(String value : values) {
+            firstSelect.selectByVisibleText(value);
+        }
+        rightArrow.click();
+    }
+    
+    public void setFirstSelect(By locator) {
+        firstSelect = new Select(root.findElement(locator));
+    }
+    
+    public void setSecondSelect(By locator) {
+        secondSelect = new Select(root.findElement(locator));
+    }
+    
+}
diff --git a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/model/Account.java b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/model/Account.java
new file mode 100644
index 0000000..f2a2ef0
--- /dev/null
+++ b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/model/Account.java
@@ -0,0 +1,93 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.ui.model;
+
+/**
+ *
+ * @author Petr Mensik
+ */
+public class Account {
+
+    private String username;
+
+    private String email;
+
+    private String lastName;
+
+    private String firstName;
+
+    public Account(String username, String email, String lastName, String firstName) {
+        this.username = username;
+        this.email = email;
+        this.lastName = lastName;
+        this.firstName = firstName;
+    }
+
+    public String getUsername() {
+        return username;
+    }
+
+    public String getEmail() {
+        return email;
+    }
+
+    public void setEmail(String email) {
+        this.email = email;
+    }
+
+    public String getLastName() {
+        return lastName;
+    }
+
+    public void setLastName(String lastName) {
+        this.lastName = lastName;
+    }
+
+    public String getFirstName() {
+        return firstName;
+    }
+
+    public void setFirstName(String firstName) {
+        this.firstName = firstName;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        Account account = (Account) o;
+
+        if (email != null ? !email.equals(account.email) : account.email != null) return false;
+        if (firstName != null ? !firstName.equals(account.firstName) : account.firstName != null) return false;
+        if (lastName != null ? !lastName.equals(account.lastName) : account.lastName != null) return false;
+        if (username != null ? !username.equals(account.username) : account.username != null) return false;
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = username != null ? username.hashCode() : 0;
+        result = 31 * result + (email != null ? email.hashCode() : 0);
+        result = 31 * result + (lastName != null ? lastName.hashCode() : 0);
+        result = 31 * result + (firstName != null ? firstName.hashCode() : 0);
+        return result;
+    }
+}
diff --git a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/model/Client.java b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/model/Client.java
new file mode 100644
index 0000000..5172f8f
--- /dev/null
+++ b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/model/Client.java
@@ -0,0 +1,104 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.ui.model;
+
+/**
+ *
+ * @author Filip Kiss
+ */
+public class Client {
+
+	private String clientId;
+    private String name;
+    private boolean enabled;
+    private String accessType;
+    private String uri;
+
+	public Client(String clientId, String uri) {
+		this.name = clientId;
+		this.clientId = clientId;		
+		this.uri = uri;
+		this.enabled = true;
+	}
+	
+    public Client(String clientId, String name, String uri) {
+        this.clientId = clientId;
+		this.uri = uri;
+        this.enabled = true;
+        this.name = name;
+    }
+
+    public Client() {
+    }
+
+    public Client(String name, String uri, String accessType, boolean enabled) {
+        this.name = name;
+        this.uri = uri;
+        this.accessType = accessType;
+        this.enabled = enabled;
+    }
+
+    public String getName() { return name; }
+
+    public void setName(String name) { this.name = name; }
+
+    public boolean isEnabled() { return enabled; }
+
+    public void setEnabled(boolean enabled) { this.enabled = enabled; }
+
+    public String getAccessType() { return accessType; }
+
+    public void setAccessType(String accessType) { this.accessType = accessType; }
+
+    public String getUri() { return uri; }
+
+    public void setUri(String uri) { this.uri = uri; }
+
+	public String getClientId() {
+		return clientId;
+	}
+
+	public void setClientId(String clientId) {
+		this.clientId = clientId;
+	}
+	
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        Client that = (Client) o;
+
+        if (enabled != that.enabled) return false;
+        if (accessType != null ? !accessType.equals(that.accessType) : that.accessType != null) return false;
+        if (!name.equals(that.name)) return false;
+        if (!uri.equals(that.uri)) return false;
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = name.hashCode();
+        result = 31 * result + (enabled ? 1 : 0);
+        result = 31 * result + (accessType != null ? accessType.hashCode() : 0);
+        result = 31 * result + uri.hashCode();
+        return result;
+    }
+}
diff --git a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/model/PasswordPolicy.java b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/model/PasswordPolicy.java
new file mode 100644
index 0000000..6d2c651
--- /dev/null
+++ b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/model/PasswordPolicy.java
@@ -0,0 +1,39 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.ui.model;
+
+/**
+ *
+ * @author Petr Mensik
+ */
+public enum PasswordPolicy {
+
+	HASH_ITERATIONS("Hash Iterations"), LENGTH("Length"), DIGITS("Digits"), LOWER_CASE("Lower Case"), 
+	UPPER_CASE("Upper Case"), SPECIAL_CHARS("Special Chars");
+	
+	private String name;
+
+	private PasswordPolicy(String name) {
+		this.name = name;
+	}
+
+	public String getName() {
+		return name;
+	}
+}
diff --git a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/model/Provider.java b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/model/Provider.java
new file mode 100644
index 0000000..869eec2
--- /dev/null
+++ b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/model/Provider.java
@@ -0,0 +1,40 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.ui.model;
+
+/**
+ *
+ * @author Petr Mensik
+ */
+public class Provider {
+
+	public SocialProvider providerName;
+	public String key;
+	public String secret;
+
+	public Provider() {
+	}
+
+	public Provider(SocialProvider providerName, String key, String secret) {
+		this.providerName = providerName;
+		this.key = key;
+		this.secret = secret;
+	}
+	
+}
diff --git a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/model/Role.java b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/model/Role.java
new file mode 100644
index 0000000..efe6178
--- /dev/null
+++ b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/model/Role.java
@@ -0,0 +1,72 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.ui.model;
+
+/**
+ *
+ * @author Petr Mensik
+ */
+public class Role {
+
+    private String name;
+    private boolean composite;
+    private String description;
+
+    public Role() {
+    }
+
+    public Role(String name) {
+        this(name, false, "");
+    }
+    
+    public Role(String name, boolean composite) {
+        this(name, composite, "");
+    }
+    
+    public Role(String name, boolean composite, String description) {
+        this.name = name;
+        this.composite = composite;
+        this.description = description;
+    }
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public boolean isComposite() {
+		return composite;
+	}
+
+	public void setComposite(boolean composite) {
+		this.composite = composite;
+	}
+
+	public String getDescription() {
+		return description;
+	}
+
+	public void setDescription(String description) {
+		this.description = description;
+	}
+
+}
diff --git a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/model/SocialProvider.java b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/model/SocialProvider.java
new file mode 100644
index 0000000..cb13d68
--- /dev/null
+++ b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/model/SocialProvider.java
@@ -0,0 +1,39 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.ui.model;
+
+/**
+ *
+ * @author Petr Mensik
+ */
+public enum SocialProvider {
+	
+	FACEBOOK("Facebook"), GITHUB("Github"), GOOGLE("Google"), TWITTER("Twitter");
+	
+	private String name;
+
+	private SocialProvider(String name) {
+		this.name = name;
+	}
+
+	public String getName() {
+		return name;
+	}
+	
+}
diff --git a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/model/Theme.java b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/model/Theme.java
new file mode 100644
index 0000000..0578449
--- /dev/null
+++ b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/model/Theme.java
@@ -0,0 +1,40 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.ui.model;
+
+/**
+ *
+ * @author Petr Mensik
+ */
+public enum Theme {
+    
+    BASE("base"), KEYCLOAK("keycloak"), PATTERNFLY("patternfly");
+    
+    private final String name;
+    
+    private Theme(String name) {
+        this.name = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    
+}
diff --git a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/model/User.java b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/model/User.java
new file mode 100644
index 0000000..e4be9f5
--- /dev/null
+++ b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/model/User.java
@@ -0,0 +1,146 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.ui.model;
+
+/**
+ *
+ * @author Filip Kiss
+ */
+public class User {
+
+    private String userName;
+	
+	private String password;
+
+    private String email;
+
+    private String firstName;
+
+    private String lastName;
+
+    private boolean userEnabled;
+
+    private boolean emailVerified;
+
+    private String requiredUserActions;
+
+    public User() {
+        this.userEnabled = true;
+        this.emailVerified = false;
+    }
+
+    public User(String userName) {
+		this();
+		this.userName = userName;
+    }
+	
+	public User(String userName, String password) {
+		this(userName);
+		this.password = password;
+    }
+
+    public User(String userName, String password, String email) {
+		this(userName, password);
+		this.email = email;
+    }
+
+    public User(String userName, String password, String email, String firstName, String lastName) {
+		this(userName, password, email);
+		this.firstName = firstName;
+        this.lastName = lastName;
+    }
+
+    public User(String userName, String password, String email, String firstName, String lastName, boolean userEnabled, boolean emailVerified, String requiredUserActions) {
+		this(userName, password, email, firstName, lastName);
+		this.requiredUserActions = requiredUserActions;
+    }
+	
+	public User(User user) {
+		this(user.userName, user.password, user.email, user.firstName, user.lastName,
+				user.userEnabled, user.emailVerified, user.requiredUserActions);
+	}
+
+    public String getUserName() { return userName; }
+
+    public void setUserName(String userName) { this.userName = userName; }
+
+    public String getEmail() { return email; }
+
+    public void setEmail(String email) { this.email = email; }
+
+    public String getFirstName() { return firstName; }
+
+    public void setFirstName(String firstName) { this.firstName = firstName; }
+
+    public String getLastName() { return lastName; }
+
+    public void setLastName(String lastName) { this.lastName = lastName; }
+
+    public boolean isUserEnabled() { return userEnabled; }
+
+    public void setUserEnabled(boolean userEnabled) { this.userEnabled = userEnabled; }
+
+    public boolean isEmailVerified() { return emailVerified; }
+
+    public void setEmailVerified(boolean emailVerified) { this.emailVerified = emailVerified; }
+
+    public String getRequiredUserActions() { return requiredUserActions; }
+
+    public void setRequiredUserActions(String requiredUserActions) { this.requiredUserActions = requiredUserActions; }
+
+	public String getPassword() {
+		return password;
+	}
+
+	public void setPassword(String password) {
+		this.password = password;
+	}
+	
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        User user = (User) o;
+
+        if (emailVerified != user.emailVerified) return false;
+        if (userEnabled != user.userEnabled) return false;
+        if (email != null ? !email.equals(user.email) : user.email != null) return false;
+        if (firstName != null ? !firstName.equals(user.firstName) : user.firstName != null) return false;
+        if (lastName != null ? !lastName.equals(user.lastName) : user.lastName != null) return false;
+        if (requiredUserActions != null ? !requiredUserActions.equals(user.requiredUserActions) : user.requiredUserActions != null)
+            return false;
+        if (!userName.equals(user.userName)) return false;
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = userName.hashCode();
+        result = 31 * result + (email != null ? email.hashCode() : 0);
+        result = 31 * result + (firstName != null ? firstName.hashCode() : 0);
+        result = 31 * result + (lastName != null ? lastName.hashCode() : 0);
+        result = 31 * result + (userEnabled ? 1 : 0);
+        result = 31 * result + (emailVerified ? 1 : 0);
+        result = 31 * result + (requiredUserActions != null ? requiredUserActions.hashCode() : 0);
+        return result;
+    }
+}
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/model/UserAction.java b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/model/UserAction.java
new file mode 100644
index 0000000..bf6a396
--- /dev/null
+++ b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/model/UserAction.java
@@ -0,0 +1,40 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.ui.model;
+
+/**
+ *
+ * @author Petr Mensik
+ */
+public enum UserAction {
+
+	UPDATE_PASSWORD("Update Password"), VERIFY_EMAIL("Verify Email"), UPDATE_PROFILE("Update Profile"), CONFIGURE_TOTP("Configure Totp");
+	
+	private final String actionName;
+
+	private UserAction(String actionName) {
+		this.actionName = actionName;
+	}
+
+	public String getActionName() {
+		return actionName;
+	}
+	
+	
+}
diff --git a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/page/AbstractPage.java b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/page/AbstractPage.java
new file mode 100644
index 0000000..8251ab2
--- /dev/null
+++ b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/page/AbstractPage.java
@@ -0,0 +1,54 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.testsuite.ui.page;
+
+import java.util.List;
+import org.jboss.arquillian.drone.api.annotation.Drone;
+import org.keycloak.testsuite.ui.util.Constants;
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ *
+ * @author Petr Mensik
+ */
+public class AbstractPage {
+
+    @Drone
+    protected WebDriver driver;
+	
+	@FindBy(css = ".btn-danger")
+	protected WebElement dangerButton;
+	
+	//@FindByJQuery(".btn-primary:visible")
+    @FindBy(css = ".btn-primary")
+    protected WebElement primaryButton;
+	
+	    @FindBy(css = ".btn-primary")
+    protected List<WebElement> primaryButtons;
+
+	
+	@FindBy(css = ".ng-binding.btn.btn-danger")
+	protected WebElement deleteConfirmationButton;
+	
+    public void goToPage(String page) {
+        driver.get(String.format(page, Constants.CURRENT_REALM));
+    }
+
+}
diff --git a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/page/account/AccountPage.java b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/page/account/AccountPage.java
new file mode 100644
index 0000000..0c8ab50
--- /dev/null
+++ b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/page/account/AccountPage.java
@@ -0,0 +1,107 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.ui.page.account;
+
+import org.jboss.arquillian.graphene.findby.FindByJQuery;
+import org.keycloak.testsuite.ui.model.Account;
+import org.keycloak.testsuite.ui.page.AbstractPage;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ *
+ * @author Petr Mensik
+ */
+public class AccountPage extends AbstractPage {
+
+    @FindBy(id = "username")
+    private WebElement username;
+
+    @FindBy(id = "email")
+    private WebElement email;
+
+    @FindBy(id = "lastName")
+    private WebElement lastName;
+
+    @FindBy(id = "firstName")
+    private WebElement firstName;
+
+    @FindByJQuery("button[value='Save']")
+    private WebElement save;
+	
+	@FindByJQuery(".nav li:eq(0) a")
+    private WebElement keyclockConsole;
+
+    @FindByJQuery(".nav li:eq(1) a")
+    private WebElement signOutLink;
+
+    @FindByJQuery(".bs-sidebar ul li:eq(0) a")
+    private WebElement accountLink;
+
+    @FindByJQuery(".bs-sidebar ul li:eq(1) a")
+    private WebElement passwordLink;
+
+    @FindByJQuery(".bs-sidebar ul li:eq(2) a")
+    private WebElement authenticatorLink;
+
+    @FindByJQuery(".bs-sidebar ul li:eq(3) a")
+    private WebElement sessionsLink;
+
+
+    public Account getAccount() {
+        return new Account(username.getAttribute("value"), email.getAttribute("value"), lastName.getAttribute("value"), firstName.getAttribute("value"));
+    }
+
+    public void setAccount(Account account) {
+        email.clear();
+        email.sendKeys(account.getEmail());
+        lastName.clear();
+        lastName.sendKeys(account.getLastName());
+        firstName.clear();
+        firstName.sendKeys(account.getFirstName());
+    }
+
+    public void save() {
+        save.click();
+    }
+	
+	 public void keycloakConsole() {
+        keyclockConsole.click();
+    }
+
+    public void signOut() {
+        signOutLink.click();
+    }
+
+    public void account() {
+        accountLink.click();
+    }
+
+    public void password() {
+        passwordLink.click();
+    }
+
+    public void authenticator() {
+        authenticatorLink.click();
+    }
+
+    public void sessions() {
+        sessionsLink.click();
+    }
+}
diff --git a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/page/account/PasswordPage.java b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/page/account/PasswordPage.java
new file mode 100644
index 0000000..b0a3e9d
--- /dev/null
+++ b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/page/account/PasswordPage.java
@@ -0,0 +1,82 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.ui.page.account;
+
+import org.jboss.arquillian.graphene.findby.FindByJQuery;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ *
+ * @author Petr Mensik
+ */
+public class PasswordPage {
+
+    @FindBy(id = "password")
+    private WebElement passwordInput;
+
+    @FindBy(id = "password-new")
+    private WebElement newPasswordInput;
+
+    @FindBy(id = "password-confirm")
+    private WebElement confirmInput;
+
+    @FindByJQuery("button[value='Save']")
+    private WebElement save;
+
+    @FindBy(xpath = "//input[@value='Submit']")
+    private WebElement submit; // on "update password" page, after first login
+
+    public void setPassword(String oldPassword, String newPassword) {
+        passwordInput.clear();
+        passwordInput.sendKeys(oldPassword);
+        confirmNewPassword(newPassword);
+    }
+    
+    public void confirmNewPassword(String newPassword) {
+        newPasswordInput.clear();
+        newPasswordInput.sendKeys(newPassword);
+        confirmInput.clear();
+        confirmInput.sendKeys(newPassword);
+    }
+    
+    public void setOldPasswordField(String oldPassword) {
+        passwordInput.clear();
+        passwordInput.sendKeys(oldPassword);
+    }
+
+    public void setNewPasswordField(String newPassword) {
+        newPasswordInput.clear();
+        newPasswordInput.sendKeys(newPassword);
+    }
+
+    public void setConfirmField(String confirmPassword) {
+        confirmInput.clear();
+        confirmInput.sendKeys(confirmPassword);
+    }
+
+    public void save() {
+        save.click();
+    }
+
+    public void submit() {
+        submit.click();
+    }
+    
+}
diff --git a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/page/LoginPage.java b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/page/LoginPage.java
new file mode 100644
index 0000000..06ef63e
--- /dev/null
+++ b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/page/LoginPage.java
@@ -0,0 +1,67 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.ui.page;
+
+import static org.keycloak.testsuite.ui.util.Constants.ADMIN_PSSWD;
+import static org.keycloak.testsuite.ui.util.SeleniumUtils.waitGuiForElement;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ *
+ * @author Petr Mensik
+ */
+public class LoginPage extends AbstractPage {
+
+    @FindBy(id = "username")
+    private WebElement usernameInput;
+    
+    @FindBy(id = "password")
+    private WebElement passwordInput;
+
+    @FindBy(linkText = "Register")
+    private WebElement registerLink;
+
+	@FindBy(id = "kc-header")
+	private WebElement loginPageHeader;
+
+	public void login(String username, String password) {
+		waitGuiForElement(usernameInput, "Login form should be visible");
+        usernameInput.sendKeys(username);
+        passwordInput.sendKeys(password);
+        passwordInput.submit();
+    }
+    
+    public void loginAsAdmin() {
+        login("admin", ADMIN_PSSWD);
+    }
+
+    public void goToUserRegistration() {
+        waitGuiForElement(usernameInput, "Login form should be visible");
+        registerLink.click();
+    }
+	
+	public String getLoginPageHeaderText() {
+		return loginPageHeader.getText();
+	}
+	
+	public WebElement getLoginPageHeader() {
+		return loginPageHeader;
+	}
+}
diff --git a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/page/RegisterPage.java b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/page/RegisterPage.java
new file mode 100644
index 0000000..1c3ad33
--- /dev/null
+++ b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/page/RegisterPage.java
@@ -0,0 +1,99 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.ui.page;
+
+import org.keycloak.testsuite.ui.model.User;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+import java.util.concurrent.TimeUnit;
+
+import static org.keycloak.testsuite.ui.util.SeleniumUtils.waitGuiForElement;
+
+/**
+ *
+ * @author Filip Kiss
+ */
+public class RegisterPage extends AbstractPage {
+
+    @FindBy(id = "username")
+    private WebElement usernameInput;
+
+    @FindBy(id = "email")
+    private WebElement emailInput;
+
+    @FindBy(id = "firstName")
+    private WebElement firstNameInput;
+
+    @FindBy(id = "lastName")
+    private WebElement lastNameInput;
+
+    @FindBy(id = "password")
+    private WebElement passwordInput;
+
+    @FindBy(id = "password-confirm")
+    private WebElement passwordConfirmInput;
+
+    @FindBy(css = "span.kc-feedback-text")
+    private WebElement feedbackError;
+	
+	@FindBy(css = "div[id='kc-form-options'] span a")
+	private WebElement backToLoginForm;
+
+	public void registerNewUser(User user) {
+		registerNewUser(user, user.getPassword());
+	}
+	
+    public void registerNewUser(User user, String confirmPassword) {
+        driver.manage().timeouts().setScriptTimeout(10, TimeUnit.SECONDS);
+        waitGuiForElement(passwordConfirmInput, "Register form should be visible");
+        clearAndType(usernameInput, user.getUserName());
+        clearAndType(firstNameInput, user.getFirstName());
+        clearAndType(lastNameInput, user.getLastName());
+        clearAndType(emailInput, user.getEmail());
+        clearAndType(passwordInput, user.getPassword());
+        clearAndType(passwordConfirmInput, confirmPassword);
+        primaryButton.click();
+    }
+
+    public void clearAndType(WebElement webElement, String text) {
+            webElement.clear();
+            webElement.sendKeys(text);
+    }
+
+    public boolean isInvalidEmail() {
+        waitGuiForElement(feedbackError, "Feedback message should be visible");
+        return feedbackError.getText().equals("Invalid email address.");
+    }
+
+    public boolean isAttributeSpecified(String attribute) {
+        waitGuiForElement(feedbackError, "Feedback message should be visible");
+        return !feedbackError.getText().contains("Please specify " + attribute + ".");
+    }
+
+    public boolean isPasswordSame() {
+        waitGuiForElement(feedbackError, "Feedback message should be visible");
+        return !feedbackError.getText().equals("Password confirmation doesn't match.");
+    }
+	
+	public void backToLoginPage() {
+		backToLoginForm.click();
+	}
+
+}
diff --git a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/page/session/SessionsPage.java b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/page/session/SessionsPage.java
new file mode 100644
index 0000000..d714a64
--- /dev/null
+++ b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/page/session/SessionsPage.java
@@ -0,0 +1,32 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.ui.page.session;
+
+import org.keycloak.testsuite.ui.page.AbstractPage;
+
+/**
+ *
+ * @author Petr Mensik
+ */
+public class SessionsPage extends AbstractPage {
+
+	public void logoutAllSessions() {
+		primaryButton.click();
+	}
+}
diff --git a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/page/settings/ClientPage.java b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/page/settings/ClientPage.java
new file mode 100644
index 0000000..890c7e5
--- /dev/null
+++ b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/page/settings/ClientPage.java
@@ -0,0 +1,129 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.ui.page.settings;
+
+import org.keycloak.testsuite.ui.model.Client;
+import org.keycloak.testsuite.ui.page.AbstractPage;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.jboss.arquillian.graphene.findby.ByJQuery;
+
+import static org.junit.Assert.assertEquals;
+import static org.keycloak.testsuite.ui.util.SeleniumUtils.waitAjaxForElement;
+import static org.openqa.selenium.By.cssSelector;
+import static org.openqa.selenium.By.linkText;
+import static org.openqa.selenium.By.tagName;
+
+/**
+ *
+ * @author Filip Kisss
+ */
+public class ClientPage extends AbstractPage {
+
+	@FindBy(id = "clientId")
+	private WebElement clientId;
+
+	@FindBy(id = "name")
+	private WebElement nameInput;
+
+	@FindBy(id = "")
+	private WebElement enabledSwitchToggle;
+
+	@FindBy(id = "accessType")
+	private WebElement accessTypeDropDownMenu;
+
+	@FindBy(id = "newRedirectUri")
+	private WebElement redirectUriInput;
+
+	@FindBy(css = "table[class*='table']")
+	private WebElement dataTable;
+
+	@FindBy(css = "input[class*='search']")
+	private WebElement searchInput;
+
+	public void addClient(Client client) {
+		primaryButton.click();
+		waitAjaxForElement(clientId);
+		clientId.sendKeys(client.getClientId());
+		nameInput.sendKeys(client.getName());
+		if (!client.isEnabled()) {
+			enabledSwitchToggle.click();
+		}
+		accessTypeDropDownMenu.sendKeys(client.getAccessType());
+		redirectUriInput.sendKeys(client.getUri());
+		primaryButton.click();
+	}
+
+	public void addUri(String uri) {
+		redirectUriInput.sendKeys(uri);
+	}
+
+	public void removeUri(Client client) {
+	}
+
+	public void confirmAddClient() {
+		primaryButton.click();
+	}
+
+	public void deleteClient(String clientName) {
+		searchInput.sendKeys(clientName);
+		driver.findElement(linkText(clientName)).click();
+		waitAjaxForElement(dangerButton);
+		dangerButton.click();
+		waitAjaxForElement(deleteConfirmationButton);
+		deleteConfirmationButton.click();
+	}
+
+	public Client findClient(String clientName) {
+		waitAjaxForElement(searchInput);
+		searchInput.sendKeys(clientName);
+		List<Client> clients = getAllRows();
+		if (clients.isEmpty()) {
+			return null;
+
+		} else {
+			assertEquals(1, clients.size());
+			return clients.get(0);
+		}
+	}
+
+	private List<Client> getAllRows() {
+		List<Client> rows = new ArrayList<Client>();
+		List<WebElement> allRows = dataTable.findElements(cssSelector("tbody tr"));
+		if (allRows.size() > 1) {
+			for (WebElement rowElement : allRows) {
+				if (rowElement.isDisplayed()) {
+					Client client = new Client();
+					List<WebElement> tds = rowElement.findElements(tagName("td"));
+					client.setClientId(tds.get(0).getText());
+					client.setUri(tds.get(2).getText());
+					rows.add(client);
+				}
+			}
+		}
+		return rows;
+	}
+
+	public void goToCreateClient() {
+		driver.findElements(ByJQuery.selector(".btn.btn-primary")).get(0).click();
+	}
+}
diff --git a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/page/settings/CredentialsPage.java b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/page/settings/CredentialsPage.java
new file mode 100644
index 0000000..a0455f5
--- /dev/null
+++ b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/page/settings/CredentialsPage.java
@@ -0,0 +1,71 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.ui.page.settings;
+
+import java.util.List;
+import org.jboss.arquillian.graphene.findby.ByJQuery;
+import org.keycloak.testsuite.ui.model.PasswordPolicy;
+import org.keycloak.testsuite.ui.page.AbstractPage;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+import org.openqa.selenium.support.ui.Select;
+/**
+ *
+ * @author Petr Mensik
+ */
+public class CredentialsPage extends AbstractPage {
+	
+	@FindBy(tagName = "select")
+	private Select addPolicySelect;
+	
+	@FindBy(css = "tr.ng-scope")
+	private List<WebElement> allRows;
+	
+	public void addPolicy(PasswordPolicy policy, int value) {
+		addPolicySelect.selectByVisibleText(policy.getName());
+		setPolicyValue(policy, value);
+		primaryButton.click();
+	}
+	
+	public void removePolicy(PasswordPolicy policy) {
+		int policyInputLocation = findPolicy(policy);
+		allRows.get(policyInputLocation).findElements(By.tagName("i")).get(0).click();
+		primaryButton.click();
+	}
+	
+	public void editPolicy(PasswordPolicy policy, int value) {
+		setPolicyValue(policy, value);
+		primaryButton.click();
+	}
+	
+	private void setPolicyValue(PasswordPolicy policy, int value) {
+		int policyInputLocation = findPolicy(policy);
+		allRows.get(policyInputLocation).findElement(By.tagName("input")).sendKeys(String.valueOf(value));
+	}
+	
+	private int findPolicy(PasswordPolicy policy) {
+		for (int i = 0; i < allRows.size(); i++) {
+			String policyName = allRows.get(i).findElement(ByJQuery.selector("td:eq(0)")).getText();
+			if(policyName.equals(policy.getName()))
+				return i;
+		}
+		return 0;
+	}
+}
diff --git a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/page/settings/DefaultRolesPage.java b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/page/settings/DefaultRolesPage.java
new file mode 100644
index 0000000..d76ede6
--- /dev/null
+++ b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/page/settings/DefaultRolesPage.java
@@ -0,0 +1,55 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.ui.page.settings;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.keycloak.testsuite.ui.fragment.PickList;
+import org.keycloak.testsuite.ui.page.AbstractPage;
+import org.keycloak.testsuite.ui.model.Role;
+import org.openqa.selenium.support.FindBy;
+import org.openqa.selenium.support.ui.Select;
+
+/**
+ *
+ * @author Petr Mensik
+ */
+public class DefaultRolesPage extends AbstractPage {
+    
+    @FindBy(id = "")
+    private PickList realmDefaultRoles;
+    
+    @FindBy(id = "")
+    private PickList applicationDefaultRoles;
+    
+    @FindBy(id = "applications")
+    private Select applicationsSelect;
+    
+    public void addDefaultRealmRoles(String... roles) {
+        realmDefaultRoles.addItems(roles);
+    }
+    
+    public void addDefaultRealmRoles(Role... roles) {
+        List<String> roleList = new ArrayList<String>();
+        for(Role role : roles) {
+            roleList.add(role.getName());
+        }
+        addDefaultRealmRoles(((String []) roleList.toArray()));
+    }
+}
diff --git a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/page/settings/GeneralSettingsPage.java b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/page/settings/GeneralSettingsPage.java
new file mode 100644
index 0000000..bfab15d
--- /dev/null
+++ b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/page/settings/GeneralSettingsPage.java
@@ -0,0 +1,80 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.ui.page.settings;
+
+import org.keycloak.testsuite.ui.page.AbstractPage;
+import org.keycloak.testsuite.ui.model.Theme;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+import org.openqa.selenium.support.ui.Select;
+
+/**
+ *
+ * @author Petr Mensik
+ */
+
+public class GeneralSettingsPage extends AbstractPage {
+
+    @FindBy(id = "name")
+    private WebElement realmName;
+    
+    @FindBy(id = "enabled")
+    private WebElement realmEnabled;
+    
+    @FindBy(id = "updateProfileOnInitialSocialLogin")
+    private WebElement updateProfileOnInitialSocialLogin;
+    
+    @FindBy(id = "passwordCredentialGrantAllowed")
+    private WebElement passwordCredentialGrantAllowed;
+    
+    @FindBy(id = "loginTheme")
+    private Select loginThemeSelect;
+    
+    @FindBy(id = "accountTheme")
+    private Select accountThemeSelect;
+    
+    @FindBy(id = "adminTheme")
+    private Select adminThemeSelect;
+    
+    @FindBy(id = "emailTheme")
+    private Select emailThemeSelect;
+    
+    @FindBy(className = "btn btn-primary btn-lg")
+    private WebElement saveButton;
+    
+    public void saveSettings() {
+        saveButton.click();
+    }
+    
+    public void selectLoginTheme(Theme theme) {
+        loginThemeSelect.selectByVisibleText(theme.getName());
+    }
+    
+    public void selecAccountTheme(Theme theme) {
+        accountThemeSelect.selectByVisibleText(theme.getName());
+    }
+    
+    public void selectAdminTheme(Theme theme) {
+        adminThemeSelect.selectByVisibleText(theme.getName());
+    }
+    
+    public void selectEmailTheme(Theme theme) {
+        emailThemeSelect.selectByVisibleText(theme.getName());
+    }
+}
diff --git a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/page/settings/LoginSettingsPage.java b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/page/settings/LoginSettingsPage.java
new file mode 100644
index 0000000..84049dc
--- /dev/null
+++ b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/page/settings/LoginSettingsPage.java
@@ -0,0 +1,63 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.ui.page.settings;
+
+import org.jboss.arquillian.graphene.findby.FindByJQuery;
+import org.keycloak.testsuite.ui.fragment.OnOffSwitch;
+import org.keycloak.testsuite.ui.page.AbstractPage;
+
+/**
+ *
+ * @author Petr Mensik
+ */
+public class LoginSettingsPage extends AbstractPage {
+
+	@FindByJQuery("div[class='onoffswitch']:eq(0)")
+	private OnOffSwitch registrationAllowed;
+
+	@FindByJQuery("div[class='onoffswitch']:eq(1)")
+	private OnOffSwitch resetPasswordAllowed;
+		
+	@FindByJQuery("div[class='onoffswitch']:eq(2)")
+	private OnOffSwitch rememberMeEnabled;
+
+	@FindByJQuery("div[class='onoffswitch']:eq(3)")
+	private OnOffSwitch verifyEmailEnabled;
+
+	@FindByJQuery("div[class='onoffswitch']:eq(4)")
+	private OnOffSwitch directGrantApiEnabled;
+
+	@FindByJQuery("div[class='onoffswitch']:eq(5)")
+	private OnOffSwitch requireSsl;
+	
+	public boolean isUserRegistrationAllowed() {
+		return registrationAllowed.isEnabled();
+	}
+	
+	public void enableUserRegistration() {
+		registrationAllowed.enable();
+		primaryButton.click();
+	}
+	
+	public void disableUserRegistration() {
+		registrationAllowed.disable();
+		primaryButton.click();
+	}
+	
+}
diff --git a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/page/settings/RolesPage.java b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/page/settings/RolesPage.java
new file mode 100644
index 0000000..1ddaca5
--- /dev/null
+++ b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/page/settings/RolesPage.java
@@ -0,0 +1,112 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.ui.page.settings;
+
+import java.util.ArrayList;
+import java.util.List;
+import static org.junit.Assert.assertEquals;
+import org.keycloak.testsuite.ui.page.AbstractPage;
+import org.keycloak.testsuite.ui.model.Role;
+import static org.openqa.selenium.By.cssSelector;
+import static org.openqa.selenium.By.linkText;
+import static org.openqa.selenium.By.tagName;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+import static org.keycloak.testsuite.ui.util.SeleniumUtils.*;
+/**
+ *
+ * @author Petr Mensik
+ */
+public class RolesPage extends AbstractPage {
+
+    @FindBy(css = "input[class*='search']")
+    private WebElement searchInput;
+
+    @FindBy(css = "table[class*='table']")
+    private WebElement dataTable;
+
+    @FindBy(id = "name")
+    private WebElement nameInput;
+
+    @FindBy(id = "description")
+    private WebElement descriptionInput;
+
+    @FindBy(id = "compositeSwitch")
+    private WebElement compositeSwitchToggle;
+	
+
+    public boolean isRoleComposite(String roleName) {
+        return findRole(roleName).isComposite();
+    }
+
+    public void addRole(Role role) {
+		primaryButton.click();
+		waitAjaxForElement(nameInput);
+        nameInput.sendKeys(role.getName());
+        if (role.isComposite()) {
+            compositeSwitchToggle.click();
+        }
+        descriptionInput.sendKeys(role.getDescription());
+		primaryButton.click();
+	}
+
+    public Role findRole(String roleName) {
+        searchInput.sendKeys(roleName);
+        List<Role> roles = getAllRows();
+        assertEquals(1, roles.size());
+        return roles.get(0);
+    }
+
+    public void editRole(Role role) {
+        driver.findElement(linkText(role.getName())).click();
+        waitAjaxForElement(nameInput);
+        nameInput.sendKeys(role.getName());
+        if (role.isComposite()) {
+            compositeSwitchToggle.click();
+        }
+        descriptionInput.sendKeys(role.getDescription());
+		primaryButton.click();
+	}
+
+    public void deleteRole(Role role) {
+        driver.findElement(linkText(role.getName())).click();
+        waitAjaxForElement(dangerButton);
+		dangerButton.click();
+		deleteConfirmationButton.click();
+    }
+	
+	public void deleteRole(String name) {
+		deleteRole(new Role(name));
+	}
+
+    private List<Role> getAllRows() {
+        List<Role> rows = new ArrayList<Role>();
+        for (WebElement rowElement : dataTable.findElements(cssSelector("tbody tr"))) {
+            Role role = new Role();
+            List<WebElement> tds = rowElement.findElements(tagName("td"));
+            if(!(tds.isEmpty() || tds.get(0).getText().isEmpty())) {
+                role.setName(tds.get(0).getText());
+                role.setComposite(Boolean.valueOf(tds.get(1).getText()));
+                role.setDescription(tds.get(2).getText());
+                rows.add(role);
+            }
+        }
+        return rows;
+    }
+}
diff --git a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/page/settings/SecurityPage.java b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/page/settings/SecurityPage.java
new file mode 100644
index 0000000..13d6a07
--- /dev/null
+++ b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/page/settings/SecurityPage.java
@@ -0,0 +1,109 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.ui.page.settings;
+
+import org.jboss.arquillian.graphene.findby.FindByJQuery;
+import org.keycloak.testsuite.ui.fragment.OnOffSwitch;
+import org.keycloak.testsuite.ui.page.AbstractPage;
+import org.keycloak.testsuite.ui.util.SeleniumUtils;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+import org.openqa.selenium.support.ui.Select;
+
+/**
+ *
+ * @author Filip Kiss
+ */
+public class SecurityPage extends AbstractPage {
+
+    @FindByJQuery("a:contains('Brute Force Detection')")
+    private WebElement bruteForceProtectionLink;
+
+    @FindByJQuery("div[class='onoffswitch']")
+    private OnOffSwitch protectionEnabled;
+
+    @FindBy(id = "failureFactor")
+    private WebElement failureFactorInput;
+
+    @FindBy(id = "waitIncrement")
+    private WebElement waitIncrementInput;
+
+    @FindBy(id = "waitIncrementUnit")
+    private Select waitIncrementSelect;
+
+    @FindBy(id = "quickLoginCheckMilliSeconds")
+    private WebElement quickLoginCheckInput;
+
+    @FindBy(id = "minimumQuickLoginWait")
+    private WebElement minQuickLoginWaitInput;
+
+    @FindBy(id = "minimumQuickLoginWaitUnit")
+    private Select minQuickLoginWaitSelect;
+
+    @FindBy(id = "maxFailureWait")
+    private WebElement maxWaitInput;
+
+    @FindBy(id = "maxFailureWaitUnit")
+    private Select maxWaitSelect;
+
+    @FindBy(id = "maxDeltaTime")
+    private WebElement failureResetTimeInput;
+
+    @FindBy(id = "maxDeltaTimeUnit")
+    private Select failureResetTimeSelect;
+
+    public void goToAndEnableBruteForceProtectionTab() {
+        SeleniumUtils.waitGuiForElement(bruteForceProtectionLink);
+        bruteForceProtectionLink.click();
+        if(!protectionEnabled.isEnabled()){
+            protectionEnabled.enable();
+        }
+    }
+
+    public void setFailureFactorInput(String value){
+        failureFactorInput.clear();
+        failureFactorInput.sendKeys(value);
+    }
+
+    public void setWaitIncrementInput(String value){
+        waitIncrementInput.clear();
+        waitIncrementInput.sendKeys(value);
+    }
+
+    public void setQuickLoginCheckInput(String value){
+        quickLoginCheckInput.clear();
+        quickLoginCheckInput.sendKeys(value);
+    }
+
+    public void setMinQuickLoginWaitInput(String value){
+        minQuickLoginWaitInput.clear();
+        minQuickLoginWaitInput.sendKeys(value);
+    }
+
+    public void setMaxWaitInput(String value){
+        maxWaitInput.clear();
+        maxWaitInput.sendKeys(value);
+    }
+
+    public void setFailureResetTimeInput(String value){
+        failureResetTimeInput.clear();
+        failureResetTimeInput.sendKeys(value);
+    }
+
+}
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/page/settings/SocialSettingsPage.java b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/page/settings/SocialSettingsPage.java
new file mode 100644
index 0000000..ef38396
--- /dev/null
+++ b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/page/settings/SocialSettingsPage.java
@@ -0,0 +1,88 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.ui.page.settings;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.jboss.arquillian.graphene.findby.FindByJQuery;
+import static org.junit.Assert.assertNotNull;
+import org.keycloak.testsuite.ui.model.Provider;
+import org.keycloak.testsuite.ui.model.SocialProvider;
+import org.keycloak.testsuite.ui.page.AbstractPage;
+import static org.openqa.selenium.By.tagName;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+import org.openqa.selenium.support.ui.Select;
+
+/**
+ *
+ * @author Petr Mensik
+ */
+public class SocialSettingsPage extends AbstractPage {
+	
+	@FindBy(tagName = "select")
+	private Select newProviderSelect;
+	
+	@FindByJQuery("input[class*='form-control']:eq(1)")
+	private WebElement providerKey;
+	
+	@FindByJQuery("input[class*='form-control']:eq(2)")
+	private WebElement providerSecret;
+	
+	@FindBy(tagName = "tbody")
+	private WebElement providersTable;
+	
+	public void addNewProvider(Provider provider) {
+		newProviderSelect.selectByVisibleText(provider.providerName.getName());
+		providerKey.sendKeys(provider.key);
+		providerSecret.sendKeys(provider.secret);
+		primaryButton.click();
+	}
+	
+	public void editProvider(SocialProvider oldProvider, Provider newProvider) {
+		Provider p = find(oldProvider);
+		assertNotNull("Provider should have been found", p);
+		System.out.println(p.providerName);
+	}
+	
+	public Provider find(SocialProvider provider){
+		List<Provider> list = getAllRows();
+		for(Provider p : list) {
+			if(p.providerName == provider) {
+				return p;
+			} 
+		}
+		return null;
+	}
+	
+	private List<Provider> getAllRows() {
+        List<Provider> rows = new ArrayList<Provider>();
+        for (WebElement rowElement : providersTable.findElements(tagName("tr"))) {
+            Provider provider = new Provider();
+            List<WebElement> tds = rowElement.findElements(tagName("td"));
+            if(!(tds.isEmpty() || tds.get(0).getText().isEmpty())) {
+                provider.providerName = SocialProvider.valueOf(tds.get(0).getText());
+                provider.key = tds.get(1).getText();
+                provider.secret = tds.get(2).getText();
+                rows.add(provider);
+            }
+        }
+        return rows;
+    }
+}
diff --git a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/page/settings/ThemesSettingsPage.java b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/page/settings/ThemesSettingsPage.java
new file mode 100644
index 0000000..d311d4d
--- /dev/null
+++ b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/page/settings/ThemesSettingsPage.java
@@ -0,0 +1,80 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.ui.page.settings;
+
+import org.keycloak.testsuite.ui.page.AbstractPage;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+import org.openqa.selenium.support.ui.Select;
+
+import static org.keycloak.testsuite.ui.util.SeleniumUtils.waitGuiForElement;
+import static org.keycloak.testsuite.ui.util.SeleniumUtils.waitGuiForElementNotPresent;
+import org.openqa.selenium.By;
+
+/**
+ *
+ * @author Filip Kiss
+ */
+public class ThemesSettingsPage extends AbstractPage {
+
+    @FindBy(css = "#loginTheme")
+    private Select loginThemeSelect;
+
+    @FindBy(css = "#accountTheme")
+    private Select accountThemeSelect;
+
+    @FindBy(css = "#adminTheme")
+    private Select adminConsoleThemeSelect;
+
+    @FindBy(css = "#emailTheme")
+    private Select emailThemeSelect;
+
+    @FindBy(css = "link[href*='login/keycloak/css/login.css']")
+    private WebElement keycloakTheme;
+
+    public void changeLoginTheme(String themeName){
+		waitGuiForElement(By.id("loginTheme"));
+        loginThemeSelect.selectByVisibleText(themeName);
+    }
+
+    public void changeAccountTheme(String themeName){
+        accountThemeSelect.selectByVisibleText(themeName);
+    }
+
+    public void changeAdminConsoleTheme(String themeName){
+        adminConsoleThemeSelect.selectByVisibleText(themeName);
+    }
+
+    public void changeEmailTheme(String themeName){
+        emailThemeSelect.selectByVisibleText(themeName);
+    }
+
+    public void verifyBaseTheme(){
+        waitGuiForElementNotPresent(keycloakTheme);
+    }
+
+    public void verifyKeycloakTheme(){
+        waitGuiForElement(keycloakTheme);
+    }
+
+    public void saveTheme() {
+        primaryButton.click();
+    }
+
+}
diff --git a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/page/settings/TokensPage.java b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/page/settings/TokensPage.java
new file mode 100644
index 0000000..fc8d433
--- /dev/null
+++ b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/page/settings/TokensPage.java
@@ -0,0 +1,65 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.ui.page.settings;
+
+import java.util.concurrent.TimeUnit;
+import org.keycloak.testsuite.ui.page.AbstractPage;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+import org.openqa.selenium.support.ui.Select;
+
+import static java.lang.String.valueOf;
+import static org.keycloak.testsuite.ui.util.SeleniumUtils.waitGuiForElement;
+import static org.apache.commons.lang3.text.WordUtils.capitalize;
+
+/**
+ *
+ * @author Petr Mensik
+ */
+public class TokensPage extends AbstractPage {
+	
+	@FindBy(id = "ssoSessionIdleTimeout")
+	private WebElement sessionTimeout;
+	
+	@FindBy(name = "ssoSessionIdleTimeoutUnit")
+	private Select sessionTimeoutUnit;
+	
+	@FindBy(id = "ssoSessionMaxLifespan")
+	private WebElement sessionLifespanTimeout;
+	
+	@FindBy(name = "ssoSessionMaxLifespanUnit")
+	private Select sessionLifespanTimeoutUnit;
+
+	public void setSessionTimeout(int timeout, TimeUnit unit) {
+		setTimeout(sessionTimeoutUnit, sessionTimeout, timeout, unit);
+	}
+	
+	public void setSessionTimeoutLifespan(int time, TimeUnit unit) {
+		setTimeout(sessionLifespanTimeoutUnit, sessionLifespanTimeout, time, unit);
+	}
+	
+	private void setTimeout(Select timeoutElement, WebElement unitElement,
+			int timeout, TimeUnit unit) {
+		waitGuiForElement(sessionTimeout);
+		timeoutElement.selectByValue(capitalize(unit.name().toLowerCase()));
+		unitElement.clear();
+		unitElement.sendKeys(valueOf(timeout));
+		primaryButton.click();
+	}
+}
diff --git a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/page/settings/UserPage.java b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/page/settings/UserPage.java
new file mode 100644
index 0000000..18450b4
--- /dev/null
+++ b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/page/settings/UserPage.java
@@ -0,0 +1,195 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.ui.page.settings;
+
+import org.jboss.arquillian.graphene.findby.FindByJQuery;
+import org.keycloak.testsuite.ui.model.User;
+import org.keycloak.testsuite.ui.page.AbstractPage;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+import org.keycloak.testsuite.ui.model.UserAction;
+import static org.keycloak.testsuite.ui.util.SeleniumUtils.waitAjaxForElement;
+import static org.openqa.selenium.By.*;
+import org.openqa.selenium.support.ui.Select;
+
+/**
+ *
+ * @author Filip Kiss
+ */
+public class UserPage extends AbstractPage {
+
+	@FindBy(id = "username")
+	private WebElement usernameInput;
+
+	@FindBy(id = "email")
+	private WebElement emailInput;
+
+	@FindBy(id = "firstName")
+	private WebElement firstNameInput;
+
+	@FindBy(id = "lastName")
+	private WebElement lastNameInput;
+
+	@FindBy(id = "emailVerified")
+	private WebElement emailVerifiedSwitchToggle;
+
+	@FindBy(css = "label[for='userEnabled']")
+	private WebElement userEnabledSwitchToggle;
+
+	@FindBy(css = "input[class*='select2-input']")
+	private WebElement requiredUserActionsInput;
+
+	@FindByJQuery(".select2-offscreen")
+	private Select actionsSelect;
+
+	@FindBy(id = "password")
+	private WebElement password;
+
+	@FindBy(id = "confirmPassword")
+	private WebElement confirmPassword;
+
+	@FindBy(css = "input[class*='search']")
+	private WebElement searchInput;
+
+	@FindBy(css = "table[class*='table']")
+	private WebElement dataTable;
+
+	@FindByJQuery("button[kc-cancel] ")
+	private WebElement cancel;
+	
+	@FindBy(css = "div[class='input-group-addon'] i")
+	private WebElement searchButton;
+
+	public void addUser(User user) {
+		primaryButtons.get(1).click();
+		waitAjaxForElement(usernameInput);
+		usernameInput.sendKeys(user.getUserName());
+		emailInput.sendKeys(user.getEmail());
+		firstNameInput.sendKeys(user.getFirstName());
+		lastNameInput.sendKeys(user.getLastName());
+		if (!user.isUserEnabled()) {
+			userEnabledSwitchToggle.click();
+		}
+		if (user.isEmailVerified()) {
+			emailVerifiedSwitchToggle.click();
+		}
+//		requiredUserActionsInput.sendKeys(user.getRequiredUserActions());
+		primaryButton.click();
+	}
+
+	public void addPasswordForUser(User user) {
+		password.sendKeys(user.getPassword());
+		confirmPassword.sendKeys(user.getPassword());
+		dangerButton.click();
+		waitAjaxForElement(deleteConfirmationButton);
+		deleteConfirmationButton.click();
+	}
+
+	public User findUser(String username) {
+		waitAjaxForElement(searchInput);
+		searchInput.sendKeys(username);
+		searchButton.click();
+		List<User> users = getAllRows();
+		if (users.isEmpty()) {
+			return null;
+
+		} else {
+			assertEquals(1, users.size());
+			return users.get(0);
+		}
+	}
+
+	public void editUser(User user) {
+		goToUser(user);
+		waitAjaxForElement(usernameInput);
+		usernameInput.sendKeys(user.getUserName());
+		emailInput.sendKeys(user.getEmail());
+		if (!user.isUserEnabled()) {
+			userEnabledSwitchToggle.click();
+		}
+		if (user.isEmailVerified()) {
+			emailVerifiedSwitchToggle.click();
+		}
+		requiredUserActionsInput.sendKeys(user.getRequiredUserActions());
+		primaryButton.click();
+	}
+
+	public void deleteUser(String username) {
+		searchInput.sendKeys(username);
+		searchButton.click();
+		driver.findElement(linkText(username)).click();
+		waitAjaxForElement(dangerButton);
+		dangerButton.click();
+		waitAjaxForElement(deleteConfirmationButton);
+		deleteConfirmationButton.click();
+	}
+
+	public void cancel() {
+		cancel.click();
+	}
+
+	public void showAllUsers() {
+		primaryButtons.get(0).click();
+	}
+
+	public void goToUser(User user) {
+		dataTable.findElement(linkText(user.getUserName())).click();
+	}
+
+	public void goToUser(String name) {
+		goToUser(new User(name));
+	}
+
+	public void addAction(UserAction action) {
+		actionsSelect.selectByValue(action.name());
+		primaryButton.click();
+	}
+
+	public void removeAction(UserAction action) {
+		actionsSelect.deselectByValue(action.name());
+		primaryButton.click();
+	}
+
+	private List<User> getAllRows() {
+		List<User> users = new ArrayList<User>();
+		List<WebElement> rows = dataTable.findElements(cssSelector("tbody tr"));
+		if (rows.size() > 1) {
+			for (WebElement rowElement : rows) {
+				if (rowElement.isDisplayed()) {
+					User user = new User();
+					List<WebElement> tds = rowElement.findElements(tagName("td"));
+					if (!(tds.isEmpty() || tds.get(0).getText().isEmpty())) {
+						user.setUserName(tds.get(0).getText());
+						user.setLastName(tds.get(1).getText());
+						user.setFirstName(tds.get(2).getText());
+						user.setEmail(tds.get(3).getText());
+						users.add(user);
+					}
+				}
+			}	
+		}
+		return users;
+	}
+
+}
diff --git a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/test/account/AccountManagementTest.java b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/test/account/AccountManagementTest.java
new file mode 100644
index 0000000..537fee3
--- /dev/null
+++ b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/test/account/AccountManagementTest.java
@@ -0,0 +1,121 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.ui.test.account;
+
+import org.jboss.arquillian.graphene.findby.FindByJQuery;
+import org.jboss.arquillian.graphene.page.Page;
+import org.junit.After;
+import org.junit.Test;
+
+import static org.keycloak.testsuite.ui.util.Constants.ADMIN_PSSWD;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import org.junit.Before;
+import org.keycloak.testsuite.ui.AbstractKeyCloakTest;
+import org.keycloak.testsuite.ui.fragment.FlashMessage;
+import org.keycloak.testsuite.ui.model.Account;
+import org.keycloak.testsuite.ui.page.account.AccountPage;
+import org.keycloak.testsuite.ui.page.account.PasswordPage;
+
+/**
+ *
+ * @author Petr Mensik
+ */
+public class AccountManagementTest extends AbstractKeyCloakTest<AccountPage> {
+
+	@FindByJQuery(".alert")
+    private FlashMessage flashMessage;
+	
+    @Page
+    private AccountPage accountPage;
+
+    @Page
+    private PasswordPage passwordPage;
+	
+    private static final String USERNAME = "admin";
+    private static final String NEW_PASSWORD = "newpassword";
+    private static final String WRONG_PASSWORD = "wrongpassword";
+
+	@Before
+	public void beforeAccountTest() {
+		menuPage.goToAccountManagement();
+	}
+	
+	@After
+	public void afterAccountTest() {
+		accountPage.keycloakConsole();
+	}
+	
+	@Test
+    public void passwordPageValidationTest() {
+	    page.password();
+        passwordPage.save();
+        flashMessage.waitUntilPresent();
+        assertTrue(flashMessage.getText(), flashMessage.isError());
+
+        passwordPage.setPassword(WRONG_PASSWORD, NEW_PASSWORD);
+        passwordPage.save();
+        flashMessage.waitUntilPresent();
+        assertTrue(flashMessage.getText(), flashMessage.isError());
+
+        passwordPage.setOldPasswordField(ADMIN_PSSWD);
+        passwordPage.setNewPasswordField("something");
+        passwordPage.setConfirmField("something else");
+        passwordPage.save();
+        flashMessage.waitUntilPresent();
+        assertTrue(flashMessage.getText(), flashMessage.isError());
+    }
+
+    @Test
+    public void changePasswordTest() {
+        page.password();
+        passwordPage.setPassword(ADMIN_PSSWD, NEW_PASSWORD);
+        passwordPage.save();
+        flashMessage.waitUntilPresent();
+        assertTrue(flashMessage.getText(), flashMessage.isSuccess());
+        page.signOut();
+        loginPage.login(USERNAME, NEW_PASSWORD);
+        page.password();
+        passwordPage.setPassword(NEW_PASSWORD, ADMIN_PSSWD);
+        passwordPage.save();
+        flashMessage.waitUntilPresent();
+        assertTrue(flashMessage.getText(), flashMessage.isSuccess());
+    }
+
+    @Test
+    public void accountPageTest() {
+        page.account();
+        Account adminAccount = accountPage.getAccount();
+        assertEquals(adminAccount.getUsername(), USERNAME);
+        adminAccount.setEmail("a@b");
+        adminAccount.setFirstName("John");
+        adminAccount.setLastName("Smith");
+        accountPage.setAccount(adminAccount);
+        accountPage.save();
+        flashMessage.waitUntilPresent();
+        assertTrue(flashMessage.getText(), flashMessage.isSuccess());
+
+        page.signOut();
+        loginPage.login(USERNAME, ADMIN_PSSWD);
+
+        page.account();
+        assertEquals(adminAccount, accountPage.getAccount());
+    }
+
+}
diff --git a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/test/client/AddNewClientTest.java b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/test/client/AddNewClientTest.java
new file mode 100644
index 0000000..8e0cb19
--- /dev/null
+++ b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/test/client/AddNewClientTest.java
@@ -0,0 +1,109 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.ui.test.client;
+
+import org.jboss.arquillian.graphene.findby.FindByJQuery;
+import org.junit.Test;
+import org.keycloak.testsuite.ui.fragment.FlashMessage;
+import org.keycloak.testsuite.ui.model.Client;
+import org.keycloak.testsuite.ui.page.settings.ClientPage;
+
+
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import org.junit.Before;
+import org.keycloak.testsuite.ui.AbstractKeyCloakTest;
+
+/**
+ *
+ * @author Filip Kiss
+ */
+public class AddNewClientTest extends AbstractKeyCloakTest<ClientPage> {
+
+    @FindByJQuery(".alert")
+    private FlashMessage flashMessage;
+	
+	@Before
+	public void beforeClientTest() {
+		navigation.clients();
+		page.goToCreateClient();
+	}
+
+    @Test
+    public void addNewClientTest() {
+        Client newClient = new Client("testClient1", "http://example.com/*");
+        page.addClient(newClient);
+        flashMessage.waitUntilPresent();
+        assertTrue(flashMessage.getText(), flashMessage.isSuccess());
+        navigation.clients();
+
+        page.deleteClient(newClient.getName());
+        assertTrue(flashMessage.getText(), flashMessage.isSuccess());
+        assertNull(page.findClient(newClient.getName()));
+    }
+
+    @Test
+    public void addNewClientWithBlankNameTest() {
+        Client newClient = new Client("", "http://example.com/*");
+        page.addClient(newClient);
+        flashMessage.waitUntilPresent();
+        assertTrue(flashMessage.getText(), flashMessage.isDanger());
+    }
+
+    @Test
+    public void addNewClientWithBlankUriTest() {
+        Client newClient = new Client("testClient2", "");
+        page.addClient(newClient);
+        page.confirmAddClient();
+        flashMessage.waitUntilPresent();
+        assertTrue(flashMessage.getText(), flashMessage.isDanger());
+
+        page.addUri("http://testUri.com/*");
+        page.confirmAddClient();
+        flashMessage.waitUntilPresent();
+        assertTrue(flashMessage.getText(), flashMessage.isSuccess());
+
+        navigation.clients();
+        page.deleteClient(newClient.getName());
+        assertTrue(flashMessage.getText(), flashMessage.isSuccess());
+        assertNull(page.findClient(newClient.getName()));
+    }
+
+    @Test
+    public void addNewClientWithTwoUriTest() {
+        Client newClient = new Client("testClient3", "");
+        page.addClient(newClient);
+        page.confirmAddClient();
+        flashMessage.waitUntilPresent();
+        assertTrue(flashMessage.getText(), flashMessage.isDanger());
+
+        page.addUri("http://testUri.com/*");
+        page.addUri("http://example.com/*");
+
+        page.confirmAddClient();
+        flashMessage.waitUntilPresent();
+        assertTrue(flashMessage.getText(), flashMessage.isSuccess());
+
+        navigation.clients();
+        page.deleteClient(newClient.getName());
+        assertTrue(flashMessage.getText(), flashMessage.isSuccess());
+        assertNull(page.findClient(newClient.getName()));
+    }
+
+}
diff --git a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/test/role/AddNewRoleTest.java b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/test/role/AddNewRoleTest.java
new file mode 100644
index 0000000..7bcd681
--- /dev/null
+++ b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/test/role/AddNewRoleTest.java
@@ -0,0 +1,94 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+package org.keycloak.testsuite.ui.test.role;
+
+import org.jboss.arquillian.graphene.findby.FindByJQuery;
+import org.jboss.arquillian.graphene.page.Page;
+import org.junit.Test;
+import org.keycloak.testsuite.ui.page.settings.RolesPage;
+import org.keycloak.testsuite.ui.model.Role;
+
+import static org.junit.Assert.*;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.keycloak.testsuite.ui.AbstractKeyCloakTest;
+import org.keycloak.testsuite.ui.fragment.FlashMessage;
+import org.keycloak.testsuite.ui.page.settings.UserPage;
+import static org.openqa.selenium.By.id;
+import org.openqa.selenium.support.ui.Select;
+
+
+/**
+ *
+ * @author Petr Mensik
+ */
+public class AddNewRoleTest extends AbstractKeyCloakTest<RolesPage> {
+	
+	@Page
+	private UserPage userPage;
+	
+	@FindByJQuery(".alert")
+    private FlashMessage flashMessage;
+	
+	@Before
+	public void beforeTestAddNewRole() {
+		navigation.roles();
+	}
+	
+    @Test
+    public void testAddNewRole() {
+        Role role = new Role("role1");
+        page.addRole(role);
+		flashMessage.waitUntilPresent();
+		assertTrue(flashMessage.getText(), flashMessage.isSuccess());
+		navigation.roles();
+        assertEquals("role1", page.findRole(role.getName()).getName());
+        page.deleteRole(role);
+    }
+    
+	@Ignore
+    @Test
+    public void testAddNewRoleWithLongName() {
+        String name = "hjewr89y1894yh98(*&*&$jhjkashd)*(&y8934h*&@#hjkahsdj";
+        page.addRole(new Role(name));
+        assertNotNull(page.findRole(name));
+        navigation.roles();
+        page.deleteRole(name);
+    }
+    
+    @Test
+    public void testAddExistingRole() {
+        Role role = new Role("role2");
+        page.addRole(role);
+		flashMessage.waitUntilPresent();
+		assertTrue(flashMessage.getText(), flashMessage.isSuccess());
+        navigation.roles();
+        page.addRole(role);
+		flashMessage.waitUntilPresent();
+		assertTrue(flashMessage.getText(), flashMessage.isDanger());
+        navigation.roles();
+        page.deleteRole(role);
+    }
+	
+	@Test
+	public void testRoleIsAvailableForUsers() {
+		Role role = new Role("User role");
+		page.addRole(role);
+		flashMessage.waitUntilPresent();
+		assertTrue(flashMessage.getText(), flashMessage.isSuccess());
+		navigation.users();
+		userPage.showAllUsers();
+		userPage.goToUser("admin");
+		navigation.roleMappings();
+		Select rolesSelect = new Select(driver.findElement(id("available")));
+		assertEquals("User role should be present in admin role mapping", 
+				role.getName(), rolesSelect.getOptions().get(0).getText());
+		navigation.roles();	
+		page.deleteRole(role);
+	}
+    
+}
diff --git a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/test/session/SessionsTest.java b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/test/session/SessionsTest.java
new file mode 100644
index 0000000..41c04c0
--- /dev/null
+++ b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/test/session/SessionsTest.java
@@ -0,0 +1,44 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.ui.test.session;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.keycloak.testsuite.ui.AbstractKeyCloakTest;
+import org.keycloak.testsuite.ui.page.session.SessionsPage;
+import static org.keycloak.testsuite.ui.util.SeleniumUtils.waitGuiForElement;
+
+/**
+ *
+ * @author Petr Mensik
+ */
+public class SessionsTest extends AbstractKeyCloakTest<SessionsPage> {
+	
+	@Before
+	public void beforeSessionTest() {
+		navigation.sessions();
+	}
+	
+	@Test
+	public void testLogoutAllSessions() {
+		page.logoutAllSessions();
+		waitGuiForElement(loginPage.getLoginPageHeader(), "Home page should be visible after logout");
+		loginPage.loginAsAdmin();
+	}
+}
diff --git a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/test/session/TokensTest.java b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/test/session/TokensTest.java
new file mode 100644
index 0000000..ba1c2e6
--- /dev/null
+++ b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/test/session/TokensTest.java
@@ -0,0 +1,70 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.ui.test.session;
+
+import java.util.concurrent.TimeUnit;
+import org.junit.Before;
+import org.junit.Test;
+import org.keycloak.testsuite.ui.page.settings.TokensPage;
+
+import static org.jboss.arquillian.graphene.Graphene.waitModel;
+import org.keycloak.testsuite.ui.AbstractKeyCloakTest;
+import static org.keycloak.testsuite.ui.util.SeleniumUtils.waitGuiForElement;
+
+
+/**
+ *
+ * @author Petr Mensik
+ */
+public class TokensTest extends AbstractKeyCloakTest<TokensPage> {
+
+	private static final int TIMEOUT = 10;
+	private static final TimeUnit TIME_UNIT = TimeUnit.SECONDS;
+	
+	@Before
+	public void beforeTokensTest() {
+		navigation.tokens();
+	}
+	
+	@Test
+	public void testTimeoutForRealmSession() throws InterruptedException {
+		page.setSessionTimeout(TIMEOUT, TIME_UNIT);
+		TIME_UNIT.sleep(TIMEOUT + 2); //add 2 secs to timeout
+		driver.navigate().refresh();
+		waitGuiForElement(loginPage.getLoginPageHeader(), "Home page should be visible after session timeout");
+		loginPage.loginAsAdmin();
+		page.setSessionTimeout(30, TimeUnit.MINUTES);
+	}
+	
+	@Test
+	public void testLifespanOfRealmSession() {
+		page.setSessionTimeoutLifespan(TIMEOUT, TIME_UNIT);
+		logOut();
+		loginAsAdmin();
+		waitModel().withTimeout(TIMEOUT + 2, TIME_UNIT) //adds 2 seconds to the timeout
+				.pollingEvery(1, TIME_UNIT)
+				.until("Home page should be visible after session timeout")
+				.element(loginPage.getLoginPageHeader())
+				.is()
+				.present();
+		loginPage.loginAsAdmin();
+		navigation.tokens();
+		page.setSessionTimeoutLifespan(10, TimeUnit.HOURS);
+	}
+}
diff --git a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/test/settings/CredentialsTest.java b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/test/settings/CredentialsTest.java
new file mode 100644
index 0000000..f016db1
--- /dev/null
+++ b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/test/settings/CredentialsTest.java
@@ -0,0 +1,43 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.ui.test.settings;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.keycloak.testsuite.ui.AbstractKeyCloakTest;
+import org.keycloak.testsuite.ui.model.PasswordPolicy;
+import org.keycloak.testsuite.ui.page.settings.CredentialsPage;
+
+/**
+ *
+ * @author Petr Mensik
+ */
+public class CredentialsTest extends AbstractKeyCloakTest<CredentialsPage> {
+	
+	@Before
+	public void beforeCredentialsTest() {
+		navigation.credentials();
+	}
+	
+	@Test
+	public void testDigitsNumber() {
+		page.addPolicy(PasswordPolicy.HASH_ITERATIONS, 5);
+		page.removePolicy(PasswordPolicy.DIGITS);
+	}
+}
diff --git a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/test/settings/SecuritySettingsTest.java b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/test/settings/SecuritySettingsTest.java
new file mode 100644
index 0000000..b748bd6
--- /dev/null
+++ b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/test/settings/SecuritySettingsTest.java
@@ -0,0 +1,38 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.ui.test.settings;
+
+import org.junit.Test;
+import org.keycloak.testsuite.ui.AbstractKeyCloakTest;
+import org.keycloak.testsuite.ui.page.settings.SecurityPage;
+
+/**
+ *
+ * @author Filip Kiss
+ */
+public class SecuritySettingsTest extends AbstractKeyCloakTest<SecurityPage>{
+
+    @Test
+    public void securitySettingsTest() {
+        navigation.security();
+        page.goToAndEnableBruteForceProtectionTab();
+        //TODO:
+
+    }
+}
diff --git a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/test/settings/SocialSettingsTest.java b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/test/settings/SocialSettingsTest.java
new file mode 100644
index 0000000..71ecc4e
--- /dev/null
+++ b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/test/settings/SocialSettingsTest.java
@@ -0,0 +1,66 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.ui.test.settings;
+
+import org.jboss.arquillian.graphene.findby.FindByJQuery;
+import static  org.junit.Assert.*;
+import org.junit.Test;
+import org.keycloak.testsuite.ui.page.settings.SocialSettingsPage;
+import org.keycloak.testsuite.ui.AbstractKeyCloakTest;
+import org.keycloak.testsuite.ui.fragment.FlashMessage;
+import org.keycloak.testsuite.ui.model.Provider;
+import org.keycloak.testsuite.ui.model.SocialProvider;
+import org.keycloak.testsuite.ui.util.URL;
+
+/**
+ *
+ * @author Petr Mensik
+ */
+public class SocialSettingsTest extends AbstractKeyCloakTest<SocialSettingsPage> {
+	
+	@FindByJQuery(".alert")
+    private FlashMessage flashMessage;
+	
+//	@Test
+	public void testAddNewProvider() {
+		page.addNewProvider(new Provider(SocialProvider.FACEBOOK, "klic", "secret"));
+		flashMessage.waitUntilPresent();
+		assertTrue("Success message should be displayed", flashMessage.isSuccess());
+	}
+	
+//	@Test(expected = NoSuchElementException.class)
+	public void testDuplicitProvider() {
+		page.addNewProvider(new Provider(SocialProvider.FACEBOOK, "a", "b"));
+	}
+	
+//	@Test
+	public void testEditProvider() {
+		page.goToPage(URL.SETTINGS_SOCIAL);
+		page.editProvider(SocialProvider.FACEBOOK, new Provider(SocialProvider.FACEBOOK, "abc", "def"));
+	}
+	
+//	@Test
+	public void testDeleteProvider() {
+		
+	}
+	
+	@Test
+	public void testAddMultipleProviders() {
+	}
+}
diff --git a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/test/settings/ThemesSettingsTest.java b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/test/settings/ThemesSettingsTest.java
new file mode 100644
index 0000000..c4686ff
--- /dev/null
+++ b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/test/settings/ThemesSettingsTest.java
@@ -0,0 +1,56 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.ui.test.settings;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.keycloak.testsuite.ui.AbstractKeyCloakTest;
+import org.keycloak.testsuite.ui.model.Theme;
+import org.keycloak.testsuite.ui.page.settings.ThemesSettingsPage;
+
+
+/**
+ *
+ * @author Filip Kiss
+ */
+public class ThemesSettingsTest extends AbstractKeyCloakTest<ThemesSettingsPage> {
+
+    @Before
+    public void beforeThemeTest() {
+        navigation.themes();
+    }
+
+    @Test
+    public void changeLoginThemeTest() {
+        page.changeLoginTheme(Theme.BASE.getName());
+        page.saveTheme();
+        logOut();
+        page.verifyBaseTheme();
+
+        loginAsAdmin();
+        navigation.themes();
+        page.changeLoginTheme(Theme.KEYCLOAK.getName());
+        page.saveTheme();
+        logOut();
+        page.verifyKeycloakTheme();
+
+        loginAsAdmin();
+    }
+
+}
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/test/user/AddNewUserTest.java b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/test/user/AddNewUserTest.java
new file mode 100644
index 0000000..5694c51
--- /dev/null
+++ b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/test/user/AddNewUserTest.java
@@ -0,0 +1,116 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.ui.test.user;
+
+import org.jboss.arquillian.graphene.findby.FindByJQuery;
+import org.junit.Test;
+import org.keycloak.testsuite.ui.fragment.FlashMessage;
+import org.keycloak.testsuite.ui.model.User;
+import org.keycloak.testsuite.ui.page.settings.UserPage;
+
+
+import static org.junit.Assert.*;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.keycloak.testsuite.ui.AbstractKeyCloakTest;
+import static org.keycloak.testsuite.ui.util.Users.TEST_USER1;
+
+/**
+ *
+ * @author Filip Kiss
+ */
+public class AddNewUserTest extends AbstractKeyCloakTest<UserPage> {
+
+    @FindByJQuery(".alert")
+    private FlashMessage flashMessage;
+
+	@Before
+	public void beforeAddNewUserTest() {
+		navigation.users();
+	}
+	
+    @Test
+    public void addUserWithInvalidEmailTest() {
+        String testUsername = "testUserInvEmail";
+        String invalidEmail = "user.redhat.com";
+        User testUser = new User(testUsername, "pass", invalidEmail);
+        page.addUser(testUser);
+        flashMessage.waitUntilPresent();
+        assertTrue(flashMessage.getText(), flashMessage.isDanger());
+        navigation.users();
+        assertNull(page.findUser(testUsername));
+    }
+
+    @Test
+    public void addUserWithNoUsernameTest() {
+        User testUser = new User();
+        page.addUser(testUser);
+        flashMessage.waitUntilPresent();
+        assertTrue(flashMessage.getText(), flashMessage.isDanger());
+    }
+
+	@Ignore
+	@Test
+    public void addUserWithLongNameTest() {
+        String longUserName = "thisisthelongestnameeveranditcannotbeusedwhencreatingnewuserinkeycloak";
+        User testUser = new User(longUserName);
+        navigation.users();
+        page.addUser(testUser);
+        flashMessage.waitUntilPresent();
+        assertTrue(flashMessage.getText(), flashMessage.isDanger());
+        assertNull(page.findUser(testUser.getUserName()));
+    }
+
+    @Test
+    public void addDuplicatedUser() {
+        String testUsername = "test_duplicated_user";
+        User testUser = new User(testUsername);
+        page.addUser(testUser);
+        flashMessage.waitUntilPresent();
+        assertTrue(flashMessage.getText(), flashMessage.isSuccess());
+		navigation.users();
+        assertNotNull(page.findUser(testUsername));
+
+        User testUser2 = new User(testUsername);
+        page.addUser(testUser2);
+        flashMessage.waitUntilPresent();
+        assertTrue(flashMessage.getText(), flashMessage.isDanger());
+		navigation.users();
+        page.deleteUser(testUsername);
+        flashMessage.waitUntilPresent();
+        assertTrue(flashMessage.getText(), flashMessage.isSuccess());
+        assertNull(page.findUser(testUser2.getUserName()));
+    }
+
+    @Test
+    public void addDisabledUser() {
+        page.addUser(TEST_USER1);
+        assertTrue(flashMessage.getText(), flashMessage.isSuccess());
+		navigation.users();
+        page.deleteUser(TEST_USER1.getUserName());
+        flashMessage.waitUntilPresent();
+        assertTrue(flashMessage.getText(), flashMessage.isSuccess());
+        assertNull(page.findUser(TEST_USER1.getUserName()));
+    }
+
+    
+
+
+
+}
diff --git a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/test/user/RegisterNewUserTest.java b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/test/user/RegisterNewUserTest.java
new file mode 100644
index 0000000..834ba25
--- /dev/null
+++ b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/test/user/RegisterNewUserTest.java
@@ -0,0 +1,132 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.ui.test.user;
+
+import org.jboss.arquillian.graphene.findby.FindByJQuery;
+import org.jboss.arquillian.graphene.page.Page;
+import org.junit.After;
+import org.junit.Test;
+import org.keycloak.testsuite.ui.fragment.FlashMessage;
+import org.keycloak.testsuite.ui.model.User;
+import org.keycloak.testsuite.ui.page.RegisterPage;
+import org.keycloak.testsuite.ui.page.settings.UserPage;
+
+import static org.junit.Assert.*;
+import org.junit.Before;
+import org.keycloak.testsuite.ui.AbstractKeyCloakTest;
+import org.keycloak.testsuite.ui.page.settings.LoginSettingsPage;
+import static org.keycloak.testsuite.ui.util.Users.*;
+
+/**
+ *
+ * @author Petr Mensik
+ */
+public class RegisterNewUserTest extends AbstractKeyCloakTest<RegisterPage> {
+
+    @Page
+    private UserPage userPage;
+
+	@Page
+	private LoginSettingsPage loginSettingsPage;
+	
+    @FindByJQuery(".alert")
+    private FlashMessage flashMessage;
+	
+	@Before
+	public void beforeUserRegistration() {
+		navigation.settings();
+		navigation.login();
+		loginSettingsPage.enableUserRegistration();
+		logOut();
+		loginPage.goToUserRegistration();
+	}
+	
+	@After
+	public void afterUserRegistration() {
+		navigation.settings();
+		navigation.login();
+		loginSettingsPage.disableUserRegistration();
+	}
+
+    @Test
+    public void registerNewUserTest() {
+        page.registerNewUser(TEST_USER1);
+		logOut();
+        loginAsAdmin();
+        navigation.users();
+        userPage.deleteUser(TEST_USER1.getUserName());
+        flashMessage.waitUntilPresent();
+        assertTrue(flashMessage.getText(), flashMessage.isSuccess());
+    }
+
+
+    @Test
+    public void registerNewUserWithWrongEmail() {
+        User testUser = new User(TEST_USER1);
+		testUser.setEmail("newUser.redhat.com");
+        page.registerNewUser(testUser);
+        assertTrue(page.isInvalidEmail());
+		page.backToLoginPage();
+        loginAsAdmin();
+        navigation.users();
+        assertNull(userPage.findUser(testUser.getUserName()));
+    }
+
+    @Test
+    public void registerNewUserWithWrongAttributes() {
+		User testUser = new User();
+		
+        page.registerNewUser(testUser);
+        assertFalse(page.isAttributeSpecified("first name"));
+		testUser.setFirstName("name");
+        page.registerNewUser(testUser);
+        assertFalse(page.isAttributeSpecified("last name"));
+		testUser.setLastName("surname");
+        page.registerNewUser(testUser);
+        assertFalse(page.isAttributeSpecified("email"));
+		testUser.setEmail("mail@redhat.com");
+        page.registerNewUser(testUser);
+        assertFalse(page.isAttributeSpecified("username"));
+		testUser.setUserName("user");
+        page.registerNewUser(testUser);
+        assertFalse(page.isAttributeSpecified("password"));
+		testUser.setPassword("password");
+        page.registerNewUser(testUser);
+		logOut();
+		loginAsAdmin();
+        navigation.users();
+        userPage.deleteUser(TEST_USER1.getUserName());
+        flashMessage.waitUntilPresent();
+        assertTrue(flashMessage.getText(), flashMessage.isSuccess());
+    }
+
+    @Test
+    public void registerNewUserWithNotMatchingPasswords() {
+        page.registerNewUser(TEST_USER1, "psswd");
+        assertFalse(page.isPasswordSame());
+        page.registerNewUser(TEST_USER1);
+		logOut();
+        loginAsAdmin();
+        navigation.users();
+        userPage.deleteUser(TEST_USER1.getUserName());
+        flashMessage.waitUntilPresent();
+        assertTrue(flashMessage.getText(), flashMessage.isSuccess());
+    }
+
+}
diff --git a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/util/Constants.java b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/util/Constants.java
new file mode 100644
index 0000000..3bdde85
--- /dev/null
+++ b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/util/Constants.java
@@ -0,0 +1,33 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.ui.util;
+
+/**
+ *
+ * @author Petr Mensik
+ */
+public final class Constants {
+
+    private Constants() {
+    }
+    
+    public static String CURRENT_REALM = "master";
+    
+    public static final String ADMIN_PSSWD = "admin";
+}
diff --git a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/util/SeleniumUtils.java b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/util/SeleniumUtils.java
new file mode 100644
index 0000000..15a4ee5
--- /dev/null
+++ b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/util/SeleniumUtils.java
@@ -0,0 +1,78 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.ui.util;
+
+import static org.jboss.arquillian.graphene.Graphene.waitAjax;
+import static org.jboss.arquillian.graphene.Graphene.waitGui;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+
+/**
+ *
+ * @author Petr Mensik
+ */
+public final class SeleniumUtils {
+
+    private SeleniumUtils() {
+    }
+
+    public static void waitAjaxForElement(By element) {
+        waitAjax().until()
+                .element(element)
+                .is()
+                .present();
+    }
+
+    public static void waitAjaxForElement(WebElement element) {
+        waitAjax().until()
+                .element(element)
+                .is()
+                .present();
+    }
+
+    public static void waitGuiForElement(By element, String message) {
+		waitGui().until(message)
+                .element(element)
+                .is()
+                .present();
+	}
+
+    public static void waitGuiForElement(By element) {
+		waitGuiForElement(element, null);
+	}
+
+    public static void waitGuiForElement(WebElement element) {
+		waitGuiForElement(element, null);
+	}
+
+	public static void waitGuiForElement(WebElement element, String message) {
+        waitGui().until(message)
+                .element(element)
+                .is()
+                .present();
+    }
+
+    public static void waitGuiForElementNotPresent(WebElement element) {
+        waitGui().until()
+                .element(element)
+                .is()
+                .not()
+                .present();
+    }
+}
diff --git a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/util/URL.java b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/util/URL.java
new file mode 100644
index 0000000..ffde485
--- /dev/null
+++ b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/util/URL.java
@@ -0,0 +1,34 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.ui.util;
+
+/**
+ *
+ * @author Petr Mensik
+ */
+public class URL {
+    
+    public static final String BASE_URL = "http://localhost:8080/auth/admin/master/console/index.html";
+    
+    public static String SETTINGS_GENERAL_SETTINGS = BASE_URL + "#/realms/%s";
+    public static String SETTINGS_ROLES = BASE_URL + "#/realms/%s/roles";
+    public static String SETTINGS_LOGIN = BASE_URL + "#/realms/%s/login-settings";
+    public static String SETTINGS_SOCIAL = BASE_URL + "#/realms/%s/social-settings";
+    
+}
diff --git a/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/util/Users.java b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/util/Users.java
new file mode 100644
index 0000000..77a6b5f
--- /dev/null
+++ b/testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/ui/util/Users.java
@@ -0,0 +1,36 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.ui.util;
+
+import org.keycloak.testsuite.ui.model.User;
+
+/**
+ *
+ * @author Petr Mensik
+ */
+public final class Users {
+
+	private Users() {
+	}
+	
+	public static final User ADMIN = new User("admin", "admin");
+	public static final User EMPTY_USER = new User();
+	public static final User TEST_USER1 = new User("user", "password", "user@redhat.com", "user", "test");
+	
+}
diff --git a/testsuite/integration-arquillian/src/test/resources/arquillian.xml b/testsuite/integration-arquillian/src/test/resources/arquillian.xml
new file mode 100644
index 0000000..91393a9
--- /dev/null
+++ b/testsuite/integration-arquillian/src/test/resources/arquillian.xml
@@ -0,0 +1,23 @@
+<arquillian xmlns="http://jboss.org/schema/arquillian"
+			xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+			xsi:schemaLocation="http://jboss.org/schema/arquillian
+        http://jboss.org/schema/arquillian/arquillian_1_0.xsd">
+
+
+	<container qualifier="wildfly-8-remote">
+		<protocol type="jmx-as7">
+			<property name="executionType">REMOTE</property>
+		</protocol>
+	</container>
+	
+	<container qualifier="wildfly-8-managed">
+		<configuration>
+			<property name="jbossHome">${jbossHome}</property>
+			<property name="serverConfig">standalone.xml</property>
+		</configuration>
+	</container>
+    
+	<extension qualifier="webdriver">
+		<property name="browser">${browser}</property>
+	</extension>
+</arquillian>