keycloak-uncached

admin ui login

8/2/2013 11:26:57 PM

Changes

services/src/main/java/org/keycloak/services/resources/RegistrationService.java 70(+0 -70)

Details

diff --git a/examples/as7-eap-demo/server/src/main/java/org/keycloak/example/demo/DemoApplication.java b/examples/as7-eap-demo/server/src/main/java/org/keycloak/example/demo/DemoApplication.java
index 1d81d72..eab7b1e 100755
--- a/examples/as7-eap-demo/server/src/main/java/org/keycloak/example/demo/DemoApplication.java
+++ b/examples/as7-eap-demo/server/src/main/java/org/keycloak/example/demo/DemoApplication.java
@@ -7,7 +7,8 @@ import org.keycloak.services.models.KeycloakSession;
 import org.keycloak.services.models.RealmModel;
 import org.keycloak.services.models.RequiredCredentialModel;
 import org.keycloak.services.resources.KeycloakApplication;
-import org.keycloak.services.resources.RegistrationService;
+import org.keycloak.services.resources.SaasService;
+import org.keycloak.services.resources.SaasService;
 
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
@@ -35,12 +36,12 @@ public class DemoApplication extends KeycloakApplication {
         defaultRealm.setEnabled(true);
         defaultRealm.setTokenLifespan(300);
         defaultRealm.setAccessCodeLifespan(60);
-        defaultRealm.setSslNotRequired(false);
+        defaultRealm.setSslNotRequired(true);
         defaultRealm.setCookieLoginAllowed(true);
         defaultRealm.setRegistrationAllowed(true);
         manager.generateRealmKeys(defaultRealm);
         defaultRealm.addRequiredCredential(RequiredCredentialModel.PASSWORD);
-        defaultRealm.addRole(RegistrationService.REALM_CREATOR_ROLE);
+        defaultRealm.addRole(SaasService.REALM_CREATOR_ROLE);
 
         RealmRepresentation rep = loadJson("META-INF/testrealm.json");
         RealmModel realm = manager.createRealm("demo", rep.getRealm());
diff --git a/examples/as7-eap-demo/server/src/main/webapp/loginForm.jsp b/examples/as7-eap-demo/server/src/main/webapp/loginForm.jsp
index f0f42ba..9a33002 100755
--- a/examples/as7-eap-demo/server/src/main/webapp/loginForm.jsp
+++ b/examples/as7-eap-demo/server/src/main/webapp/loginForm.jsp
@@ -61,11 +61,11 @@
     <hr/>
     <% String errorMessage = (String)request.getAttribute("KEYCLOAK_LOGIN_ERROR_MESSAGE");
        if (errorMessage != null) { %>
-    <div id="error-message" class="alert alert-block alert-error" style="block"><%=errorMessage%></div>
+    <div id="error-message" class="alert alert-block alert-error" ><%=errorMessage%></div>
     <% } %>
     <form class="form-horizontal" name="loginForm" action="<%=request.getAttribute("KEYCLOAK_LOGIN_ACTION")%>" method="POST">
         <div class="control-group">
-            <label class="control-label" for="username">User Name</label>
+            <label class="control-label">User Name</label>
 
             <div class="controls">
             <% if (username != null) { %>
@@ -123,5 +123,6 @@
 </div>
 <footer>
   <p>Powered By Keycloak</p>
+</footer>
 </body>
 </html>
diff --git a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/OAuthManagedResourceValve.java b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/OAuthManagedResourceValve.java
index 3614d58..3a49660 100755
--- a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/OAuthManagedResourceValve.java
+++ b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/OAuthManagedResourceValve.java
@@ -163,7 +163,7 @@ public class OAuthManagedResourceValve extends FormAuthenticator implements Life
 
     protected void remoteLogout(JWSInput token, HttpServletResponse response) throws IOException {
         try {
-            log.debug("->> remoteLogout: ");
+            log.info("->> remoteLogout: ");
             LogoutAction action = JsonSerialization.fromBytes(LogoutAction.class, token.getContent());
             if (action.isExpired()) {
                 log.warn("admin request failed, expired token");
diff --git a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/ServletOAuthLogin.java b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/ServletOAuthLogin.java
index b3cdebb..f046eed 100755
--- a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/ServletOAuthLogin.java
+++ b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/ServletOAuthLogin.java
@@ -175,8 +175,9 @@ public class ServletOAuthLogin {
             return false;
         }
         // reset the cookie
-        Cookie reset = new Cookie(stateCookie.getName(), stateCookie.getValue());
-        reset.setPath(stateCookie.getPath());
+        log.info("** reseting application state cookie");
+        Cookie reset = new Cookie(realmInfo.getStateCookieName(), "");
+        reset.setPath(getDefaultCookiePath());
         reset.setMaxAge(0);
         response.addCookie(reset);
 
diff --git a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
index 8bcf3c6..e98cdbc 100755
--- a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
@@ -1,5 +1,7 @@
 package org.keycloak.services.managers;
 
+import org.jboss.resteasy.jose.jws.JWSBuilder;
+import org.jboss.resteasy.jwt.JsonSerialization;
 import org.jboss.resteasy.logging.Logger;
 import org.jboss.resteasy.spi.HttpResponse;
 import org.jboss.resteasy.spi.ResteasyProviderFactory;
@@ -11,6 +13,7 @@ import org.keycloak.services.models.RealmModel;
 import org.keycloak.services.models.RequiredCredentialModel;
 import org.keycloak.services.models.UserModel;
 import org.keycloak.services.resources.RealmsResource;
+import org.keycloak.services.resources.SaasService;
 
 import javax.ws.rs.NotAuthorizedException;
 import javax.ws.rs.core.Cookie;
@@ -31,6 +34,7 @@ import java.util.Set;
 public class AuthenticationManager {
     protected Logger logger = Logger.getLogger(AuthenticationManager.class);
     public static final String FORM_USERNAME = "username";
+    public static final String KEYCLOAK_IDENTITY_COOKIE = "KEYCLOAK_IDENTITY";
 
     /**
      * Grabs token from headers, authenticates, authorizes
@@ -44,21 +48,99 @@ public class AuthenticationManager {
         return realm.isRealmAdmin(user);
     }
 
+    public SkeletonKeyToken createIdentityToken(RealmModel realm, String username) {
+        SkeletonKeyToken token = new SkeletonKeyToken();
+        token.id(RealmManager.generateId());
+        token.issuedNow();
+        token.principal(username);
+        token.audience(realm.getId());
+        if (realm.getTokenLifespan() > 0) {
+            token.expiration((System.currentTimeMillis() / 1000) + realm.getTokenLifespan());
+        }
+        return token;
+    }
+
+
+    public NewCookie createLoginCookie(RealmModel realm, UserModel user, UriInfo uriInfo) {
+        String cookieName = KEYCLOAK_IDENTITY_COOKIE;
+        URI uri = RealmsResource.realmBaseUrl(uriInfo).build(realm.getId());
+        String cookiePath = uri.getPath();
+        return createLoginCookie(realm, user, cookieName, cookiePath);
+    }
+
+    public NewCookie createSaasIdentityCookie(RealmModel realm, UserModel user, UriInfo uriInfo) {
+        String cookieName = SaasService.SAAS_IDENTITY_COOKIE;
+        URI uri = SaasService.saasCookiePath(uriInfo).build();
+        String cookiePath = uri.getPath();
+        return createLoginCookie(realm, user, cookieName, cookiePath);
+    }
+
+
+    protected NewCookie createLoginCookie(RealmModel realm, UserModel user, String cookieName, String cookiePath) {
+        SkeletonKeyToken identityToken = createIdentityToken(realm, user.getLoginName());
+        String encoded = encodeToken(realm, identityToken);
+        boolean secureOnly = !realm.isSslNotRequired();
+        logger.info("creatingLoginCookie - name: " + cookieName + " path: " + cookiePath);
+        NewCookie cookie = new NewCookie(cookieName, encoded, cookiePath, null, null, NewCookie.DEFAULT_MAX_AGE, secureOnly, false);
+        return cookie;
+    }
+
+    protected String encodeToken(RealmModel realm, Object token) {
+        byte[] tokenBytes = null;
+        try {
+            tokenBytes = JsonSerialization.toByteArray(token, false);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+        String encodedToken = new JWSBuilder()
+                .content(tokenBytes)
+                .rsa256(realm.getPrivateKey());
+        return encodedToken;
+    }
+
+
     public void expireIdentityCookie(RealmModel realm, UriInfo uriInfo) {
         URI uri = RealmsResource.realmBaseUrl(uriInfo).build(realm.getId());
+        logger.info("Expiring identity cookie");
+        String path = uri.getPath();
+        String cookieName = KEYCLOAK_IDENTITY_COOKIE;
+        expireCookie(cookieName, path);
+    }
+
+    public void expireSaasIdentityCookie(UriInfo uriInfo) {
+        URI uri = SaasService.saasCookiePath(uriInfo).build();
+        String cookiePath = uri.getPath();
+        expireCookie(SaasService.SAAS_IDENTITY_COOKIE, cookiePath);
+    }
+
+    protected void expireCookie(String cookieName, String path) {
         HttpResponse response = ResteasyProviderFactory.getContextData(HttpResponse.class);
         if (response == null) {
             logger.info("can't expire identity cookie, no HttpResponse");
             return;
         }
-        logger.info("Expiring identity cookie");
-        NewCookie expireIt = new NewCookie(TokenManager.KEYCLOAK_IDENTITY_COOKIE, "", uri.getPath(), null, "Expiring cookie", 0, false);
+        logger.info("Expiring cookie: " + cookieName + " path: " + path);
+        NewCookie expireIt = new NewCookie(cookieName, "", path, null, "Expiring cookie", 0, false);
         response.addNewCookie(expireIt);
     }
 
     public UserModel authenticateIdentityCookie(RealmModel realm, UriInfo uriInfo, HttpHeaders headers) {
-        Cookie cookie = headers.getCookies().get(TokenManager.KEYCLOAK_IDENTITY_COOKIE);
-        if (cookie == null) return null;
+        String cookieName = KEYCLOAK_IDENTITY_COOKIE;
+        return authenticateIdentityCookie(realm, uriInfo, headers, cookieName);
+    }
+
+    public UserModel authenticateSaasIdentityCookie(RealmModel realm, UriInfo uriInfo, HttpHeaders headers) {
+        String cookieName = SaasService.SAAS_IDENTITY_COOKIE;
+        return authenticateIdentityCookie(realm, uriInfo, headers, cookieName);
+    }
+
+
+    protected UserModel authenticateIdentityCookie(RealmModel realm, UriInfo uriInfo, HttpHeaders headers, String cookieName) {
+        Cookie cookie = headers.getCookies().get(cookieName);
+        if (cookie == null) {
+            logger.info("authenticateCookie could not find cookie: " + cookieName);
+            return null;
+        }
 
         String tokenString = cookie.getValue();
         try {
@@ -112,7 +194,6 @@ public class AuthenticationManager {
     }
 
     public boolean authenticateForm(RealmModel realm, UserModel user, MultivaluedMap<String, String> formData) {
-        String username = user.getLoginName();
         Set<String> types = new HashSet<String>();
 
         for (RequiredCredentialModel credential : realm.getRequiredCredentials()) {
diff --git a/services/src/main/java/org/keycloak/services/managers/ResourceAdminManager.java b/services/src/main/java/org/keycloak/services/managers/ResourceAdminManager.java
index fd2fe2a..0f1760c 100755
--- a/services/src/main/java/org/keycloak/services/managers/ResourceAdminManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/ResourceAdminManager.java
@@ -2,6 +2,7 @@ package org.keycloak.services.managers;
 
 import org.jboss.resteasy.client.jaxrs.ResteasyClient;
 import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
+import org.jboss.resteasy.logging.Logger;
 import org.keycloak.TokenIdGenerator;
 import org.keycloak.representations.idm.admin.LogoutAction;
 import org.keycloak.services.models.RealmModel;
@@ -17,6 +18,7 @@ import java.util.List;
  * @version $Revision: 1 $
  */
 public class ResourceAdminManager {
+    protected Logger logger = Logger.getLogger(ResourceAdminManager.class);
 
     public void logoutAll(RealmModel realm) {
         singleLogOut(realm, null);
@@ -28,6 +30,7 @@ public class ResourceAdminManager {
                 .build();
 
         List<ResourceModel> resources = realm.getResources();
+        logger.info("logging out " + resources.size() + " resoures.");
         for (ResourceModel resource : resources) {
             logoutResource(realm, resource, user, client);
         }
@@ -38,7 +41,9 @@ public class ResourceAdminManager {
         String token = new TokenManager().encodeToken(realm, adminAction);
         Form form = new Form();
         form.param("token", token);
-        Response response = client.target(resource.getManagementUrl()).queryParam("action", "logout").request().post(Entity.form(form));
+        String managementUrl = resource.getManagementUrl();
+        logger.info("logout user: " + user + " resource: " + resource.getName() + " url" + managementUrl);
+        Response response = client.target(managementUrl).queryParam("action", "logout").request().post(Entity.form(form));
         boolean success = response.getStatus() == 204;
         response.close();
         return success;
diff --git a/services/src/main/java/org/keycloak/services/managers/TokenManager.java b/services/src/main/java/org/keycloak/services/managers/TokenManager.java
index 4f9bdb3..121de14 100755
--- a/services/src/main/java/org/keycloak/services/managers/TokenManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/TokenManager.java
@@ -30,7 +30,6 @@ import java.util.concurrent.ConcurrentHashMap;
  */
 public class TokenManager {
 
-    public static final String KEYCLOAK_IDENTITY_COOKIE = "KEYCLOAK_IDENTITY";
     protected Map<String, AccessCodeEntry> accessCodeMap = new ConcurrentHashMap<String, AccessCodeEntry>();
 
     public void clearAccessCodes() {
@@ -45,14 +44,6 @@ public class TokenManager {
         return accessCodeMap.remove(key);
     }
 
-    public NewCookie createLoginCookie(RealmModel realm, UserModel user, UriInfo uriInfo) {
-        SkeletonKeyToken identityToken = createIdentityToken(realm, user.getLoginName());
-        String encoded = encodeToken(realm, identityToken);
-        URI uri = RealmsResource.realmBaseUrl(uriInfo).build(realm.getId());
-        boolean secureOnly = !realm.isSslNotRequired();
-        NewCookie cookie = new NewCookie(KEYCLOAK_IDENTITY_COOKIE, encoded, uri.getPath(), null, null, NewCookie.DEFAULT_MAX_AGE, secureOnly, true);
-        return cookie;
-    }
 
     public AccessCodeEntry createAccessCode(String scopeParam, String state, String redirect, RealmModel realm, UserModel client, UserModel user) {
         AccessCodeEntry code = new AccessCodeEntry();
@@ -212,17 +203,6 @@ public class TokenManager {
         return token;
     }
 
-    public SkeletonKeyToken createIdentityToken(RealmModel realm, String username) {
-        SkeletonKeyToken token = new SkeletonKeyToken();
-        token.id(RealmManager.generateId());
-        token.issuedNow();
-        token.principal(username);
-        token.audience(realm.getId());
-        if (realm.getTokenLifespan() > 0) {
-            token.expiration((System.currentTimeMillis() / 1000) + realm.getTokenLifespan());
-        }
-        return token;
-    }
 
     public String encodeToken(RealmModel realm, Object token) {
         byte[] tokenBytes = null;
diff --git a/services/src/main/java/org/keycloak/services/models/picketlink/mappings/ResourceData.java b/services/src/main/java/org/keycloak/services/models/picketlink/mappings/ResourceData.java
index 7133039..2a02572 100755
--- a/services/src/main/java/org/keycloak/services/models/picketlink/mappings/ResourceData.java
+++ b/services/src/main/java/org/keycloak/services/models/picketlink/mappings/ResourceData.java
@@ -23,6 +23,7 @@ public class ResourceData extends AbstractPartition {
         super(name);
     }
 
+    @AttributeProperty
     public String getResourceName() {
         return resourceName;
     }
diff --git a/services/src/main/java/org/keycloak/services/models/picketlink/mappings/ResourceEntity.java b/services/src/main/java/org/keycloak/services/models/picketlink/mappings/ResourceEntity.java
index c64c33c..a2d43f8 100755
--- a/services/src/main/java/org/keycloak/services/models/picketlink/mappings/ResourceEntity.java
+++ b/services/src/main/java/org/keycloak/services/models/picketlink/mappings/ResourceEntity.java
@@ -24,7 +24,7 @@ public class ResourceEntity implements Serializable {
     private PartitionTypeEntity partitionTypeEntity;
 
     @AttributeValue
-    private String realmName;
+    private String resourceName;
     @AttributeValue
     private boolean enabled;
     @AttributeValue
@@ -45,12 +45,12 @@ public class ResourceEntity implements Serializable {
         this.partitionTypeEntity = partitionTypeEntity;
     }
 
-    public String getRealmName() {
-        return realmName;
+    public String getResourceName() {
+        return resourceName;
     }
 
-    public void setRealmName(String realmName) {
-        this.realmName = realmName;
+    public void setResourceName(String realmName) {
+        this.resourceName = realmName;
     }
 
     public boolean isEnabled() {
diff --git a/services/src/main/java/org/keycloak/services/models/picketlink/PicketlinkKeycloakSession.java b/services/src/main/java/org/keycloak/services/models/picketlink/PicketlinkKeycloakSession.java
index 8efe790..b6becf9 100755
--- a/services/src/main/java/org/keycloak/services/models/picketlink/PicketlinkKeycloakSession.java
+++ b/services/src/main/java/org/keycloak/services/models/picketlink/PicketlinkKeycloakSession.java
@@ -1,6 +1,8 @@
 package org.keycloak.services.models.picketlink;
 
+import org.jboss.resteasy.spi.HttpRequest;
 import org.jboss.resteasy.spi.NotImplementedYetException;
+import org.jboss.resteasy.spi.ResteasyProviderFactory;
 import org.keycloak.services.models.KeycloakSession;
 import org.keycloak.services.models.KeycloakTransaction;
 import org.keycloak.services.models.RealmModel;
@@ -17,6 +19,7 @@ import java.util.concurrent.atomic.AtomicLong;
 public class PicketlinkKeycloakSession implements KeycloakSession {
     public static ThreadLocal<EntityManager> currentEntityManager = new ThreadLocal<EntityManager>();
     public static ThreadLocal<Exception> setWhere = new ThreadLocal<Exception>();
+    public static ThreadLocal<String> setFromPath = new ThreadLocal<String>();
     protected PartitionManager partitionManager;
     protected EntityManager entityManager;
 
@@ -31,7 +34,14 @@ public class PicketlinkKeycloakSession implements KeycloakSession {
         if (currentEntityManager.get() != null)
         {
             setWhere.get().printStackTrace();
-            throw new IllegalStateException("Thread local was leaked!");
+            String path = setFromPath.get();
+            if (path == null) path = "???";
+
+            throw new IllegalStateException("Thread local was leaked! from path: " + path);
+        }
+        HttpRequest request = ResteasyProviderFactory.getContextData(HttpRequest.class);
+        if (request != null) {
+            setFromPath.set(request.getUri().getPath());
         }
         currentEntityManager.set(entityManager);
         setWhere.set(new Exception());
@@ -75,9 +85,10 @@ public class PicketlinkKeycloakSession implements KeycloakSession {
 
     @Override
     public void close() {
-        if (entityManager.getTransaction().isActive()) entityManager.getTransaction().rollback();
+        setFromPath.set(null);
         setWhere.set(null);
         currentEntityManager.set(null);
+        if (entityManager.getTransaction().isActive()) entityManager.getTransaction().rollback();
         if (entityManager.isOpen()) entityManager.close();
     }
 }
diff --git a/services/src/main/java/org/keycloak/services/models/picketlink/RealmAdapter.java b/services/src/main/java/org/keycloak/services/models/picketlink/RealmAdapter.java
index b0ad70b..7bff8af 100755
--- a/services/src/main/java/org/keycloak/services/models/picketlink/RealmAdapter.java
+++ b/services/src/main/java/org/keycloak/services/models/picketlink/RealmAdapter.java
@@ -390,11 +390,17 @@ public class RealmAdapter implements RealmModel {
 
     @Override
     public ResourceModel addResource(String name) {
-        ResourceData resourceData = new ResourceData(name);
+        ResourceData resourceData = new ResourceData(RealmManager.generateId());
         User resourceUser = new User(name);
         idm.add(resourceUser);
         resourceData.setResourceUser(resourceUser);
+        resourceData.setResourceName(name);
+        resourceData.setResourceUser(resourceUser);
         partitionManager.add(resourceData);
+        ResourceRelationship resourceRelationship = new ResourceRelationship();
+        resourceRelationship.setRealm(realm.getName());
+        resourceRelationship.setResource(resourceData.getName());
+        getRelationshipManager().add(resourceRelationship);
         ResourceModel resource = new ResourceAdapter(resourceData, this, partitionManager);
         resource.addRole("*");
         resource.addScope(new UserAdapter(resourceUser, idm), "*");
diff --git a/services/src/main/java/org/keycloak/services/models/picketlink/relationships/RequiredCredentialRelationship.java b/services/src/main/java/org/keycloak/services/models/picketlink/relationships/RequiredCredentialRelationship.java
index 1af2627..4434a19 100755
--- a/services/src/main/java/org/keycloak/services/models/picketlink/relationships/RequiredCredentialRelationship.java
+++ b/services/src/main/java/org/keycloak/services/models/picketlink/relationships/RequiredCredentialRelationship.java
@@ -1,12 +1,10 @@
 package org.keycloak.services.models.picketlink.relationships;
 
-import org.keycloak.services.models.picketlink.mappings.RealmData;
 import org.picketlink.idm.model.AbstractAttributedType;
 import org.picketlink.idm.model.Attribute;
 import org.picketlink.idm.model.Relationship;
 import org.picketlink.idm.model.annotation.AttributeProperty;
 import org.picketlink.idm.query.AttributeParameter;
-import org.picketlink.idm.query.RelationshipQueryParameter;
 
 /**
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
diff --git a/services/src/main/java/org/keycloak/services/models/picketlink/relationships/ResourceRelationship.java b/services/src/main/java/org/keycloak/services/models/picketlink/relationships/ResourceRelationship.java
index d826a4e..429ea25 100755
--- a/services/src/main/java/org/keycloak/services/models/picketlink/relationships/ResourceRelationship.java
+++ b/services/src/main/java/org/keycloak/services/models/picketlink/relationships/ResourceRelationship.java
@@ -1,15 +1,10 @@
 package org.keycloak.services.models.picketlink.relationships;
 
-import org.keycloak.services.models.picketlink.mappings.RealmData;
-import org.keycloak.services.models.picketlink.mappings.ResourceData;
 import org.picketlink.idm.model.AbstractAttributedType;
 import org.picketlink.idm.model.Attribute;
 import org.picketlink.idm.model.Relationship;
 import org.picketlink.idm.model.annotation.AttributeProperty;
-import org.picketlink.idm.model.sample.Agent;
-import org.picketlink.idm.model.sample.User;
 import org.picketlink.idm.query.AttributeParameter;
-import org.picketlink.idm.query.RelationshipQueryParameter;
 
 /**
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -20,9 +15,10 @@ public class ResourceRelationship extends AbstractAttributedType implements Rela
 
     public static final AttributeParameter REALM = new AttributeParameter("realm");
 
-    protected String realm;
-    protected String resource;
+    public ResourceRelationship() {
+    }
 
+    @AttributeProperty
     public String getRealm() {
         return (String)getAttribute("realm").getValue();
     }
@@ -32,6 +28,7 @@ public class ResourceRelationship extends AbstractAttributedType implements Rela
     }
 
 
+    @AttributeProperty
     public String getResource() {
         return (String)getAttribute("resource").getValue();
     }
diff --git a/services/src/main/java/org/keycloak/services/resources/AbstractLoginService.java b/services/src/main/java/org/keycloak/services/resources/AbstractLoginService.java
old mode 100644
new mode 100755
index b2103c6..1814ab3
--- a/services/src/main/java/org/keycloak/services/resources/AbstractLoginService.java
+++ b/services/src/main/java/org/keycloak/services/resources/AbstractLoginService.java
@@ -11,6 +11,7 @@ import org.jboss.resteasy.spi.HttpRequest;
 import org.jboss.resteasy.spi.HttpResponse;
 import org.keycloak.services.JspRequestParameters;
 import org.keycloak.services.managers.AccessCodeEntry;
+import org.keycloak.services.managers.AuthenticationManager;
 import org.keycloak.services.managers.RealmManager;
 import org.keycloak.services.managers.TokenManager;
 import org.keycloak.services.models.RealmModel;
@@ -34,6 +35,7 @@ public abstract class AbstractLoginService {
 
     protected RealmModel realm;
     protected TokenManager tokenManager;
+    protected AuthenticationManager authManager = new AuthenticationManager();
 
     public AbstractLoginService(RealmModel realm, TokenManager tokenManager) {
         this.realm = realm;
@@ -69,7 +71,7 @@ public abstract class AbstractLoginService {
             redirectUri.queryParam("state", state);
         Response.ResponseBuilder location = Response.status(302).location(redirectUri.build());
         if (realm.isCookieLoginAllowed()) {
-            location.cookie(tokenManager.createLoginCookie(realm, accessCode.getUser(), uriInfo));
+            location.cookie(authManager.createLoginCookie(realm, accessCode.getUser(), uriInfo));
         }
         return location.build();
     }
diff --git a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java
index 5408c92..b84eb7a 100755
--- a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java
+++ b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java
@@ -54,7 +54,7 @@ public class KeycloakApplication extends Application {
         singletons.add(filter);
         classes.add(KeycloakSessionResponseFilter.class);
         classes.add(SkeletonKeyContextResolver.class);
-        classes.add(RegistrationService.class);
+        classes.add(SaasService.class);
     }
 
     protected KeycloakSessionFactory createSessionFactory() {
diff --git a/services/src/main/java/org/keycloak/services/resources/RealmsResource.java b/services/src/main/java/org/keycloak/services/resources/RealmsResource.java
index b2db4f4..d9319ca 100755
--- a/services/src/main/java/org/keycloak/services/resources/RealmsResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/RealmsResource.java
@@ -5,8 +5,6 @@ import org.keycloak.representations.idm.RealmRepresentation;
 import org.keycloak.services.managers.AuthenticationManager;
 import org.keycloak.services.managers.RealmManager;
 import org.keycloak.services.managers.TokenManager;
-import org.keycloak.services.models.KeycloakSession;
-import org.keycloak.services.models.KeycloakSessionFactory;
 import org.keycloak.services.models.RealmModel;
 import org.keycloak.services.models.RoleModel;
 import org.keycloak.services.models.UserModel;
@@ -88,7 +86,7 @@ public class RealmsResource {
                 }
                 SocialService socialService = new SocialService(realm, tokenManager, socialRequestManager);
                 resourceContext.initResource(socialService);
-                return socialService;
+                return socialService;
             }
         }.call();
     }
@@ -121,7 +119,7 @@ public class RealmsResource {
                 RealmManager realmManager = new RealmManager(session);
                 RealmModel defaultRealm = realmManager.getRealm(RealmModel.DEFAULT_REALM);
                 UserModel realmCreator = new AuthenticationManager().authenticateBearerToken(defaultRealm, headers);
-                RoleModel creatorRole = defaultRealm.getRole(RegistrationService.REALM_CREATOR_ROLE);
+                RoleModel creatorRole = defaultRealm.getRole(SaasService.REALM_CREATOR_ROLE);
                 if (!defaultRealm.hasRole(realmCreator, creatorRole)) {
                     logger.warn("not a realm creator");
                     throw new NotAuthorizedException("Bearer");
diff --git a/services/src/main/java/org/keycloak/services/resources/SaaSService.java b/services/src/main/java/org/keycloak/services/resources/SaaSService.java
new file mode 100755
index 0000000..93a3e48
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/resources/SaaSService.java
@@ -0,0 +1,296 @@
+package org.keycloak.services.resources;
+
+import org.jboss.resteasy.logging.Logger;
+import org.jboss.resteasy.spi.HttpRequest;
+import org.jboss.resteasy.spi.HttpResponse;
+import org.jboss.resteasy.spi.NotImplementedYetException;
+import org.keycloak.representations.idm.CredentialRepresentation;
+import org.keycloak.representations.idm.RequiredCredentialRepresentation;
+import org.keycloak.representations.idm.UserRepresentation;
+import org.keycloak.services.managers.AuthenticationManager;
+import org.keycloak.services.managers.RealmManager;
+import org.keycloak.services.models.KeycloakSession;
+import org.keycloak.services.models.RealmModel;
+import org.keycloak.services.models.RoleModel;
+import org.keycloak.services.models.UserModel;
+import org.keycloak.services.models.UserCredentialModel;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.ForbiddenException;
+import javax.ws.rs.FormParam;
+import javax.ws.rs.GET;
+import javax.ws.rs.NotFoundException;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.NewCookie;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
+import java.net.URI;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+@Path("/saas")
+public class SaasService {
+    protected static final Logger logger = Logger.getLogger(SaasService.class);
+    public static final String REALM_CREATOR_ROLE = "realm-creator";
+    public static final String SAAS_IDENTITY_COOKIE = "KEYCLOAK_SAAS_IDENTITY";
+
+    @Context
+    protected UriInfo uriInfo;
+
+    @Context
+    protected HttpRequest request;
+
+    @Context
+    HttpResponse response;
+
+    protected String saasLoginPath = "/saas/saas-login.jsp";
+    protected String saasRegisterPath = "/saas/saas-register.jsp";
+    protected String adminPath = "/saas/admin/index.html";
+    protected AuthenticationManager authManager = new AuthenticationManager();
+
+    public static class WhoAmI {
+        protected String userId;
+        protected String displayName;
+
+        public WhoAmI() {
+        }
+
+        public WhoAmI(String userId, String displayName) {
+            this.userId = userId;
+            this.displayName = displayName;
+        }
+
+        public String getUserId() {
+            return userId;
+        }
+
+        public void setUserId(String userId) {
+            this.userId = userId;
+        }
+
+        public String getDisplayName() {
+            return displayName;
+        }
+
+        public void setDisplayName(String displayName) {
+            this.displayName = displayName;
+        }
+    }
+
+    @Path("whoami")
+    @GET
+    @Produces("application/json")
+    public Response whoAmI(final @Context HttpHeaders headers) {
+        return new Transaction() {
+            @Override
+            public Response callImpl()
+            {
+                logger.info("WHOAMI start.");
+                RealmManager realmManager = new RealmManager(session);
+                RealmModel realm = realmManager.defaultRealm();
+                if (realm == null) throw new NotFoundException();
+                UserModel user = authManager.authenticateSaasIdentityCookie(realm, uriInfo, headers);
+                if (user == null) {
+                    return Response.status(404).build();
+                }
+                logger.info("WHOAMI: " + user.getLoginName());
+                return Response.ok(new WhoAmI(user.getLoginName(), user.getLoginName())).build();
+            }
+        }.call();
+    }
+
+    @Path("isLoggedIn.js")
+    @GET
+    @Produces("application/javascript")
+    public String isLoggedIn(final @Context HttpHeaders headers) {
+        return new Transaction() {
+            @Override
+            public String callImpl()
+            {
+                logger.info("WHOAMI Javascript start.");
+                RealmManager realmManager = new RealmManager(session);
+                RealmModel realm = realmManager.defaultRealm();
+                if (realm == null) {
+                    return "var keycloakCookieLoggedIn = false;";
+
+                }
+                UserModel user = authManager.authenticateSaasIdentityCookie(realm, uriInfo, headers);
+                if (user == null) {
+                    return "var keycloakCookieLoggedIn = false;";
+                }
+                logger.info("WHOAMI: " + user.getLoginName());
+                return "var keycloakCookieLoggedIn = true;";
+            }
+        }.call();
+    }
+
+
+    public static UriBuilder contextRoot(UriInfo uriInfo) {
+        return UriBuilder.fromUri(uriInfo.getBaseUri()).replacePath("/auth-server");
+    }
+
+    public static UriBuilder saasCookiePath(UriInfo uriInfo) {
+        return contextRoot(uriInfo).path("rest").path(SaasService.class);
+    }
+
+
+
+    @Path("logout")
+    @GET
+    public void logout() {
+        new Transaction() {
+            @Override
+            protected void runImpl() {
+                authManager.expireSaasIdentityCookie(uriInfo);
+                request.forward(saasLoginPath);
+            }
+        }.run();
+    }
+
+    @Path("logout-cookie")
+    @GET
+    public void logoutCookie() {
+        logger.info("*** logoutCookie");
+        new Transaction() {
+            @Override
+            protected void runImpl() {
+                authManager.expireSaasIdentityCookie(uriInfo);
+            }
+        }.run();
+    }
+
+
+    @Path("login")
+    @POST
+    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
+    public void processLogin(final MultivaluedMap<String, String> formData) {
+        logger.info("processLogin start");
+        new Transaction() {
+            @Override
+            protected void runImpl() {
+                RealmManager realmManager = new RealmManager(session);
+                RealmModel realm = realmManager.defaultRealm();
+                if (realm == null) throw new NotFoundException();
+
+                if (!realm.isEnabled()) {
+                    throw new NotImplementedYetException();
+                }
+                String username = formData.getFirst("username");
+                UserModel user = realm.getUser(username);
+                if (user == null) {
+                    logger.info("Not Authenticated! Incorrect user name");
+                    request.setAttribute("KEYCLOAK_LOGIN_ERROR_MESSAGE", "Incorrect user name.");
+                    request.forward(saasLoginPath);
+                    return;
+                }
+                if (!user.isEnabled()) {
+                    logger.info("NAccount is disabled, contact admin.");
+                    request.setAttribute("KEYCLOAK_LOGIN_ERROR_MESSAGE", "Account is disabled, contact admin.");
+                    request.forward(saasLoginPath);
+                    return;
+                }
+
+                boolean authenticated = authManager.authenticateForm(realm, user, formData);
+                if (!authenticated) {
+                    logger.info("Not Authenticated! Invalid credentials");
+                    request.setAttribute("KEYCLOAK_LOGIN_ERROR_MESSAGE", "Invalid credentials.");
+                    request.forward(saasLoginPath);
+                    return;
+                }
+
+                NewCookie cookie = authManager.createSaasIdentityCookie(realm, user, uriInfo);
+                response.addNewCookie(cookie);
+                request.forward(adminPath);
+            }
+        }.run();
+    }
+
+    @Path("registrations")
+    @POST
+    @Consumes(MediaType.APPLICATION_JSON)
+    public Response register(final UserRepresentation newUser) {
+        return new Transaction() {
+            @Override
+            protected Response callImpl() {
+                RealmManager realmManager = new RealmManager(session);
+                RealmModel defaultRealm = realmManager.defaultRealm();
+                UserModel user = registerMe(defaultRealm, newUser);
+                if (user == null) {
+                    return Response.status(400).type("text/plain").entity("Already exists").build();
+                }
+                URI uri = uriInfo.getBaseUriBuilder().path(RealmsResource.class).path(user.getLoginName()).build();
+                return Response.created(uri).build();
+            }
+        }.call();
+    }
+
+    @Path("registrations")
+    @POST
+    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
+    public Response processRegister(final @FormParam("name") String name,
+                                    final @FormParam("email") String email,
+                                    final @FormParam("username") String username,
+                                    final @FormParam("password") String password,
+                                    final @FormParam("password-confirm") String confirm) {
+        if (!password.equals(confirm)) {
+            request.setAttribute("KEYCLOAK_LOGIN_ERROR_MESSAGE", "Password confirmation doesn't match.");
+            request.forward(saasRegisterPath);
+            return null;
+        }
+        return new Transaction() {
+            @Override
+            protected Response callImpl() {
+                RealmManager realmManager = new RealmManager(session);
+                RealmModel defaultRealm = realmManager.defaultRealm();
+                UserRepresentation newUser = new UserRepresentation();
+                newUser.setUsername(username);
+                newUser.credential(RequiredCredentialRepresentation.PASSWORD, password, false);
+                UserModel user = registerMe(defaultRealm, newUser);
+                if (user == null) {
+                    request.setAttribute("KEYCLOAK_LOGIN_ERROR_MESSAGE", "Username already exists.");
+                    request.forward(saasRegisterPath);
+                    return null;
+
+                }
+                NewCookie cookie = authManager.createSaasIdentityCookie(defaultRealm, user, uriInfo);
+                return Response.status(302).location(contextRoot(uriInfo).path(adminPath).build()).cookie(cookie).build();
+            }
+        }.call();
+    }
+
+
+    protected UserModel registerMe(RealmModel defaultRealm, UserRepresentation newUser) {
+        if (!defaultRealm.isEnabled()) {
+            throw new ForbiddenException();
+        }
+        if (!defaultRealm.isRegistrationAllowed()) {
+            throw new ForbiddenException();
+        }
+        UserModel user = defaultRealm.getUser(newUser.getUsername());
+        if (user != null) {
+            return null;
+        }
+
+        user = defaultRealm.addUser(newUser.getUsername());
+        for (CredentialRepresentation cred : newUser.getCredentials()) {
+            UserCredentialModel credModel = new UserCredentialModel();
+            credModel.setType(cred.getType());
+            credModel.setValue(cred.getValue());
+            defaultRealm.updateCredential(user, credModel);
+        }
+        RoleModel realmCreator = defaultRealm.getRole(REALM_CREATOR_ROLE);
+        defaultRealm.grantRole(user, realmCreator);
+        return user;
+    }
+
+
+}
diff --git a/services/src/main/java/org/keycloak/services/resources/TokenService.java b/services/src/main/java/org/keycloak/services/resources/TokenService.java
index b16c1ef..ba4d265 100755
--- a/services/src/main/java/org/keycloak/services/resources/TokenService.java
+++ b/services/src/main/java/org/keycloak/services/resources/TokenService.java
@@ -49,7 +49,6 @@ public class TokenService extends AbstractLoginService {
     @Context
     protected SecurityContext securityContext;
 
-    protected AuthenticationManager authManager = new AuthenticationManager();
     private ResourceAdminManager resourceAdminManager = new ResourceAdminManager();
 
     public TokenService(RealmModel realm, TokenManager tokenManager) {
@@ -115,7 +114,7 @@ public class TokenService extends AbstractLoginService {
                     throw new NotAuthorizedException("FORM");
                 }
                 tokenManager = new TokenManager();
-                SkeletonKeyToken token = tokenManager.createIdentityToken(realm, username);
+                SkeletonKeyToken token = authManager.createIdentityToken(realm, username);
                 String encoded = tokenManager.encodeToken(realm, token);
                 AccessTokenResponse res = accessTokenResponse(token, encoded);
                 return Response.ok(res, MediaType.APPLICATION_JSON_TYPE).build();
diff --git a/services/src/test/java/org/keycloak/test/ImportTest.java b/services/src/test/java/org/keycloak/test/ImportTest.java
index 1c783e4..03f945f 100755
--- a/services/src/test/java/org/keycloak/test/ImportTest.java
+++ b/services/src/test/java/org/keycloak/test/ImportTest.java
@@ -12,10 +12,13 @@ import org.keycloak.services.models.KeycloakSession;
 import org.keycloak.services.models.KeycloakSessionFactory;
 import org.keycloak.services.models.RealmModel;
 import org.keycloak.services.models.RequiredCredentialModel;
+import org.keycloak.services.models.ResourceModel;
 import org.keycloak.services.models.UserModel;
 import org.keycloak.services.resources.KeycloakApplication;
-import org.keycloak.services.resources.RegistrationService;
+import org.keycloak.services.resources.SaasService;
+import org.keycloak.services.resources.SaasService;
 
+import java.util.List;
 import java.util.Set;
 
 /**
@@ -56,17 +59,21 @@ public class ImportTest {
         defaultRealm.setRegistrationAllowed(true);
         manager.generateRealmKeys(defaultRealm);
         defaultRealm.addRequiredCredential(RequiredCredentialModel.PASSWORD);
-        defaultRealm.addRole(RegistrationService.REALM_CREATOR_ROLE);
+        defaultRealm.addRole(SaasService.REALM_CREATOR_ROLE);
 
         RealmRepresentation rep = KeycloakTestBase.loadJson("testrealm.json");
         RealmModel realm = manager.createRealm("demo", rep.getRealm());
         manager.importRealm(rep, realm);
+        List<RequiredCredentialModel> creds = realm.getRequiredCredentials();
+        Assert.assertEquals(1, creds.size());
 
         UserModel user = realm.getUser("loginclient");
         Assert.assertNotNull(user);
         Set<String> scopes = realm.getScope(user);
         System.out.println("Scopes size: " + scopes.size());
         Assert.assertTrue(scopes.contains("*"));
+        List<ResourceModel> resources = realm.getResources();
+        Assert.assertEquals(2, resources.size());
 
     }
 
diff --git a/services/src/test/java/org/keycloak/test/InstallationManager.java b/services/src/test/java/org/keycloak/test/InstallationManager.java
index bd4724e..63e5d1d 100755
--- a/services/src/test/java/org/keycloak/test/InstallationManager.java
+++ b/services/src/test/java/org/keycloak/test/InstallationManager.java
@@ -3,7 +3,8 @@ package org.keycloak.test;
 import org.keycloak.services.managers.RealmManager;
 import org.keycloak.services.models.RealmModel;
 import org.keycloak.services.models.RequiredCredentialModel;
-import org.keycloak.services.resources.RegistrationService;
+import org.keycloak.services.resources.SaasService;
+import org.keycloak.services.resources.SaasService;
 
 /**
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -21,7 +22,7 @@ public class InstallationManager {
         defaultRealm.setRegistrationAllowed(true);
         manager.generateRealmKeys(defaultRealm);
         defaultRealm.addRequiredCredential(RequiredCredentialModel.PASSWORD);
-        defaultRealm.addRole(RegistrationService.REALM_CREATOR_ROLE);
+        defaultRealm.addRole(SaasService.REALM_CREATOR_ROLE);
     }
 
     public boolean isInstalled(RealmManager manager) {
diff --git a/services/src/test/java/org/keycloak/test/RealmCreationTest.java b/services/src/test/java/org/keycloak/test/RealmCreationTest.java
index 7003890..fad6c34 100755
--- a/services/src/test/java/org/keycloak/test/RealmCreationTest.java
+++ b/services/src/test/java/org/keycloak/test/RealmCreationTest.java
@@ -64,7 +64,7 @@ public class RealmCreationTest {
         user.credential(RequiredCredentialRepresentation.PASSWORD, "geheim", false);
 
         WebTarget target = client.target(generateURL("/"));
-        Response response = target.path("registrations").request().post(Entity.json(user));
+        Response response = target.path("saas/registrations").request().post(Entity.json(user));
         Assert.assertEquals(201, response.getStatus());
         response.close();